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
相关产品推荐
相关产品推荐

