All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 41s
- apple-auth.service.ts: verifyIdentityToken 增加 NODE_ENV 检查, 生产环境缺 APPLE_BUNDLE_ID 时运行时返回 401,不再走 mock - 新增 CAPIErrorCode 语义错误码体系 (src/common/errors/) - 新增 CapiException 携带 errorCode 的 HttpException 子类 - GlobalExceptionFilter 响应自动包含 errorCode 字段 - AuthService/JwtAuthGuard/AppleAuthService 全部改用 CapiException - 新增 LoginResponseDto/RefreshResponseDto/LogoutResponseDto/UserDto - Auth controller Swagger 添加 type 参数 - 新增 docs/ios-auth-api-contract.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5.0 KiB
5.0 KiB
iOS Auth API Contract
冻结日期:2026-05-27 | 版本:1.0 | 未经评审不得修改请求/响应字段
1. 基础约定
| 项目 | 值 |
|---|---|
| Base URL(生产) | https://api.longde.cloud |
| Content-Type | application/json |
| 认证方式 | Authorization: Bearer <accessToken> |
| 成功响应格式 | { success: true, data: <T>, timestamp: "ISO8601" } |
| 错误响应格式 | { success: false, statusCode: <int>, message: "<中文>", errorCode: "<语义码>" } |
2. Token 生命周期
| Token | 有效期 | 存储位置 |
|---|---|---|
| accessToken (JWT) | 1 小时 | Keychain |
| refreshToken (opaque) | 7 天 | Keychain |
- refreshToken 是一次性的:每次
/auth/refresh成功后旧的立即吊销,返回新的 - accessToken 过期 → iOS 用 refreshToken 换新,不要重新走 Apple 登录
- refreshToken 过期 → 回到登录页
3. 接口清单
3.1 Apple 登录
POST /auth/apple
请求体:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
| identityToken | string | 是 | Apple 返回的 JWT identityToken |
| authorizationCode | string | 否 | Apple 返回的授权码(建议传) |
| nonce | string | 否 | iOS 生成的原始 nonce(未哈希) |
| fullName.givenName | string | 否 | 用户的名 |
| fullName.familyName | string | 否 | 用户的姓 |
| string | 否 | Apple 返回的邮箱 |
成功响应 data:
| 字段 | 类型 | 说明 |
|---|---|---|
| accessToken | string | JWT,含 type: "user" |
| refreshToken | string | 96 位十六进制字符串 |
| user.id | string | 用户 ID |
| user.email | string|null | 邮箱 |
| user.nickname | string|null | 昵称 |
| user.avatarUrl | string|null | 头像 URL |
| user.role | string | 角色 |
| user.status | string | 状态 |
| user.onboardingCompleted | boolean | 是否完成引导 |
错误:
| errorCode | HTTP | 说明 |
|---|---|---|
| AUTH_INVALID_APPLE_TOKEN | 401 | identityToken 无效、过期或验证失败 |
3.2 刷新 Token
POST /auth/refresh
请求体:
| 字段 | 类型 | 必需 |
|---|---|---|
| refreshToken | string | 是 |
成功响应 data: 同登录响应(新 accessToken + 新 refreshToken + user)
错误:
| errorCode | HTTP | 说明 |
|---|---|---|
| AUTH_REFRESH_TOKEN_EXPIRED | 401 | 超过 7 天未使用 |
| AUTH_REFRESH_TOKEN_REVOKED | 401 | 已被登出/安全事件撤销 |
| AUTH_USER_DISABLED | 401 | 账号被管理员禁用 |
| AUTH_USER_DELETED | 401 | 账号已注销 |
3.3 获取当前用户
GET /users/me
需要 Bearer token。
成功响应 data: 用户对象(同登录响应中的 user)
错误:
| errorCode | HTTP | 说明 |
|---|---|---|
| AUTH_UNAUTHORIZED | 401 | 未登录或 token 过期 |
| AUTH_USER_DISABLED | 401 | 账号被禁用 |
| AUTH_USER_DELETED | 401 | 账号已注销 |
| AUTH_WRONG_TOKEN_TYPE | 401 | 使用了 admin token |
3.4 登出
POST /auth/logout
需要 Bearer token。
请求体:
| 字段 | 类型 | 必需 |
|---|---|---|
| refreshToken | string | 是 |
成功响应: { success: true, message: "已退出登录" }
错误:
| errorCode | HTTP | 说明 |
|---|---|---|
| AUTH_UNAUTHORIZED | 401 | token 已过期(不影响客户端清本地状态) |
4. 完整错误码表
| errorCode | 含义 | iOS 处理策略 |
|---|---|---|
| AUTH_INVALID_APPLE_TOKEN | Apple token 验证失败 | 提示用户重试 Apple 登录 |
| AUTH_USER_DISABLED | 账号被禁用 | 清空本地 session,显示禁用提示 |
| AUTH_USER_DELETED | 账号已注销 | 清空本地 session,回到欢迎页 |
| AUTH_REFRESH_TOKEN_EXPIRED | Refresh token 过期 | 清空本地 session,回到登录页 |
| AUTH_REFRESH_TOKEN_REVOKED | Refresh token 被撤销 | 清空本地 session,回到登录页 |
| AUTH_UNAUTHORIZED | 未认证 | 尝试 refresh,失败则回登录页 |
| AUTH_WRONG_TOKEN_TYPE | Token 类型错误 | 清空本地 session,重新登录 |
| AUTH_DEV_LOGIN_FORBIDDEN | 生产环境禁用 dev 登录 | 不触发(仅 iOS 不关心) |
| VALIDATION_ERROR | 请求参数校验失败 | 检查发送的字段 |
| NOT_FOUND | 资源未找到 | 提示用户 |
| FORBIDDEN | 权限不足 | 提示用户 |
| RATE_LIMITED | 请求过快 | 稍后重试 |
| INTERNAL_ERROR | 服务器错误 | 提示用户稍后重试 |
5. iOS 实现要点
- Token 存储 — 用 Keychain(不是 UserDefaults)
- 401 自动刷新 — APIClient 拦截 401,用 refreshToken 换新 accessToken,失败则清 session
- 并发刷新 — 多个请求同时 401 时只发一次 refresh
- AppSession 状态 — 维护状态机:
unauthenticated → authenticating → authenticated → refreshing/expired/disabled/deleted - Apple 登录 nonce — 用
SecRandomCopyBytes生成,SHA256 后传给 Apple,原始值传给后端 - authorizationCode — 提取并传给后端