使用create-t3-stack时Credentials Provider会话始终返回null求助
问题:NextAuth Credentials登录后Session返回null
我用NextAuth的CredentialsProvider结合数据库验证邮箱密码,登录时signIn返回ok: true,但在getServerSideProps中获取的Session始终为null,已经调试一天了,以下是相关代码:
我的NextAuth配置
CredentialsProvider({ name: "Credentials", credentials: {}, async authorize(credentials: any) { //check user const user: any = await prisma.user.findUnique({ where: { email: credentials.email, }, }); if (!user) { throw new Error("No user found"); } console.log("user credentials", user); const checkPassword = await compare( credentials.password, user?.password ); console.log("user?.password", user?.password); if (!checkPassword || user.email !== credentials.email) { throw new Error("Password or Email dont match"); } return user; }, }), ], // Include user.id on session callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, session: { strategy: "jwt", }, }, jwt: { secret: "test", }, secret: "test", // pages: { // signIn: "/", // newUser: "/signin", // signOut: "/signout", // error: '/error' // },
登录页面的signIn调用
async function onSubmitLoginValues(values: LoginValuesProps): Promise<any> { const result = await signIn("credentials", { email: values?.email, password: values?.password, redirect: false, callbackUrl: "/", }); console.log("result", result); if (result?.ok) router.push("/"); return result; }
signIn返回结果:
error: null ok: true status: 200 url: "http://localhost:3000/"
getServerSideProps中获取Session的尝试
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { const session = await getServerAuthSession(ctx); if (!session) { return { redirect: { destination: "/signin", permanent: false }, }; } return { props: { session, }, }; //OR console.log("ctx", ctx); const response = await fetch("http://localhost:3000/api/auth", { method: "GET", credentials: "include", //@ts-ignore headers: { Cookie: ctx.req.headers.cookie, }, }); console.log("RESPOSNDE", response.json()); if (!response) { return { redirect: { destination: "/signin", permanent: false }, }; } const data = await response.json(); return { props: { data } }; };
解决方案
问题出在JWT会话策略下的回调配置缺失,以下是修复步骤:
1. 补充jwt回调,将用户数据存入JWT
当使用jwt策略时,NextAuth不会自动把authorize返回的用户信息写入JWT,必须通过jwt回调手动处理:
callbacks: { // 新增jwt回调 async jwt({ token, user }) { // 用户登录成功时,将authorize返回的user数据合并到token if (user) { token.id = user.id; token.email = user.email; } return token; }, // 修改session回调,从token取数据而非user参数 async session({ session, token }) { if (session.user) { session.user.id = token.id as string; session.user.email = token.email as string; } return session; }, },
注意:在
jwt策略下,session回调的第二个参数是token而非user,你之前的代码错误地使用了user参数,导致无法赋值用户ID。
2. 优化authorize返回值
不要返回完整的用户对象(包含密码),只返回必要字段:
async authorize(credentials: any) { const user = await prisma.user.findUnique({ where: { email: credentials.email }, }); if (!user) throw new Error("No user found"); const passwordMatch = await compare(credentials.password, user.password); if (!passwordMatch) throw new Error("Password does not match"); // 只返回需要存入JWT的安全字段 return { id: user.id, email: user.email }; },
3. 确保getServerAuthSession正确封装
如果你自己封装了getServerAuthSession,要确保它使用官方的getServerSession方法:
// lib/auth.ts import { getServerSession } from "next-auth/next"; import { authOptions } from "../pages/api/auth/[...nextauth]"; export const getServerAuthSession = async (ctx: any) => { return await getServerSession(ctx.req, ctx.res, authOptions); };
4. 建议使用环境变量存储密钥
不要硬编码jwt.secret和secret,改用环境变量:
jwt: { secret: process.env.NEXTAUTH_SECRET }, secret: process.env.NEXTAUTH_SECRET,
修复后,重新登录,getServerSideProps就能正确获取到Session了。
内容的提问来源于stack exchange,提问作者RicardoFonseca
相关产品推荐
相关产品推荐

