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

Next.js中NextAuth结合AWS Amplify刷新Token的服务端问题

Next.js 13 + NextAuth + AWS Amplify v6 服务端Token刷新问题解决

核心问题分析

客户端能用fetchAuthSession刷新token,但服务端NextAuth回调里失败,本质是服务端环境下Amplify的初始化和session存储逻辑与客户端不同,同时NextAuth的JWT回调中token更新的流程没有适配Amplify的服务端调用规则。

解决方案步骤

1. 服务端专用Amplify配置

创建服务端初始化Amplify的文件(比如lib/amplify-server.ts),禁用浏览器存储,启用SSR模式:

import { Amplify } from 'aws-amplify';
import { Auth } from 'aws-amplify/auth';

// 服务端配置Amplify,避免依赖浏览器存储
Amplify.configure({
  Auth: {
    region: process.env.AWS_REGION,
    userPoolId: process.env.AWS_USER_POOL_ID,
    userPoolWebClientId: process.env.AWS_USER_POOL_WEB_CLIENT_ID,
    // 禁用默认的浏览器存储,使用自定义空存储适配服务端环境
    storage: {
      getItem: (key: string) => Promise.resolve(null),
      setItem: (key: string, value: string) => Promise.resolve(),
      removeItem: (key: string) => Promise.resolve(),
    },
  },
  ssr: true, // 开启SSR支持
});

export { Auth };

2. 调整NextAuth的JWT回调逻辑

在pages/api/auth/[...nextauth].ts中,修改jwt回调,实现过期token的服务端刷新:

import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { Auth } from '../../lib/amplify-server'; // 引入服务端Amplify Auth

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        username: { label: 'Username', type: 'text' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials) {
        // 初始登录逻辑,验证用户并返回Amplify会话信息
        try {
          const user = await Auth.signIn({
            username: credentials?.username || '',
            password: credentials?.password || '',
          });
          return user;
        } catch (error) {
          return null;
        }
      },
    }),
  ],
  session: { strategy: 'jwt' },
  callbacks: {
    async jwt({ token, user, account }) {
      // 首次登录时,将Amplify的token信息存入NextAuth的JWT
      if (user && account) {
        token.idToken = user.signInUserSession?.idToken?.getJwtToken();
        token.refreshToken = user.signInUserSession?.refreshToken?.getToken();
        token.expiresAt = user.signInUserSession?.idToken?.payload?.exp || 0;
      }

      // 检查token是否即将过期(提前60秒触发刷新)
      const now = Math.floor(Date.now() / 1000);
      if (token.expiresAt && now < token.expiresAt - 60) {
        return token; // token仍有效,直接返回
      }

      // token过期,调用Amplify服务端接口刷新
      try {
        const session = await Auth.refreshSession({
          refreshToken: token.refreshToken as string,
        });
        // 更新NextAuth的JWT数据
        token.idToken = session.idToken.getJwtToken();
        token.refreshToken = session.refreshToken.getToken();
        token.expiresAt = session.idToken.payload.exp || 0;
        return token;
      } catch (error) {
        console.error('Token刷新失败:', error);
        // 刷新失败时标记错误,供前端处理
        return { ...token, error: 'RefreshTokenError' };
      }
    },
    async session({ session, token }) {
      // 将最新的token传递给前端session
      session.user.idToken = token.idToken;
      if (token.error) {
        session.error = token.error;
      }
      return session;
    },
  },
  secret: process.env.NEXTAUTH_SECRET,
});

3. 关键注意事项

  • Refresh Token存储:初始登录时必须把Amplify的refresh token存入NextAuth的JWT,服务端刷新依赖这个凭证。
  • 服务端存储适配:必须禁用Amplify默认的浏览器存储,否则服务端会因访问localStorage报错。
  • 过期提前判断:提前60秒触发刷新,避免请求过程中token刚好过期导致的异常。
  • 错误前端联动:session中携带刷新错误标记后,前端可监听该字段,引导用户重新登录。

错误排查方向

  • 确认NEXTAUTH_SECRET配置正确,该值是NextAuth签名JWT的核心,配置错误会直接触发JWT_SESSION_ERROR。
  • 检查Amplify服务端配置的region、userPoolId、userPoolWebClientId与客户端完全一致。
  • 验证refresh token有效性:Amplify refresh token默认有效期30天,过期后需用户重新登录。

内容的提问来源于stack exchange,提问作者Suraj Kadam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 13:20:17