# 设备与登录 — 客户端对接说明 面向 iOS / Android / Web 等客户端 ## 变更摘要(Breaking) | 项 | 旧行为 | 新行为 | | --- | --- | --- | | 登录 Body `device_id` | 客户端生成并必填 | **删除**;由服务端根据物理指纹生成 | | 登录 Body | 无 | **`app_id` 必填**;**`device_type` 为整数 1–6**(见 meta) | | 物理标识 | 无 | **`idfv`(iOS/macOS)或 `android_id`(Android/Harmony)** 按平台必填 | | JWT | `sub` + `did` | 增加 **`aid`(app_id)**;access / refresh 均含 | | 登录响应 `data` | `device_id` | 仍为服务端 **`device_id`**,并增加 **`app_id`** | | 同一邮箱 | 同一用户 | 不变 | | 同机多 App | 各 App 各自 `device_id` | **同机共用一个 `device_id`**;会话按 **`app_id` / JWT `aid`** 区分 | ## 登录 `POST /v1/auth/login` ### 必填字段 - `email`、`verify_code` - `app_id`:例如 Spark IM 为 `1`(以 meta 枚举为准) - `device_type`:**整数枚举**(见 `GET /v1/meta/enums` → `device_type`);`1`=ios,`2`=android,`3`=harmony,`4`=windows,`5`=macos,`6`=web。可选 **`platform`** 同枚举,仅当未传 `device_type` 时作别名。 ### 物理设备字段(生成 `device_id`) | `device_type` | 必填 Body 字段 | | --- | --- | | `1`(ios)、`5`(macos) | `idfv` | | `2`(android)、`3`(harmony) | `android_id` | | `4`(windows)、`6`(web) | `idfv` 或 `android_id` 至少一个(建议 Web 用稳定 local id 映射到其一) | 可选:`device_name`(展示名)。 **不要**再传 `device_id`。指纹算法(与客户端实现一致即可): ```text payload = "{device_type}:{idfv|android_id}" fingerprint_hash = HEX(SHA256(payload)) device_id = uint64(SHA256(fingerprint_hash)[0:8]),非 0 ``` `device_type` 与 meta / DB / 指纹 **同一套数值**(ios=1, android=2, …)。 ### 响应 保存: - `access_token`、`refresh_token` - **`device_id`**(写入本地,与 JWT `did` 相同) - **`app_id`**(与 JWT `aid` 相同) - `user_id`、`spark_id` ### 示例 ```http POST /v1/auth/login HTTP/1.1 Content-Type: application/json X-Language: zh-CN X-App-Version: 1.0.0 { "email": "user@example.com", "verify_code": "123456", "app_id": 1, "device_type": 1, "idfv": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "device_name": "iPhone 15" } ``` ## JWT 解码 access / refresh token(HS256,生产由服务端签发): | claim | 含义 | | --- | --- | | `sub` | `user_id` | | `did` | `device_id`(服务端生成) | | **`aid`** | **`app_id`(当次登录 App)** | | `use` | `access` 或 `refresh` | 刷新 token 后 **`aid` 不变**(与首次登录 App 绑定)。 ## 登录后的 HTTP - **`Authorization: Bearer `**(必填) - **`X-Device-ID`**:十进制 `device_id`,须等于 JWT **`did`**(与登录响应一致) - 可选 **`X-App-ID`**:若传,须与 JWT **`aid`** 一致 - **`X-Language` / `Accept-Language`**、`X-App-Version` 仍走 Header 业务 JSON **不要**再塞 `device_id` / `app_id`(登录、刷新除外)。 ## 刷新与注销 - **`POST /v1/auth/token/refresh`**:Body 仅 `refresh_token`;新 token 对仍带相同 `did` / `aid`。 - **`POST /v1/auth/logout`**:吊销当前 **`(user, device, app)`** 会话(默认服务端配置下各 App 独立;若运维切到 `pair` 模式,同设备会互踢 refresh,见下)。 ## TCP AUTH - Body:`token`(access)、可选 **`device_id`**(须等于 JWT `did`)、`app_version`、`language` - **不必**在 AUTH Body 传 `app_id`;以 token 内 **`aid`** 为准 - 连接仍按 **`(user_id, device_id)`** 绑定;同机多 App 各连各的 token 即可 ## 同机多 App - **同一物理机 → 同一 `device_id`**(指纹不含 `app_id`) - **不同 App → 不同 JWT `aid`**,各自 login 拿各自 token - 默认(`triple`):App A 与 App B **可同时** refresh,互不踢 - 若服务端改为 `pair` 模式:后登录 App 会 **踢掉同设备上其它 App 的 refresh**(需产品公告) ## 客户端迁移清单 1. 登录 Body:加 **`app_id`**、**`idfv` / `android_id`**;去掉自研 **`device_id`** 2. 首次登录成功后 **持久化响应里的 `device_id`**,后续 Header / TCP 使用该值 3. 解析 JWT 时读取 **`aid`**,多 App 共用一套 `device_id` 存储键时应用 **`(device_id, app_id)`** 区分 token 4. 拉取 **`GET /v1/meta/enums`** 获取 `app_id`、`device_type` 等