Обработка задержки инициализации firebase и токенов id для пользовательского бэкэнда apollo graphql

В настоящее время, когда я аутентифицирую пользователя с помощью firebase, я сохраняю его токен авторизации в localStorage будет использоваться позже для подключения к моему бэкэнду следующим образом:

const httpLink = new HttpLink({uri: 'http://localhost:9000/graphql'})

const authMiddleware = new ApolloLink((operation, forward) => {
  // add the authorization token to the headers
  const token = localStorage.getItem(AUTH_TOKEN) || null
  operation.setContext({
    headers: {
      authorization: token ? `Bearer ${token}` : ''
    }
  })
  return forward(operation)
})

const authAfterware = onError(({networkError}) => {
  if (networkError.statusCode === 401) AuthService.signout()
})

function createApolloClient() {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: authMiddleware.concat(authAfterware).concat(httpLink)
  })
}

Моя проблема в том, что у меня нет возможности обновить токен после его истечения. Поэтому я попытался использовать следующее для установки токена авторизации для apollo:

const httpLink = new HttpLink({uri: 'http://localhost:9000/graphql'})

const asyncAuthLink = setContext(
  () => {
    return new Promise((success, reject) => {
      firebase.auth().currentUser.getToken().then(token => {
        success({
          headers: {
            authorization: token ? `Bearer ${token}` : ''
          }
        })
      }).catch(error => {
        reject(error)
      })
    })
  }
)

const authAfterware = onError(({networkError}) => {
  if (networkError.statusCode === 401) AuthService.signout()
})

function createApolloClient() {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: asyncAuthLink.concat(authAfterware.concat(httpLink))
  })
}

Это работает, когда пользователь сначала аутентифицируется, но как только пользователь обновляет страницу, firebase больше не инициализируется, когда мои запросы graphql отправляются на мой бэкэнд, поэтому токен не отправляется вместе с ним. Есть ли способ, которым я могу асинхронно ждать firebase.auth().currentUser так это будет работать? Или есть другой подход, который я должен использовать полностью? Насколько я знаю (уверен на 100%) currentUser.getIdToken только делает сетевой вызов, если текущий токен больше не действителен. Я думаю, что это приемлемо, так как в случаях, когда токен недействителен, серверная часть не может ответить в любом случае, поэтому мне придется ждать продолжения обновления токена.

Некоторые другие идеи, о которых я подумал:

  • Продолжайте использовать localStorage чтобы сохранить токен авторизации, обновите его в authAfterware если мой бэкэнд отправляет ответ 401 и повторяет запрос.
  • Установить задержку при получении токена авторизации (не желательно)
  • Есть еще идеи?

Спасибо!

1 ответ

Я знаю, что немного поздно, но я тоже застрял на этом и нашел способ решить эту проблему. Может быть, не лучший, но, по крайней мере, он работает. Мой подход заключается в создании конечной точки Next API для получения токена пользователя с помощью метода getUserFromCookies:

      import { NextApiRequest, NextApiResponse } from "next";
import { getUserFromCookies } from "next-firebase-auth";
import initAuth from "../../utils/initAuth";

initAuth();

const handler = async (req: NextApiRequest, res: NextApiResponse<any>) => {
  try {
    const user = await getUserFromCookies({ req, includeToken: true });
    const accessToken = await user.getIdToken();
    return res.status(200).json({ success: true, accessToken });
  } catch (e) {
    console.log(`${e}`);
    return res.status(500).json({ error: `Unexpected error. ${e}` });
  }
};

export default handler;

А затем вызовите эту конечную точку в конфигурации клиента apollo следующим образом:

      import { ApolloClient, InMemoryCache, ApolloLink, HttpLink, concat } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { relayStylePagination } from "@apollo/client/utilities";

const getUserToken = async () => {
  const res = await fetch("http://localhost:3000/api/get-user-token");
  const { accessToken } = await res.json();
  return accessToken;
};

const asyncAuthLink = setContext(async (request) => {
  const token = await getUserToken();
  return { ...request, headers: { authorization: token ? `Bearer ${token}` : "" } };
});

const httpLink = new HttpLink({ uri: process.env.NEXT_PUBLIC_API_URL });

const client = new ApolloClient({
  name: "web",
  version: "1.0",
  uri: process.env.NEXT_PUBLIC_API_URL,
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          users: relayStylePagination(),
        },
      },
    },
  }),
  link: concat(asyncAuthLink, httpLink),
});

export default client;
Другие вопросы по тегам