Обработка задержки инициализации 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;