Next-auth Credentials Provider认证状态不更新问题求助
解决NextAuth 4.18.4 Credentials Provider登录后状态不更新问题
问题原因
当使用redirect: false调用signIn时,NextAuth不会自动更新客户端的session状态;同时Credentials Provider需要正确配置JWT和session回调,确保用户信息被正确存储和同步。
解决方案
1. 完善NextAuth配置文件 (api/auth/[...nextauth].ts)
添加session策略和回调函数,确保用户信息正确映射到JWT和session中:
import NextAuth from 'next-auth' import CredentialsProvider from 'next-auth/providers/credentials' import { verifyPassword } from '../../../lib/auth' import conn from '../../../lib/db' export default NextAuth({ providers: [ CredentialsProvider({ async authorize(credentials) { const query = `SELECT * FROM users WHERE username = $1` const values = [credentials.username] let user try { const result = await conn.query(query, values) if (result.rows.length > 0) user = result.rows[0] } catch (err) { console.log(`Error fetching user from DB: ${err.stack}`) throw new Error(err) } if (!user) throw new Error('No user found!') const isValid = await verifyPassword( credentials?.password, user.password ) if (!isValid) throw new Error('Could not log you in!') // 返回包含id的用户对象,NextAuth默认用id标识用户 return { id: user.id, username: user.username, profilePic: user.profile_pic } }, }), ], // Credentials Provider必须使用JWT策略 session: { strategy: 'jwt', }, callbacks: { // 将用户信息添加到JWT中 async jwt({ token, user }) { if (user) { token.id = user.id token.username = user.username token.profilePic = user.profilePic } return token }, // 将JWT中的数据映射到session对象,供前端使用 async session({ session, token }) { if (token) { session.user.id = token.id session.user.username = token.username session.user.profilePic = token.profilePic } return session }, }, })
2. 修改登录页,手动刷新session
在登录成功后调用refetch()更新客户端session状态:
import { useForm } from 'react-hook-form' import { z } from 'zod' import { zodResolver } from '@hookform/resolvers/zod' import { signIn } from 'next-auth/react' import { useRouter } from 'next/router' import { useSession } from 'next-auth/react' // 新增导入 import Input from "../components/input" const validationSchema = z .object({ userName: z .string() .min(1, { message: 'Username is required' }) .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{5,10}$/, { message: '5-10 upper and lowercase letters, and digits', }), password: z .string() .min(5, { message: 'Between 5-10 characters' }) .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{5,10}$/, { message: 'Upper and lowercase letters, and digits', }) }) export default function Auth() { const { register, handleSubmit, watch, formState: { errors, isValid }, setValue } = useForm({ mode: 'all', resolver: zodResolver(validationSchema), }) const router = useRouter() const { refetch } = useSession() // 获取session刷新方法 async function submitHandler(data: any) { const result = await signIn('credentials', { redirect: false, username: data.userName, password: data.password }) if (!result?.error) { setValue('userName', '') setValue('password', '') await refetch() // 手动刷新session,更新客户端状态 router.replace('/') } else { console.log(`Error: ${JSON.stringify(result.error)}`) } } return ( <div className='text-white max-w-4xl mx-auto pt-10 pb-20 px-2'> <h1 className='text-2xl text-center pb-8'>Log in</h1> <form onSubmit={handleSubmit(submitHandler)} className='space-y-4 flex flex-col items-center ' > <Input id='userName' type='text' label='Username' register={register} registerOptions={{ required: true }} errors={errors} isRequired={true} /> <Input id='password' type='password' label='Password' register={register} registerOptions={{ required: true }} errors={errors} isRequired={true} /> <button type='submit' disabled={!isValid} className={`p-3 border-[1px] border-slate-500 rounded-md hover:enabled:bg-white hover:enabled:bg-opacity-20 disabled:cursor-not-allowed w-[90%]`} > {isValid ? 'Log In' : 'Please, fill the form'} </button> </form> </div> ) }
关键说明
- Credentials Provider不支持数据库session策略,必须显式声明
strategy: 'jwt'。 - 使用
redirect: false时,signIn不会自动触发客户端session更新,需要手动调用refetch()同步状态。 - 确保返回的用户对象包含
id字段,或在JWT回调中手动映射用户标识字段,NextAuth依赖此字段关联用户。
内容的提问来源于stack exchange,提问作者Bobby Wan-Kenobi
相关产品推荐
相关产品推荐

