What would it be the best way to clear the NextAuth.js session when trying to hit the backend (Apollo GraphQL) and it returns a 401 because the token has expired or is invalid?
I thought about an errorLink
and signout
, but as far as I know signout
cannot be used server side at getServerSideProps
, but only client-side.
What is the recommended way to do so? Is there any other way to implement a middleware to take care of that scenario?
This would be the proof of concepto of the errorLink
I'm trying to implement, the code is caught in that if
but I can't use signOut()
as it's only available client-side.
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}` : '' },
}),
]),
});
}
Thanks
As Noah and Yog told you, there is no way to do it server-side, as signOut
must clear the state client-side. So this is how I would do it:
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}` : '' },
}),
]),
});
}
Then, in your 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 } };
}
};
Finally, your page would look like:
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);
If it can't be done server-side, I guess this would be the better flow for it, right?