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

Next.js NextAuth signIn回调获取GitHub用户名存库方法

问题根因
  • 回调执行顺序搞反:NextAuth OAuth流程的回调执行顺序固定为 signIn → jwt → session,你在jwt回调里给user对象追加属性时,signIn回调已经执行完毕,自然读不到后加的login值。
  • 无效修改user对象:NextAuth在各个回调里传入的user是OAuth流程生成的默认只读对象,你在jwt回调里手动加的属性不会跨回调传递,只有存在token里的字段才会在后续流程里保留。
  • 忽略了signIn回调自带的数据源:signIn回调本身就接收GitHub返回的原始profile对象,login(GitHub用户名)、id(数值型用户ID)等字段都在这个对象里,不需要绕路去jwt里赋值。
修复方案

1. 直接在signIn回调读取profile字段完成入库

不要在jwt回调里绕路传值,直接从signIn回调的入参profile里取GitHub用户名,同时把原来的纯插入逻辑改成upsert,避免用户重复登录触发唯一键冲突:

async signIn({ user: User, account: Account, profile, email: Email }) {
  const client = await clientPromise;
  const db = client.db("userData");
  const users = db.collection("users");

  // 直接读取GitHub返回的原始字段
  const githubLogin = profile?.login as string;
  const githubId = profile?.id as number;

  // 必要字段校验,缺失直接拒绝登录
  if (!githubLogin || !githubId) {
    console.log("GitHub返回数据缺失id或用户名");
    return false;
  }

  try {
    // upsert逻辑:存在则更新,不存在则插入,避免重复插入报错
    await users.updateOne(
      { _id: githubId },
      {
        $set: {
          githubLogin: githubLogin,
          userInfo: {
            name: User.name,
            email: User.email,
            avatar: User.image
          },
          lastLoginAt: new Date()
        },
        $setOnInsert: {
          createdAt: new Date()
        }
      },
      { upsert: true }
    );
    console.log(`用户${githubLogin}数据同步完成`);
    return true;
  } catch (error) {
    console.log("用户数据入库失败:", error);
    return false;
  }
}

2. 修正jwt和session回调的传值逻辑

不要尝试修改跨回调传递的user对象,自定义字段全部存在token中,再从token传递给前端session:

async jwt({ token, account, profile }) {
  // 仅首次登录时写入OAuth返回的字段到token
  if (account && profile) {
    token.accessToken = account.access_token;
    token.githubId = profile.id as number;
    token.githubLogin = profile.login as string;
  }
  return token;
},
async session({ session, token }) {
  // 从token读取字段传递给客户端session
  session.accessToken = token.accessToken as string;
  session.githubId = token.githubId as number;
  session.githubLogin = token.githubLogin as string;
  return session;
}

3. 补充TypeScript类型声明

在项目根目录新建next-auth.d.ts文件,补全自定义字段的类型,避免TS报错:

import NextAuth from "next-auth";

declare module "next-auth" {
  interface Session {
    accessToken: string;
    githubId: number;
    githubLogin: string;
  }
  interface Profile {
    id: number;
    login: string;
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    accessToken: string;
    githubId: number;
    githubLogin: string;
  }
}
额外注意事项
  • 不建议用GitHub用户名作为MongoDB主键:GitHub支持用户修改用户名,用户名变化后会导致主键关联的所有数据失效,用GitHub返回的固定数值ID作为_id是更稳妥的方案,用户名单独存为普通字段即可,实现domain.com/[github username]路由时,根据URL中的用户名查询对应githubLogin字段的用户数据即可,用户修改用户名时同步更新该字段。
  • 原代码中导入的useSession是客户端React Hook,在服务端的NextAuth配置文件中完全无效,可以直接删除。
  • 原代码中每次登录都执行insertOne,用户二次登录时会因为_id重复抛出错误,虽然catch分支返回了true允许登录,但用户信息永远不会更新,必须替换为upsert逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:12:20