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

NextAuth v4配置:以外端JWT的expiresAt为准自动登出

NextAuth v4 配置外部JWT过期时间自动登出

问题背景

在Next.js 14中使用NextAuth v4对接外部JWT认证后端,登录成功后会话中同时存在NextAuth自动生成的expires字段,以及后端返回的expiresAt时间戳。需要让NextAuth以外端返回的expiresAt作为令牌过期判断标准,到期后自动登出用户。

解决方案

需要修改authOptions中的JWT和Session回调,同时明确session策略,具体步骤如下:

1. 明确Session策略(可选但推荐)

在authOptions中添加session配置,指定使用jwt策略:

session: {
  strategy: "jwt",
},

2. 修改JWT回调,同步外部过期时间

在jwt回调中,将后端返回的expiresAt转换为NextAuth JWT识别的exp字段(Unix时间戳),并添加过期检查逻辑:

async jwt({ token, user }) {
  if (user) {
    // 将后端返回的expiresAt赋值给token.exp,作为JWT过期判断依据
    token.exp = user.authorization.expiresAt;
    // 保留用户其他信息
    return { ...token, ...user };
  }

  // 每次刷新JWT时,检查是否已过期
  const now = Math.floor(Date.now() / 1000);
  if (token.exp && now > token.exp) {
    // 令牌已过期,返回null触发登出
    return null;
  }

  return token;
},

3. 修改Session回调,同步会话过期时间

在session回调中,将session.expires设置为expiresAt对应的ISO时间字符串,确保前端useSession能正确识别会话过期状态:

async session({ token, session }) {
  session.user = token.user;
  session.authorization = token.authorization;
  // 将expiresAt转换为ISO时间字符串,覆盖NextAuth自动生成的expires
  session.expires = new Date(token.authorization.expiresAt * 1000).toISOString();
  return session;
},

修改后的完整authOptions.ts

import NextAuth, { NextAuthOptions } from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import { JWT } from "next-auth/jwt"

const API_ENDPOINT =
  process.env.NEXT_PUBLIC_API_URL || "http://myapp.test/api"


class CustomError extends Error {
  status: number;

  constructor(message: string, status: number) {
    super(message);
    this.name = this.constructor.name;
    this.status = status;
    Error.captureStackTrace(this, this.constructor);
  }
}

const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      type: "credentials",
      name: "Email and Password",
      credentials: {
        email: { label: "Email", type: "email", placeholder: "Your Email" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) return null

        const { email, password } = credentials

        const res = await fetch(`${API_ENDPOINT}/auth/login`, {
          method: "POST",
          body: JSON.stringify({
            email,
            password,
          }),
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json",
          },
        })

        const response = await res.json()
        if (res.ok) {
          return response.data
        } else {
          throw new CustomError(response.message, response.status);
        }
      },
    }),
  ],
  // 明确使用jwt会话策略
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        // 用后端返回的expiresAt设置JWT过期时间
        token.exp = user.authorization.expiresAt;
        return { ...token, ...user };
      }

      // 检查令牌是否已过期
      const now = Math.floor(Date.now() / 1000);
      if (token.exp && now > token.exp) {
        return null;
      }

      return token;
    },
    async session({ token, session }) {
      session.user = token.user;
      session.authorization = token.authorization;
      // 同步会话过期时间为后端expiresAt对应的ISO字符串
      session.expires = new Date(token.authorization.expiresAt * 1000).toISOString();
      return session;
    },
  },

  pages: {
    signIn: "/auth/login",
  },
}
export {authOptions}

原理说明

  • NextAuth的JWT过期判断依赖token.exp字段(Unix时间戳),通过将后端的expiresAt赋值给该字段,让NextAuth以外端令牌的过期时间为准。
  • 每次JWT刷新时,会自动检查token.exp,如果当前时间已超过该值,返回null会触发自动登出。
  • 修改session.expires是为了让前端的useSession钩子能正确显示会话的过期状态,确保前端逻辑和后端令牌过期时间一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 06:00:06