NextAuth未遵循Azure AD B2C会话过期配置的问题求助
NextAuth + Azure AD B2C 会话有效期不匹配问题解决
问题描述
我在SPA应用中使用NextAuth作为认证方案,提供商是Azure AD B2C。目前遇到的问题是:已经在AD B2C中将会话有效期配置为1天,但用户实际会话能保持有效长达一个月。
为测试我添加了以下配置:
session: { maxAge: 120, }, jwt: { maxAge: 120, },
但2分钟后刷新页面,系统会生成新令牌,却不会跳转到登录页。
我需要强制NextAuth遵循B2C的配置,实现用户在X小时无活动后被重定向至登录页。
我的相关代码如下:
import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c"; import NextAuth, { NextAuthOptions } from "next-auth"; export const authOptions: NextAuthOptions = { secret: process.env.NEXTAUTH_SECRET, session: { strategy: "jwt", // Even adding this doesn't help invalidating the session // Seconds - How long until an idle session expires and is no longer valid. maxAge: 60, // 20 Seconds }, providers: [ AzureADB2CProvider({ tenantId: process.env.AZURE_AD_B2C_TENANT_NAME, clientId: process.env.AZURE_AD_B2C_CLIENT_ID!, clientSecret: process.env.AZURE_AD_B2C_CLIENT_SECRET!, primaryUserFlow: process.env.AZURE_AD_B2C_PRIMARY_USER_FLOW, authorization: { params: { scope: `offline_access openid`, }, }, profile: (profile) => { return { id: profile.oid, fName: profile.name, email: profile.emails?.length ? profile.emails[0] : null, myClaim: profile.extension_myClaim, }; }, checks: ["pkce"], client: { token_endpoint_auth_method: "none", }, }), ], callbacks: { async jwt({ token, account, profile }) { // Persist the OAuth access_token to the token right after signin if (profile) { token.myClaim = profile.extension_myClaim; } return token; }, async session({ session, token, user }) { // Send properties to the client, like an access_token from a provider. session.myClaim = token.myClaim; return session; }, }, pages: { signIn: "/auth/signin", signOut: "/auth/signout", }, }; export default NextAuth(authOptions);
核心问题分析
NextAuth默认会利用offline_access权限自动刷新令牌,忽略手动设置的maxAge;同时未跟踪用户活动时间,导致无活动超时逻辑无法触发。要解决问题,需结合Azure AD B2C返回的令牌过期信息,同时实现无活动超时校验。
具体解决步骤
1. 在JWT回调中保存令牌原生过期时间和活动时间
修改jwt回调,保存B2C返回的exp(令牌过期时间),并添加lastActive字段跟踪用户最后活动时间:
async jwt({ token, account, profile, trigger, session }) { // 首次登录时保存B2C返回的令牌过期时间 if (account) { token.exp = account.expires_at; token.lastActive = Date.now(); } // 客户端触发会话更新时,同步活动时间 if (trigger === "update" && session?.lastActive) { token.lastActive = session.lastActive; } if (profile) { token.myClaim = profile.extension_myClaim; } return token; }
2. 在Session回调中校验过期时间和无活动超时
在session回调中同时校验令牌绝对过期时间和无活动超时,超时则抛出错误:
async session({ session, token }) { const idleTimeout = 60 * 60 * 1000; // 1小时无活动超时,按需调整 const currentTime = Date.now(); // 校验令牌是否绝对过期 if (token.exp && currentTime > token.exp * 1000) { throw new Error("Session expired"); } // 校验无活动超时 if (token.lastActive && currentTime - token.lastActive > idleTimeout) { throw new Error("Session timed out due to inactivity"); } // 同步活动时间到会话 session.lastActive = token.lastActive; session.myClaim = token.myClaim; return session; }
3. 关闭NextAuth自动令牌刷新
在Azure AD B2C Provider配置中移除offline_access scope,避免自动刷新令牌:
authorization: { params: { scope: `openid`, // 移除offline_access }, },
4. 客户端处理会话失效重定向
通过useSession钩子监听会话状态,出现错误时重定向到登录页:
import { useSession } from "next-auth/react"; import { useEffect } from "react"; import { useRouter } from "next/router"; export default function ProtectedPage() { const { data: session, status, error } = useSession(); const router = useRouter(); useEffect(() => { if (error) { router.push("/auth/signin"); } }, [error, router]); if (status === "loading") { return <div>Loading...</div>; } if (!session) { return null; } return <div>Protected Content</div>; }
5. 调整Session配置
设置session.updateAge为较短时间,确保定期校验会话状态:
session: { strategy: "jwt", updateAge: 60 * 5, // 5分钟更新一次会话,触发超时校验 },
注意不要设置maxAge,让B2C的令牌过期时间主导绝对过期逻辑。
关键说明
- 绝对过期时间:完全遵循Azure AD B2C返回的
expires_at,无需手动设置maxAge - 无活动超时:通过
lastActive字段跟踪,每次客户端交互时调用updateSession更新该字段 - 关闭自动刷新:移除
offline_access避免NextAuth自动延长会话,确保过期后必须重新登录
内容的提问来源于stack exchange,提问作者magnetarneo
相关产品推荐
相关产品推荐

