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

使用NextAuth CredentialsProvider时Session回调未触发的问题

问题原因

当你设置session: { strategy: "database" }时,NextAuth的数据库适配器仅会为OAuth/Email这类有状态认证提供者自动创建会话并触发session回调,但CredentialsProvider是无状态认证方式,默认不会触发数据库会话的创建流程,这就是为什么你能看到authorize和jwt的日志,却看不到session回调日志、数据库也没有会话记录的原因。而Google登录属于OAuth提供者,适配器会自动处理会话创建,因此session回调正常触发。

解决方案

推荐采用JWT会话策略(这是CredentialsProvider的常规搭配,配置简单且符合无状态认证的设计),具体调整如下:

1. 修改会话策略与回调逻辑

import NextAuth, { type NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import bcryptjs from "bcryptjs";
import { PrismaAdapter } from "@next-auth/prisma-adapter";

import { env } from "../../../env/server.mjs";
import { prisma } from "../../../server/db/client";

export const authOptions: NextAuthOptions = {
  callbacks: {
    async session({ session, token }) {
      console.log('session ', session)
      console.log('token ', token)
      // 从JWT中提取自定义用户信息,注入到会话中
      if (session.user) {
        session.user.id = token.id as string;
        session.user.email = token.email as string;
        session.user.isRegistered = token.isRegistered as boolean;
      }
      return session;
    },
    async jwt({ token, user }) {
      console.log('user is ', user)
      console.log('token is ', token)
      // 登录时将authorize返回的用户信息存入JWT
      if (user) {
        token.id = user.id;
        token.email = user.email;
        token.isRegistered = user.isRegistered;
      }
      return token;
    }
  },
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
    }),
    CredentialsProvider({
      name: "credentials",
      credentials: {
        email: { label: 'Email', name: 'email', type: 'email' },
        password: { label: 'Password', name: 'password', type: 'password' }
      },
      async authorize(credentials) {
        if(!credentials) return null;
        const { email, password } = credentials;
        if(!email || !password) return null;

        const userExists = await prisma.user.findUnique({
          where: { email: credentials.email }
        });
        if (!userExists || !userExists.hashedPassword) return null;
      
        const isPasswordValid = await bcryptjs.compare(password, userExists.hashedPassword);
        if(!isPasswordValid) return null;

        // 返回自定义用户对象(无需修改,保持原逻辑即可)
        return {
          id: userExists.id,
          email: userExists.email,
          isRegistered: userExists.isRegistered
        };
      }
    })
  ], 
  // 切换为JWT会话策略
  session: { strategy: "jwt" },
  // 可选:配置JWT过期时间(默认30天)
  jwt: {
    maxAge: 30 * 24 * 60 * 60,
  }
};

export default NextAuth(authOptions);

2. 验证效果

修改后,通过CredentialsProvider登录时:

  • authorize验证通过后返回用户对象
  • jwt回调会将用户信息存入JWT令牌
  • 当前端调用useSession()或getSession()时,session回调会触发,从JWT中提取信息并返回完整会话
  • 会话信息通过JWT维护,无需依赖数据库存储(数据库仍会存储用户基础信息)
备选方案(不推荐)

如果必须坚持使用database会话策略,需要在authorize中返回完整的数据库用户对象(而非自定义对象),同时手动触发会话创建逻辑,但这种方式需要深入NextAuth内部机制,复杂度较高,不建议采用。

内容的提问来源于stack exchange,提问作者Matthew Mannning

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 11:37:46