Выход NextAuth.js, если токен Apollo GraphQL недействителен или срок его действия истек
Как лучше всего очистить сеанс NextAuth.js при попытке попасть в бэкэнд (Apollo GraphQL), и он возвращает 401, потому что срок действия токена истек или он недействителен?
Я думал о
errorLink
и
signout
, но насколько я знаю
signout
не может использоваться на стороне сервера
getServerSideProps
, но только на стороне клиента.
Каков рекомендуемый способ сделать это? Есть ли другой способ реализовать промежуточное ПО, которое позаботится об этом сценарии?
Это было бы доказательством концепции
errorLink
Я пытаюсь реализовать, код застрял в этом
if
но я не могу использовать
signOut()
поскольку он доступен только на стороне клиента.
const errorLink = onError(({ graphQLErrors }) => {
if (graphQLErrors?.[0]?.message === 'Unauthenticated.') {
// signOut();
}
});
function createApolloClient(session) {
return new ApolloClient({
cache: new InMemoryCache(),
ssrMode: typeof window === 'undefined',
link: from([
errorLink,
createUploadLink({
uri: GRAPHQL_URI,
credentials: 'same-origin',
headers: { Authorization: session?.accessToken ? `Bearer ${session.accessToken}` : '' },
}),
]),
});
}
благодаря
3 ответа
Как сказали вам Ной и Йог, это невозможно сделать на стороне сервера, поскольку
signOut
должен очистить состояние на стороне клиента. Вот как бы я это сделал:
function createApolloClient(session) {
return new ApolloClient({
cache: new InMemoryCache(),
ssrMode: typeof window === 'undefined',
link: from([
createUploadLink({
uri: GRAPHQL_URI,
credentials: 'same-origin',
headers: { Authorization: session?.accessToken ? `Bearer ${session.accessToken}` : '' },
}),
]),
});
}
Затем в вашем
getServerSiderProps
:
export const fetchServerSideProps = (
initializeApollo: (context: SessionBase | null) => ApolloClient<NormalizedCacheObject>
): GetServerSideProps => async (context) => {
const session = await getSession(context);
const apolloClient = initializeApollo(session);
try {
// Fetch anything from GraphQL here
return {
props: {
// add your required page props here
session,
},
};
} catch {
// Token invalid or expired error (401) caught here, so let's handle this client-side.
return { props: { session: null } };
}
};
Наконец, ваша страница будет выглядеть так:
const withAuth = <P extends { session: Session | null }>(Page: NextPage<P>) => (props: P): JSX.Element | null => {
if (!props.session) {
// Clear session and redirect to login
signOut({ callbackUrl: '/login' });
return null;
}
return <Page {...props} />;
};
const Page: NextPage<P> = (props) => (
<p>This should be shown ONLY if the user is logged in</p>
);
export default withAuth(LoggedInPage);
Sign Out() очищает сеанс, очищая состояние на стороне клиента, здесь вы можете проверить из бэкэнда, существует ли состояние, а если его нет, тогда сделайте что-нибудь вместо 401(Unauthorized). Надеюсь, вы это прочитали: https://next-auth.js.org/getting-started/client#signout
Это зависит от того, какую версию Apollo вы используете, но, предполагая, что вы используете Apollo 3.0 <=, вы можете создать setcontext для своих запросов.
import { setContext } from '@apollo/client/link/context';
const authLink = setContext((_, { headers }) => {
const token = session?.accessToken ? session.accessToken : ""
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
function createApolloClient(session) {
return new ApolloClient({
cache: new InMemoryCache(),
ssrMode: typeof window === 'undefined',
link: from([
authLink,
errorLink,
createUploadLink({
uri: GRAPHQL_URI,
credentials: 'same-origin'
}),
]),
});
}
Поскольку signOut() повторно визуализирует ваше приложение, вы можете проверить контекст, чтобы узнать, есть ли у него действительный токен по запросу с вашего сервера. Не тестировал это, но теоретически я могу реализовать это так.