api-server/docs/ios-auth-api-contract.md

155 lines
5.0 KiB
Markdown
Raw Normal View History

# 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** — 提取并传给后端