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

NextJS 13.1.1 + NextAuth 4.3.4:middleware.ts中getToken始终返回null

问题:NextAuth getToken在Middleware中始终返回null

使用NextJS 13.1.1和next-auth 4.3.4,调用getToken({req, secret})始终返回null。已配置process.env.NEXTAUTH_SECRET,Middleware在未认证时能正常重定向到登录页,但无法获取cookie中的令牌。

package.json:

{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "deploy": "npx serverless"
  },
  "dependencies": {
    "@emotion/react": "^11.10.0",
    "@emotion/styled": "^11.10.0",
    "@mui/icons-material": "^5.8.4",
    "@mui/lab": "^5.0.0-alpha.95",
    "@mui/material": "^5.10.0",
    "@reduxjs/toolkit": "^1.8.4",
    "animate.css": "^4.1.1",
    "chart.js": "^3.9.1",
    "cookie": "^0.5.0",
    "html-to-image": "^1.11.4",
    "jsonwebtoken": "^9.0.0",
    "jspdf": "^2.1.0",
    "moment": "^2.29.4",
    "next": "^13.1.1",
    "next-auth": "^4.3.4",
    "react": "^18.2.0",
    "react-chartjs-2": "^4.3.1",
    "react-cookie": "^4.1.1",
    "react-dom": "^18.2.0",
    "react-dropzone": "^14.2.2",
    "react-hook-form": "^7.34.1",
    "react-redux": "^8.0.2",
    "sass": "^1.54.4",
    "swr": "^1.3.0",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "@types/cookie": "^0.5.1",
    "@types/jsonwebtoken": "^9.0.0",
    "@types/node": "18.7.5",
    "@types/react": "18.0.17",
    "@types/react-dom": "18.0.6",
    "@types/uuid": "^8.3.4",
    "eslint": "8.22.0",
    "eslint-config-next": "^13.1.1",
    "typescript": "4.7.4"
  }
}

[...nextauth].js:

import NextAuth, { NextAuthOptions } from "next-auth";

// Providers
import Credentials from "next-auth/providers/credentials";

// Services
import { authService, refreshAccessToken } from "../../../services";

export const authOptions: NextAuthOptions = {
  providers: [
    Credentials({
      name: "Credentials",
      credentials: {
        username: {
          label: "Username",
          type: "text",
          placeholder: "C.C.",
          autoComplete: true,
        },
        password: {
          label: "Password",
          type: "password",
          placeholder: "Type your password...",
          autoComplete: true,
        },
      },
      async authorize(credentials) {
        return await authService({
          username: credentials!.username,
          password: credentials!.password,
        });
      },
    }),
  ],
  pages: {
    signIn: "/auth/login",
  },
  session: {
    strategy: "jwt",
    maxAge: 60 * 60 * 7, // 7 hours
    updateAge: 60 * 60 * 2,
  },
  callbacks: {
    async jwt({ token, account, user }) {
      if (user) {
        token.accessToken = user.accessToken;
        token.refreshToken = user.refreshToken;

        if (account) {
          switch (account.type) {
            case "credentials":
              token.user = user.user;
              token.accessTokenExpires = user.accessTokenExpires;
              break;
          }
        }
      }

      if (Date.now() / 1000 < (token.accessTokenExpires as number)) {
        return token;
      }

      return await refreshAccessToken(token);
    },
    async session({ session, token }) {
      session.accessToken = token.accessToken;
      session.user = token.user as any;

      return session;
    },
  },
  // secret: process.env.NEXTAUTH_SECRET!,
  secret: process.env.NEXTAUTH_SECRET,
};

export default NextAuth(authOptions);

middleware.ts:

import { getToken } from "next-auth/jwt";
import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";

// Interfaces
import { IUser } from "./interfaces";

const secret = process.env.NEXTAUTH_SECRET;

export default withAuth(async function middleware(req) {
  const token = await getToken({ req, secret }); // ALWAYS returns null
  const { user } = (token as { user: IUser; accessToken: string }) || {};

  const isAuth = !!token;
  const isAuthPage = req.nextUrl.pathname.startsWith("/auth");

  if (isAuthPage) {
    if (isAuth) {
      // return NextResponse.redirect(new URL(`/${user.hierarchy}`, req.url));
      return NextResponse.redirect(new URL(`/role`, req.url));
    }

    return null;
  }

  if (!isAuth) {
    let from = req.nextUrl.pathname;
    if (req.nextUrl.search) {
      from += req.nextUrl.search;
    }

    return NextResponse.redirect(new URL(`/auth/login`, req.url));
  }
});

解决方案

针对你的问题,以下是几个可能的原因和解决步骤:

1. 升级next-auth版本以兼容NextJS 13

next-auth 4.3.4发布于2022年10月,而NextJS 13.1.1是2023年1月的版本,两者存在兼容性问题——早期的next-auth版本对NextJS 13的Middleware API支持不完善,导致无法正确读取cookie中的令牌。

操作:将next-auth升级到至少4.18.0以上的版本(建议最新稳定版):

npm install next-auth@latest

2. 显式配置NextAuth的Cookie参数

默认的Cookie配置可能在NextJS 13的环境下无法被Middleware正确读取,需要在authOptions中添加Cookie配置,确保路径、SameSite等参数正确:

修改[...nextauth].js中的authOptions,添加cookies配置:

export const authOptions: NextAuthOptions = {
  // ...其他配置
  cookies: {
    sessionToken: {
      name: `next-auth.session-token`,
      options: {
        httpOnly: true,
        sameSite: "lax",
        path: "/",
        secure: process.env.NODE_ENV === "production",
      },
    },
    // 若需要其他cookie配置,可参考next-auth文档添加
  },
  // ...其他配置
};

3. 利用withAuth上下文直接获取Token

使用withAuth包裹Middleware时,可直接通过req.nextauth.token获取令牌,无需再调用getToken,避免手动传递参数出错:

修改middleware.ts:

import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";

// Interfaces
import { IUser } from "./interfaces";

export default withAuth(async function middleware(req) {
  // 直接从withAuth的上下文获取token
  const token = req.nextauth.token;
  const { user } = (token as { user: IUser; accessToken: string }) || {};

  const isAuth = !!token;
  const isAuthPage = req.nextUrl.pathname.startsWith("/auth");

  if (isAuthPage) {
    if (isAuth) {
      return NextResponse.redirect(new URL(`/role`, req.url));
    }
    return null;
  }

  if (!isAuth) {
    let from = req.nextUrl.pathname;
    if (req.nextUrl.search) {
      from += req.nextUrl.search;
    }
    return NextResponse.redirect(new URL(`/auth/login`, req.url));
  }
});

4. 验证NEXTAUTH_SECRET的有效性

确保process.env.NEXTAUTH_SECRET在Middleware中能正确读取,且值与[...nextauth].js中使用的一致。如果是Serverless部署,需确认环境变量已正确注入到部署环境中。

可在Middleware中临时添加日志验证:

console.log("NEXTAUTH_SECRET:", secret); // 检查是否输出正确的值

5. 检查authorize返回的用户结构

确保authService返回的用户对象包含accessToken、refreshToken、user、accessTokenExpires字段,且accessTokenExpires是正确的秒级时间戳。如果字段缺失或类型错误,会导致JWT令牌生成异常,最终无法被getToken解析。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 20:15:55