NestJS Passport Apple 策略 — 与 Apple 为 NestJS 签名
苹果在几分钟内登录了雀巢JS. 纳入护照战略 用苹果签名 输入 NestJS 应用程序 – Apple OAuth 2.0 认证,身份符 (JWT) 验证,刷新令牌验证,撤销令牌以删除账户,以及 Apple 的服务器对服务器通知,都用一个输入,测试的软件包 900k+ 下载。 。 。 .
与 @nestjs/passport 财务报告和审定财务报表 AuthGuard,支持网络应用(Services ID)和原生iOS/macOS应用(App ID),并在您代码看到之前,验证所有与苹果公钥相对抗的令牌.
特征
- 🍎 插件和游戏苹果登录 - 与NestJS 10 & 11通过
AuthGuard - 🔐 在你看到它之前已经验证过 – ID 代号与 Apple 的公共密钥对照检查; 您
validate只接收可信任的数据 - 🧾 类型
Profile电子邮件、 校验状态和隐藏我的电子邮件旗 - 📱 本地应用程序支持 核查iOS/macOS应用程序中的身份标志,并提供非ce保护(
verifyIdToken(中文(简体) ) - ♻️ 会话验证 ——检查用户的Apple授权是否仍然有效(
refreshAccessToken(中文(简体) ) - 🗑️ 账户删除准备 —— App Store 准则 5.1.1 (五) 撤销令状 (
revokeToken(中文(简体) ) - 📨 服务器对服务器的通知 ——验证苹果公司签署的账户事件(
verifyServerNotification(中文(简体) ) - 🛡️ CSRF 保护 ——可选 OAuth
state生成和验证 - ✅ 完全测试 ——单位和集成套件运行于模拟 Apple 服务器的全流,所发布的类型由 CI 中真正的严格模式 NestJS 消费者编译
所需资源
- 与 NestJS 10 一起使用的节点.js 18 或以后
- 与 NestJS 11(NestJS 11本身需要 Node.js 20)一起使用时的节点.js 20或更后
安装
npm install @arendajaelu/nestjs-passport-apple
使用量
战略设置
以下示例显示所有常用选项:
import { Injectable } from '@nestjs/common';
import { AuthGuard, PassportStrategy } from '@nestjs/passport';
import { Strategy, Profile } from '@arendajaelu/nestjs-passport-apple';
const APPLE_STRATEGY_NAME = 'apple';
@Injectable()
export class AppleStrategy extends PassportStrategy(Strategy, APPLE_STRATEGY_NAME) {
constructor() {
super({
clientID: process.env.APPLE_OAUTH_CLIENT_ID,
teamID: process.env.APPLE_TEAMID,
keyID: process.env.APPLE_KEYID,
key: process.env.APPLE_KEY_CONTENTS,
// OR
keyFilePath: process.env.APPLE_KEYFILE_PATH,
callbackURL: process.env.APPLE_OAUTH_CALLBACK_URL,
scope: ['email', 'name'],
// state: true, // recommended in production for CSRF protection; requires express-session
passReqToCallback: false,
});
}
async validate(_accessToken: string, _refreshToken: string, profile: Profile) {
return {
emailAddress: profile.email,
firstName: profile.name?.firstName || '',
lastName: profile.name?.lastName || '',
};
}
}
@Injectable()
export class AppleOAuthGuard extends AuthGuard(APPLE_STRATEGY_NAME) {}
说明: 登记册
AppleStrategy输入providers模块的数组.
控制器终点
import { Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AppleOAuthGuard } from './strategies/apple.strategy';
@ApiTags('oauth')
@Controller('oauth')
export class OAuthController {
@Get('apple')
@UseGuards(AppleOAuthGuard)
async appleLogin() {}
@Post('apple/callback')
@UseGuards(AppleOAuthGuard)
async appleCallback(@Req() req) {
return req.user;
}
}
Apple 将授权召回作为
application/x-www-form-urlencodedHTTP POST 当范围如:name或者说response_mode=form_post) (中文(简体) ). 因此,回调路线必须是@Post已启用 URL 编码的正文解析器 —— NestJS 默认允许此功能 。 回调 URL 必须完全匹配在苹果开发者门户网站上注册的 HTTPS 返回 URL; 苹果不接受localhost或 IP 地址回调 URL 。 授权代码为单用,5分钟后失效.
战略选项
| 选项 | 需求 | 说明 |
|---|---|---|
clientID | 对 | 用作 OAuth 客户端标识的 Apple 服务ID( 或 App ID) |
teamID | 对 | 苹果开发者团队 ID |
keyID | 对 | 用 Apple 私钥标识符号 |
key | 对* | 私钥的内容 。 提供其中之一 key 或者说 keyFilePath |
keyFilePath | 对* | 私密密钥文件的路径; 装入 fs.readFileSync |
callbackURL | 没有 | OAuth 重定向 URI; 必须匹配在 Apple 注册的返回 URL |
scope | 没有 | 瞄准镜阵等. ['email', 'name'] |
state | 没有 | 启用 OAuth state 生成和验证以防范CSRF:在重定向到苹果公司之前将随机值存储在会话中,并与苹果公司帖子回放的值进行比较. 大力推荐出产. 要求数 express-session 或兼容会话中间软件。 默认为 false |
store | 没有 | 自定义状态存储执行 store(req, callback) 财务报告和审定财务报表 verify(req, state, callback),用于没有会话中间软件的部署 |
sessionKey | 没有 | 存储状态的会话密钥; 默认为 apple:<hostname> |
passReqToCallback | 没有 | 通过请求,作为第一个论据: validate; 默认为 false |
clientSecretExpiry | 没有 | 生成客户端秘密JWT的寿命;默认为 '5 minutes'。密钥在到期前自动被缓存和重生成 |
authorizationURL | 没有 | 默认为 https://appleid.apple.com/auth/authorize |
tokenURL | 没有 | 默认为 https://appleid.apple.com/auth/token |
revokeURL | 没有 | 默认为 https://appleid.apple.com/auth/revoke |
jwksURL | 没有 | JWKS 端点用于获取 Apple 的公钥; 默认为 https://appleid.apple.com/auth/keys |
验证召回
认证成功后, validate 接收 accessToken, (中文(简体) ), refreshToken,并核实 profile。返回的值变为 req.user存储 refreshToken 如果您打算验证会话或稍后取消访问.
配置对象
{
id: string; // stable Apple user identifier (the `sub` claim)
provider: 'apple';
email?: string;
emailVerified?: boolean;
isPrivateEmail?: boolean; // true when the address is a Hide My Email relay
name?: { // present only on the user's first authorization
firstName: string;
lastName: string;
};
}
重要: 总是将应用程序账户与
profile.id,而不是电子邮件地址。 苹果贴出user对象( 包含)name)仅在用户使用期间 第一个 ——在当时坚持. 因为名称来源于浏览器的形成后而非所签名的身份符号,所以在存储或显示之前验证并消毒. 其后授权不包括user对象; 身份符号通常带有电子邮件请求, 但是应用程序也应该容忍缺失的电子邮件( 有些管理好的苹果账户没有).
私人转发电子邮件地址
不通过检查电子邮件域来识别或过滤苹果中继地址; 依赖已核实的地址 isPrivateEmail 换成旗帜。 如果您的电子邮件验证或发送基础设施按域过滤, 它必须接受 Apple 的所有中继域 : private.icloud.com (用于2026年年中产生的地址)以及遗产 privaterelay.appleid.com 财务报告和审定财务报表 icloud.com。 。 。 .
超出登录范围
战略实例将剩余Sign与苹果的生命周期操作相接而来. 由于这一策略是雀巢JS的提供商,可以在需要这些操作的地方注入.
正在验证本地 App 的身份
当您的后端也使用 iOS/macOS 应用程序服务时 ASAuthorizationAppleIDCredential,该应用程序直接提交身份标志。 根据苹果的公用钥匙校验:
const claims = await appleStrategy.verifyIdToken(identityToken, {
audience: 'com.example.bundleid', // the app's bundle ID, from server-side configuration
nonce: expectedNonce, // the nonce your server issued for this sign-in attempt
});
// claims.sub is the stable Apple user identifier
audience默认为配置clientID; 当为不同的客户端发布符号时, 如应用的捆绑ID , 将它覆盖 .- 当a
nonce提供, 核查失败, 除非信使的非ce 索赔完全匹配。 苹果在使用非ce时需要非ce验证,作为防止回放攻击的保护.
警卫: 预期
audience财务报告和审定财务报表nonce必须是来自信任的服务器侧配置或服务器侧会话状态。 绝不接受来自未认证请求的任一值.
验证刷新键并获取新键
const tokens = await appleStrategy.refreshAccessToken(refreshToken);
// tokens: { access_token, token_type, expires_in, id_token }
苹果公司建议验证一个存储的刷新令牌,每天不超过一次;更频繁的验证有节奏的风险. 一个 invalid_grant 错误指授权代码或刷新令牌无效,过期,已消耗,被撤销,或与所配置的客户端不符——在任何情况下,应用程序都应要求用户再次认证.
撤销托肯( 账户删除)
App Store Review 准则 5.1.1(v) 要求提供账户创建的应用程序也提供账户删除,包括撤销带有 Apple 令牌的签名:
await appleStrategy.revokeToken(refreshToken); // token_type_hint defaults to 'refresh_token'
// or
await appleStrategy.revokeToken(accessToken, 'access_token');
一个成功的呼叫在苹果的授权下撤销了该令牌及其相關的Sign,或确认该令牌已经无效——苹果在两种情况下都返回了HTTP 200.
说明:撤销苹果令牌不会删除应用程序账户或终止应用程序自己的会话. 被撤销后,该应用程序仍然负责删除适用的用户数据,去掉所储存的令牌,清除本地会话和饼干,并将客户端还原为未经认证的状态.
处理服务器对服务器的通知
当用户更改与您应用程序的关系时,苹果可以通知您的服务器. 在 Apple 开发者门户网站中注册一个通知端点 URL; 然后, Apple 将签名的事件作为 HTTP POST 发送, 并带有表单的 JSON 机构 { "payload": "<signed JWS>" }在对事件采取行动之前验证签名 :
@Post('apple/notifications')
async appleNotifications(@Body() body: { payload: string }) {
const notification = await this.appleStrategy.verifyServerNotification(body.payload);
switch (notification.events.type) {
case 'email-enabled': // user enabled email forwarding
case 'email-disabled': // user disabled email forwarding
break;
case 'consent-revoked': // user revoked authorization — end their sessions
break;
case 'account-deleted': // user deleted their Apple Account — remove associated data
break;
}
}
verifyServerNotification 验证 JWS 签名对 Apple 的公钥, 以及发行人和受众, 并将权利主张与 events 已分析有效载荷。 图书馆只进行核查;决定如何就每个事件采取行动仍然是应用程序的责任。 核查失败——将这类请求视为伪造和拒绝.
重放保护: 经核实的通知载有:
jti(独特的通知ID). 因为抓获的快递可以重新播放到您的端点,同时仍留在其有效性窗口内,所以继续处理jti值(数据库、Redis等),并在对事件采取行动前拒绝重复:const accepted = await this.notificationStore.claim(notification.jti); if (!accepted) { throw new ConflictException('Notification already processed'); }
类型脚本: 请求打字
战略的申报类型完全自成一体——不 @types/express 或者说 @types/passport-strategy 所需经费。 请求类型是通用的,所以 passReqToCallback 和自定义状态存储工作
import type { Request } from 'express'; // or FastifyRequest from 'fastify'
export class AppleStrategy extends PassportStrategy(Strategy<Request>, 'apple') {
constructor() {
super({ /* options */, passReqToCallback: true });
}
validate(req: Request, accessToken: string, refreshToken: string, profile: Profile) {
return { id: profile.id };
}
}
测试
在Node.js本土测试跑道上建造的拥有全面自动化测试套件的寄存器船——不需要额外的测试依赖性:
npm test
套房包括两个层面:
- 单位测试 (单位:千美元)
test/strategy.test.js: 构造器验证,授权重定向参数, Apple 错误处理,剖析配置要求(包括字符串和布尔格式)email_verified财务报告和审定财务报表is_private_email),撤销并刷新请求格式,客户端秘密生成,过期驱动再生,每个客户端秘密缓存,以及状态商店行为(基于会场的验证,错配拒绝,以及自定义商店). - 融合测试 (单位:千美元)
test/integration.test.js:针对苹果的指代和JWKS端点的本地模拟被执行的完整认证流有真实的RS256签名的身份指代,以及负面案例——错误的受众,未知的签名密钥,过期的指代,非ce不匹配,以及伪造的服务器通知.
教学
包括认证、吊销和Apple使用寿命周期的完整标志:
Apple 登录 NestJS 于 2026–2027 : 认证、托肯撤销和更多完整指南
苹果开发者门户网站设置的原始行走通道也可用:
财务问题
我如何在 NestJS 执行 Apple 登录?
安装此软件包, 注册显示于 战略设置 作为雀巢JS的供应商, 守护你的路线 AuthGuard('apple')编辑 完整指南 走遍了每个步骤,包括苹果开发者门户配置.
我如何从我的NestJS后端的iOS应用程序中验证出苹果的身份代号??
使用 verifyIdToken 你的应用软件的捆绑 ID作为观众。 它验证了针对Apple的JWKS的RS256签名,加上发行者,到期,和(可选择的)非ce.
我如何撤销与苹果公司签入的签名 以删除账户?
打电话 revokeToken 与用户存储的刷新符号。 这满足了App Store Review 准则 5.1.1 (v),该准则要求提供账户创建的应用程序在账户删除时也撤销了苹果的令牌.
首次Apple登录后为何用户名缺失??
苹果发送 name (和 user 对象)仅在用户的 第一个 授权。 坚持在第一个登录上——看 配置对象。 。 。 .
这是否与新的 private.icloud.com 隐藏我的电子邮件地址吗 ?
对 战略暴露了一个经过验证的 isPrivateEmail 旗子,所以你不需要自己匹配接力域——看 私人转发电子邮件地址。 。 。 .
当用户取消访问或删除其苹果账户时, 能否通知我的服务器 ?
是 – 向苹果公司注册一个通知端点,并通过 verifyServerNotification。 。 。 .
许可证
许可证根据 麻省理工学院。 。 。 .
关键词:与Apple,Apple登录NestJS,NestJS Apple OAuth,护照-apple,Apple OAuth2策略,苹果标志,苹果ID认证,验证苹果身份令牌,苹果令牌撤销,苹果服务器对服务器通知,隐藏我的电子邮件,NestJS认证,Passport.js Apple策略