NestJS中使用passport-apple实现Apple登录无id_token及用户识别问题
Apple登录无法获取id_token及用户唯一标识问题解决(NestJS + passport-apple)
问题根源
- 原策略没实现
validate方法——这是passport策略的核心,没有它根本拿不到Apple返回的id_token、access_token等关键数据。 passReqToCallback设为false,导致获取不到请求体里的user信息,也没法触发完整的token交换流程。- 手动加
response_type属于画蛇添足,passport-apple会自动处理这个参数。
直接改策略代码
把你的AppleStrategy改成下面这样,核心是加validate方法,打开passReqToCallback:
import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy, VerifyCallback } from 'passport-apple'; import { readFileSync } from 'fs'; @Injectable() export class AppleStrategy extends PassportStrategy(Strategy, 'apple') { constructor(configService: ConfigService) { super({ clientID: configService.get<string>('APPLE_CLIENT_ID'), teamID: configService.get<string>('APPLE_TEAM_ID'), keyID: configService.get<string>('APPLE_KEY_ID'), key: readFileSync(__dirname + '/../../../apple_secret/apple_secret_key.p8'), callbackURL: configService.get<string>('APPLE_CALLBACK_URL'), passReqToCallback: true, // 改成true,才能拿到完整请求参数 scope: ['name', 'email'], }); } // 必须加这个方法,passport靠它传递认证后的用户数据 async validate( req: Request, accessToken: string, refreshToken: string, idToken: string, profile: any, done: VerifyCallback, ): Promise<any> { // 从id_token里解析出用户唯一标识sub——这是Apple给用户的永久ID,永远不变 const userSub = JSON.parse(Buffer.from(idToken.split('.')[1], 'base64').toString()).sub; // 首次登录时,user信息在req.body里,后续登录没有 let userInfo = null; if (req.body.user) { userInfo = JSON.parse(req.body.user); } // 组装你需要的用户数据,丢给后面的业务逻辑用 const user = { sub: userSub, email: userInfo?.email || profile?.email, name: userInfo?.name, accessToken, refreshToken, }; done(null, user); } }
用户识别逻辑
- 首次登录:把解析到的
sub和user里的姓名、邮箱绑定,存到你的数据库里。 - 后续登录:直接用
sub去数据库里匹配用户,不管有没有user字段,都能唯一识别。
关键提醒
- Apple返回的
code是一次性的,用完就失效,绝对不能用它当用户标识。 sub是唯一可靠的用户ID,同一个用户在你的客户端下,sub永远不会变,这是你唯一能依赖的字段。- 首次登录后,Apple不会再返回姓名和邮箱,所以第一次一定要把这些信息存好。
内容的提问来源于stack exchange,提问作者zackOverflow
相关产品推荐
相关产品推荐

