我使用 nextJs 作为前端,express 作为后端。对于身份验证,我正在使用 Next-Auth。
当我从 13.5 升级 nextJs 14.1.0 时仍然没问题,但昨天我将下一个应用程序更新为最新版本(v14.1.4),并且收到了 auth api 错误。然后,我再次恢复到以前的版本(14.1.0)并解决了错误。但尝试登录时出现新错误。
问题是,会话存储中设置了太多的 Next-auth 会话 cookie(此处为 7)。
这是我的授权选项:
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { AuthOptions } from "next-auth";
import prisma from '@/libs/prismadb';
import bcrypt from "bcrypt";
import CredentialsProvider from 'next-auth/providers/credentials';
export const authOptions:AuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
name: 'credentials',
credentials:{
email: { label: 'email', type: 'text' },
password: { label: 'password', type: 'text'},
},
async authorize(credentials) {
if (!credentials?.email || !credentials.password) {
throw new Error(`Invalid credentials`)
}
const user = await prisma.user.findUnique({
where: {
email: credentials.email
}
});
if (!user || !user.hashedPassword) {
throw new Error(`Invalid credentials`);
}
const isCorrectPassword = await bcrypt.compare(credentials.password, user.hashedPassword);
if (!isCorrectPassword) {
throw new Error(`Incorrect password`)
}
return user;
}
})
],
debug: process.env.NODE_ENV === 'development',
session: {
strategy: 'jwt'
},
secret: process.env.NEXTAUTH_SECRET
}
我的 Next-Auth 文件夹路径是:
src/app/api/auth/[...nextauth]/route.ts
route.ts 包括:
import NextAuth from 'next-auth/next';
import { authOptions } from '@/utils/authOptions';
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
我该如何解决这个问题?或者有什么好的资源可以使用我的外部快递服务器进行身份验证吗?
我尝试重建应用程序并在将 nextJs 应用程序升级到最新版本后放弃所有更改。
一种解决方法是使用 回调
简短回答: 仅保存会话中所需的内容以避免出现此警告。
我与您分享一个例子。 这款没有 prisma 适配器,但逻辑类似,请参考。
在此示例中,登录端点响应对象包括
当我们考虑代币大小时,收到此警告是完全正常的。为了解决这个问题,我们只保存会话中需要的内容。为此,我们添加两个回调方法,如下所示。
callbacks: {
async jwt({ token, user, account }) {
// I advise you to log those variables here to see what you get
if (account && user) {
token.id = user.id
token.accessToken = user.accessToken
token.refreshToken = user.refreshToken
// idToken may include some user information such as email, I leave this piece of code as an example to understand how we can extract information from it
const decodedIdToken = JSON.parse(Buffer.from(user.idToken.split(".")[1], "base64").toString())
if (decodedIdToken) {
token.email = decodedIdToken["email"]
}
}
const { refreshToken, ...rest } = token // we do not return refreshToken here because it would increase the session cookie size
return rest
},
async session({ session, token }) {
return {
...session,
user: {
...session.user,
id: token.id as string,
email: token.email as string,
accessToken: token.accessToken as string,
},
error: "" // long story, I will not go into the details about this now
}
}
}
当然,为了防止打字稿抱怨,我们需要在路径 /types 下添加 next-auth.d.ts 文件(types 文件夹位于项目的根目录)
import { DefaultSession } from "next-auth"
declare module "next-auth" {
interface User {
id: string
email: string
accessToken: string
refreshToken: string
idToken: string
}
interface Session {
user: User & DefaultSession["user"]
expires: string
error: string
}
}
如您所见,我们可以提取我们需要的任何信息。在此示例中,我们从会话 cookie 中排除了刷新令牌。有人可能会问“那么,一旦我的accessToken过期,我如何访问我的refreshToken?我们在会话中没有它”。答案是使用 cookies
成功登录后,我们可以将refreshToken保存为http only cookie
async authorize(credentials) {
const payload = {
email: credentials.email,
password: credentials.password,
}
const res = await fetch(`${process.env.API_BASE_URL}/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify(payload)
})
const user = await res.json()
if (!res.ok) {
throw new Error(user.message)
}
if (res.ok && user) {
cookies().set({
name: `refresh-token`,
value: user.refreshToken,
httpOnly: true,
sameSite: "strict",
secure: true,
} as any);
return user;
}
return null;
}