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
相关产品推荐
相关产品推荐

