解决Next-Auth/jwt的getToken生产环境老是返回null

2024-08 1

涉及版本号:

{
    "next": "14.2.3",
    "next-auth": "5.0.0-beta.20",
}

middleware.ts中的代码初版如下:

import { getToken } from "next-auth/jwt";
import { type NextRequest, NextResponse } from "next/server";
import { PATHS } from "@/constants";

export async function middleware(req: NextRequest) {
  const secret = process.env.AUTH_SECRET;
  try {
    // 确保环境变量已定义
    if (!secret) {
      throw new Error("AUTH_SECRET is not defined");
    }

    console.log("Secret:", secret);
    console.log("Request Headers:", req.headers.get("cookie"));

    const token = await getToken({req, secret});
    const { pathname } = req.nextUrl;

    const redirectAuthSignUrl = new URL(PATHS.AUTH_SIGN_IN, req.url);
    console.log("middleware token", token);

    if (!token) {
      return NextResponse.redirect(redirectAuthSignUrl, { status: 302 });
    }

    // 只有管理员才能访问 /admin 路径
    if (pathname.startsWith(PATHS.ADMIN_HOME) && token.role !== "admin") {
      return NextResponse.redirect(new URL("/403", req.url));
    }

    // 放行其他请求
    return NextResponse.next();
  } catch (e) {
    console.log("middleware报错", e);
    console.log("middleware process.env", process.env);
  }
}

export const config = {
  matcher: ["/admin/:path*"],
};

{ req: NextRequest; secret: string; }" 中缺少属性 "salt"

首先,ts类型提示getToken({req, secret})报错如下:

 "{ req: NextRequest; secret: string; }" 中缺少属性 "salt",但类型 "GetTokenParams<false>" 中需要该属性。

经过一番折腾,在这里找到解决方案,给getToken加上salt属性就行:

    const token = await getToken({
      req,
      secret,
      salt: "authjs.session-token"
    });

本地调试非常成功,能登录,能跳转,也能实现拦截,打印token也能正确解析。
之后执行npm run build打包,打包后还在本地执行npm run start查看了一番,也能正常运行。

生产环境getToken老是返回null

将build后的产物push到服务器后,服务器执行npm run start,结果发现线上环境解析token总是失败,一直返回tokennull

经过一番排查之后,了解到getToken传入的第一个参数req,主要是用到req.headers.cookie里的session-token字段,这个字段,在本地的完整名称是authjs.session-token

Request Headers cookie: authjs.callback-url=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fsignin; authjs.csrf-token=b3fb60796fe7303d38aee7ecac629bed230a18203313782e7b38a895181bd51d%7C80d1217e5f74083771c4e8ebca5fe5a47859e87222db8e8bb65726a499f3bd14; authjs.session-token=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoiWmxScjlydk5MVXJOMFA2OF9pTVFKYlN3U1BpdENfbzZ3eXVsVnJQV21UMXhsby1IOTRNOUtnRERkc0Q5UkJqb3lWZDBwaVZjNHptTzZzS1FIM0ZXZFEifQ..0q5b8APPATKwEoO0vunyNQ.XsHKTp3bl7v4aIHew7VifV2T_RGnNHRCKJF-6Au0iI5Wt1jCC8ZkrwMDCWVTey_rWk7caApIqStl3fo5Aul8GKIWTuTQ-ZKJKCzq9va4ZPo4XQ_BfLb4rui3zAjAc7atfmtPe75NJ7zZaAre1reHStf4JbDLGYFE_2iWbIQodIu8SXRBbPgkDhrjqcQZKwwReBREu_9nkzNicE6n50TnknFXTXsLvh6oIiZxeU-CcpzeZNO8AcfBjav3lzNHIAFyoIRkTduwGXqwIvZPQ5oSBKX4801Uf6Xs7tSXyJJ5RR0.1tcZZ-1K-x9NwvuPLU21W_5eR3WH4r9Et-rVbcTAXpw

但是在生产环境中,这个字段变成了__Secure-authjs.session-token=xxx,相比与本地,多了一个__Secure-的前缀,为什么会多这个前缀?因为生产环境是https,该前缀标识该Cookie应该仅通过HTTPS传输。
之后google了一番,怀疑有可能是这个的区别,又查到可以通过给getToken()指定第四个参数cookieName来决定解析的cookie中token的名字。
于是将getToken改版如下:

const token = await getToken({
  req,
  secret,
  salt: "authjs.session-token",
  cookieName:
    process.env.NODE_ENV === "production"
      ? "__Secure-authjs.session-token"
      : "authjs.session-token",
});

试了之后还是报同样的错误,google了一番也查不到,突然注意到salt字段里也用到了authjs.session-token,怀疑是否也要改动这里,于是继续调整getToken如下:

const token = await getToken({
  req,
  secret,
  salt:
    process.env.NODE_ENV === "production"
      ? "__Secure-authjs.session-token"
      : "authjs.session-token",
  cookieName:
    process.env.NODE_ENV === "production"
      ? "__Secure-authjs.session-token"
      : "authjs.session-token",
});

然后就!!!解决了!!!!

  • Next