NextAuth结合Apollo GraphQL认证异常:后端未认证但NextAuth仍显示已认证
解决NextAuth与后端JWT认证状态不同步问题
问题根源
NextAuth的session状态独立于后端JWT的生命周期,当后端JWT过期后,NextAuth未及时感知状态变化,仍维持authenticated状态,导致页面不跳转登录页,直接展示后端错误。
解决方案
1. 自定义Session回调,实时验证后端JWT有效性
在AuthOptions的session配置中添加回调函数,每次获取session时主动调用后端的认证验证接口(比如返回用户信息的me接口),验证失败则返回null,让NextAuth将session标记为未认证。
示例代码:
import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; export const authOptions = { providers: [ CredentialsProvider({ // 你的Credentials登录验证逻辑 async authorize(credentials) { // 调用后端login接口获取JWT const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/graphql`, { method: "POST", body: JSON.stringify({ query: `mutation Login($input: LoginInput!) { login(input: $input) { accessToken user { id name } } }`, variables: { input: credentials } }), headers: { "Content-Type": "application/json" } }); const data = await res.json(); if (data.data?.login) return data.data.login; return null; } }), ], session: { strategy: "jwt", maxAge: 30 * 60, // 和后端JWT过期时间保持一致(30分钟) }, callbacks: { async session({ session, token }) { // 调用后端验证接口,确认JWT有效性 try { const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/graphql`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token.accessToken}` }, body: JSON.stringify({ query: "{ me { id } }" }) }); const data = await res.json(); if (data.errors?.some(err => err.message === "Unauthenticated!")) { // 验证失败,返回null触发session失效 return null; } return { ...session, user: { ...session.user, accessToken: token.accessToken } }; } catch (err) { return null; } }, async jwt({ token, user }) { // 登录时保存后端返回的accessToken到JWT中 if (user) token.accessToken = user.accessToken; return token; }, }, }; export default NextAuth(authOptions);
2. 拦截API请求错误,主动触发登出
在前端Apollo客户端配置中添加错误拦截,捕获到"Unauthenticated!"错误时,调用NextAuth的signOut方法强制清除session并跳转登录页。
示例代码:
import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; import { onError } from "@apollo/client/link/error"; import { getSession, signOut } from "next-auth/react"; const httpLink = createHttpLink({ uri: process.env.NEXT_PUBLIC_API_URL }); const authLink = setContext(async (_, { headers }) => { const session = await getSession(); return { headers: { ...headers, authorization: session?.user?.accessToken ? `Bearer ${session.user.accessToken}` : "" } }; }); // 错误拦截逻辑 const errorLink = onError(({ graphQLErrors }) => { if (graphQLErrors?.some(err => err.message === "Unauthenticated!")) { signOut({ callbackUrl: "/login" }); } }); const client = new ApolloClient({ link: errorLink.concat(authLink.concat(httpLink)), cache: new InMemoryCache() }); export default client;
3. 对齐Session与JWT的过期时间
在AuthOptions的session配置中设置maxAge等于后端JWT的过期时间,确保即使后端未主动通知,NextAuth也会自动在JWT过期后将session标记为未认证。
内容的提问来源于stack exchange,提问作者PugProvoleta
相关产品推荐
相关产品推荐

