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

NextAuth中自定义返回含ID的用户对象的问题求助

解决NextAuth邮箱登录返回用户ID的问题

问题核心在于NextAuth的Credentials登录需要通过JWT回调持久化用户数据,再通过Session回调同步到前端会话,你当前的代码缺少这两个关键步骤的正确处理,同时类型定义也需要调整。

具体修改步骤:


1. 扩展NextAuth的类型定义(解决TS类型提示问题)

不要直接重定义Session接口,而是通过模块扩展的方式覆盖NextAuth的默认类型,确保TS能识别自定义的用户字段:

import NextAuth, { DefaultSession, DefaultJWT } from "next-auth";

declare module "next-auth" {
  interface Session {
    user: {
      id: string;
      // 可添加其他需要的字段,比如address
      address?: string;
    } & DefaultSession["user"];
  }

  interface JWT {
    id: string;
    address?: string;
  }
}

2. 启用并完善JWT回调

JWT回调是存储自定义用户数据的关键,在用户首次登录时(user参数存在),把authorize返回的字段存入token:

callbacks: {
  async jwt({ token, user }) {
    // 用户首次登录时,user是authorize返回的对象
    if (user) {
      token.id = user.id;
      token.address = user.address; // 如果需要其他字段
    }
    return token;
  },
  // ... 其他回调
}

3. 修改Session回调,同步token数据到会话

在Session回调里,把JWT token中的自定义字段合并到session.user,返回修改后的session:

async session({ session, token }) {
  // 把token里的id和其他字段同步到session.user
  if (session.user) {
    session.user.id = token.id;
    session.user.address = token.address;
  }
  return session;
},

4. 修正authorize方法的返回值

从Firebase的用户对象中获取真实数据,不要硬编码:

async authorize(credentials, req) {
  try {
    const userCredential = await signInWithEmailAndPassword(
      auth,
      credentials?.email!,
      credentials?.password!
    );
    const firebaseUser = userCredential.user;
    
    if (firebaseUser) {
      return {
        id: firebaseUser.uid,
        name: firebaseUser.displayName || "",
        email: firebaseUser.email || "",
        address: "" // 如果需要从Firebase获取,可在这里添加逻辑
      };
    }
  } catch (err) {
    console.error("登录失败:", err);
  }
  return null;
},

完整修改后的代码

import NextAuth, { AuthOptions, DefaultSession, DefaultJWT } from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import CredentialsProvider from 'next-auth/providers/credentials';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '@/src/lib/firebase';

// 扩展NextAuth类型
declare module "next-auth" {
  interface Session {
    user: {
      id: string;
      address?: string;
    } & DefaultSession["user"];
  }

  interface JWT {
    id: string;
    address?: string;
  }
}

export const authOptions: AuthOptions = {
  providers: [
    GoogleProvider({
      name: 'google',
      clientId: process.env.GOOGLE_ID!,
      clientSecret: process.env.GOOGLE_SECRET!,
    }),
    CredentialsProvider({
      id: 'email',
      name: 'email',
      credentials: {
        email: {
          label: 'Email',
          type: 'text',
          placeholder: 'your-email@example.com',
        },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials, req) {
        try {
          const userCredential = await signInWithEmailAndPassword(
            auth,
            credentials?.email!,
            credentials?.password!
          );
          const firebaseUser = userCredential.user;
          
          if (firebaseUser) {
            return {
              id: firebaseUser.uid,
              name: firebaseUser.displayName || "未知用户",
              email: firebaseUser.email!,
              address: "" // 可根据实际需求从Firebase获取
            };
          }
        } catch (err) {
          console.error("邮箱登录失败:", err);
        }
        return null;
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
        token.address = user.address;
      }
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.id;
        session.user.address = token.address;
      }
      return session;
    },
  },
  // 可选:指定会话策略为jwt(CredentialsProvider默认就是jwt,可省略)
  session: {
    strategy: "jwt",
  },
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

关键说明

  • JWT回调:负责存储用户的自定义数据,只有用户首次登录时user参数才会存在,之后每次请求都会基于token更新。
  • Session回调:负责把JWT中的数据同步到前端可见的session对象中,前端通过useSession()就能拿到包含id的完整用户信息。
  • 类型扩展:确保TypeScript能正确识别自定义的用户字段,避免类型报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:44:58