You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:10:22