You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 15:15:36