NextJS+NextAuth实现Google认证时存用户信息入库并返回Token
NextAuth Google登录同步用户数据至数据库+自定义会话Token实现方案
核心实现逻辑
要达成需求,只需要在原有配置基础上补充3部分逻辑,不需要改动已正常运行的凭证登录流程:
- 新增
signIn回调:识别Google登录请求,拿到Google返回的用户邮箱、姓名等信息后,发送POST请求到本地数据库服务完成用户数据存储 - 修正原有JWT回调的参数逻辑:读取数据库接口返回的用户数据,写入会话Token
- (可选)新增
session回调:把Token中存储的业务字段暴露给前端,方便页面直接读取用户信息
关键修改点说明
- Google登录触发时,会自动拿到用户公开的基础信息,不需要额外申请权限
- 数据库接口返回的用户ID、业务Token等自定义字段,全部存在JWT中,不会丢失
- 原有凭证登录的逻辑完全保留,不会影响现有账号密码登录功能
修改后完整配置代码
import NextAuth from "next-auth" import GoogleProvider from "next-auth/providers/google"; import CredentialsProvider from "next-auth/providers/credentials"; export default NextAuth({ providers: [ CredentialsProvider({ async authorize(credentials){ // 原有凭证登录校验逻辑保持不变 const res =await fetch('http://localhost:8080/user/login?deviceToken=eEiLMMkzR1ypiCwp068z97:APA91bEiBpfwCmpZ5-ijVU4FKcl-4d0QkuWrBtXgcZRJF06MUw8GJvcBn_4ci-v1IFOD8wMF0bNqEheFq0LR0Vz5hXIktT-7sMwOfR52ULhy14NgjiUUW_0nNs5gBXAZHwhtifJluS7v', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(credentials), }) const x=await res.json(); const user={email:x.user.email,name:`${x.user.firstName} ${x.user.lastName}`}; if(res.ok && user){ console.log("logged In"); return user; } console.log("login error"); return null; }}), GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorization: { params: { prompt: "consent", access_type: "offline", response_type: "code" } } }), ], jwt: { encryption:true, }, callbacks:{ // 新增signIn回调,处理Google登录时的用户数据同步 async signIn({ user, account, profile }) { // 仅处理Google登录场景 if (account.provider === 'google') { try { // 发送POST请求把用户信息存入数据库 const res = await fetch('http://localhost:8080/user/google-login?deviceToken=eEiLMMkzR1ypiCwp068z97:APA91bEiBpfwCmpZ5-ijVU4FKcl-4d0QkuWrBtXgcZRJF06MUw8GJvcBn_4ci-v1IFOD8wMF0bNqEheFq0LR0Vz5hXIktT-7sMwOfR52ULhy14NgjiUUW_0nNs5gBXAZHwhtifJluS7v', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: user.email, name: user.name, firstName: profile.given_name, lastName: profile.family_name, googleOpenId: profile.sub }) }) const dbResp = await res.json() if (res.ok && dbResp.user) { // 将数据库返回的用户信息挂载到user对象,供后续JWT回调读取 user.dbUserInfo = dbResp.user return true } // 接口返回错误时阻止登录 return false } catch (err) { console.error('Google登录同步用户数据失败:', err) return false } } // 凭证登录直接放行 return true }, // 修正原有JWT回调,写入数据库返回的自定义字段 async jwt({ token, user, account }) { if (account && user) { token.accessToken = account.accessToken; // Google登录场景,把数据库返回的信息写入Token if (account.provider === 'google' && user.dbUserInfo) { token.userId = user.dbUserInfo.id token.email = user.dbUserInfo.email token.name = `${user.dbUserInfo.firstName} ${user.dbUserInfo.lastName}` // 如果后端返回了业务Token,可一并存入 token.businessToken = user.dbUserInfo.token } // 凭证登录场景保持原有逻辑 if (account.provider === 'credentials') { token.email = user.email token.name = user.name } } return token; }, // 可选:新增session回调,把Token字段暴露给前端会话对象 async session({ session, token }) { session.user.id = token.userId session.user.email = token.email session.user.name = token.name session.accessToken = token.accessToken session.businessToken = token.businessToken return session } } })
注意事项
- 代码中Google用户入库的接口地址
/user/google-login需要替换成你后端实际的接口路径,请求体字段也可以根据后端要求调整 - 如果生产环境部署,
localhost:8080要替换成后端服务的实际可访问域名 - 若不需要前端读取业务Token等自定义字段,可以去掉
session回调,不影响核心功能
内容的提问来源于stack exchange,提问作者Gaurav Kumawat
相关产品推荐
相关产品推荐

