解决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总是失败,一直返回token
为null
。
经过一番排查之后,了解到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