api-server/docs/ios-auth-api-contract.md
wangdl b9e6055400
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 41s
fix: H0-01 彻底阻断生产环境 mock + 结构化错误码 + iOS Auth 合同文档
- 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>
2026-05-27 21:03:15 +08:00

155 lines
5.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 | 否 | 用户的姓 |
| email | 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 实现要点
1. **Token 存储** — 用 Keychain不是 UserDefaults
2. **401 自动刷新** — APIClient 拦截 401用 refreshToken 换新 accessToken失败则清 session
3. **并发刷新** — 多个请求同时 401 时只发一次 refresh
4. **AppSession 状态** — 维护状态机:`unauthenticated → authenticating → authenticated → refreshing/expired/disabled/deleted`
5. **Apple 登录 nonce** — 用 `SecRandomCopyBytes` 生成SHA256 后传给 Apple原始值传给后端
6. **authorizationCode** — 提取并传给后端