docs: sync IM integration guide with current behavior
This commit is contained in:
+18
-15
@@ -135,6 +135,7 @@ WebSocket Upgrade 阶段由 `server/hdl_websock.go` 先调用 `checkAPIKey`。
|
||||
```text
|
||||
读取客户端预置的 host 和普通 API Key
|
||||
-> 用户输入/客户端读取 tenant_code
|
||||
-> 可选:POST /v1/im/tenants/resolve 校验企业码并取得公开配置
|
||||
-> 带 API Key 完成 WebSocket Upgrade
|
||||
-> hi(tenant_code)
|
||||
-> 收到 hi 的 201 ctrl,确认 tenant 与服务端限制
|
||||
@@ -146,7 +147,7 @@ WebSocket Upgrade 阶段由 `server/hdl_websock.go` 先调用 `checkAPIKey`。
|
||||
-> 进入可发送状态
|
||||
```
|
||||
|
||||
当前服务端没有 `/v1/im/tenants/resolve`,因此现阶段企业码是否有效是在第一帧 `{hi}` 中校验的。只有 WebSocket Upgrade 返回 `101 Switching Protocols` 后才能发送下面的 JSON 文本帧。
|
||||
当前服务端已提供 `/v1/im/tenants/resolve`,用于进入企业前校验企业码并返回公开配置;但它不能代替 WebSocket 第一帧 `{hi}`。即使 HTTP 解析成功,WebSocket Upgrade 返回 `101 Switching Protocols` 后仍必须先发送下面的 `{hi}` JSON 文本帧,并使用同一个 `tenant_code` 绑定当前 Session。
|
||||
|
||||
`hi` 请求:
|
||||
|
||||
@@ -249,9 +250,9 @@ X-Tinode-APIKey: <预置的普通 API Key>
|
||||
|
||||
`sid` 无效或过期时服务端返回 HTTP 403 `invalid or expired session`,客户端必须重新创建 Long Polling Session 并从 `hi` 开始,不能只重发 `login`。Long Polling 是兼容方案,移动端和 Web 端正常网络条件下优先使用 WebSocket。
|
||||
|
||||
#### 0.5.3 目标产品流程
|
||||
#### 0.5.3 企业解析与产品流程
|
||||
|
||||
第 1.1 节规划的 `POST /v1/im/tenants/resolve` 属于新增接口。实现后目标流程调整为:
|
||||
第 1.1 节的 `POST /v1/im/tenants/resolve` 已实现,推荐产品入口流程为:
|
||||
|
||||
```text
|
||||
读取预置 API Key
|
||||
@@ -261,7 +262,7 @@ X-Tinode-APIKey: <预置的普通 API Key>
|
||||
-> login -> sub me -> 恢复会话
|
||||
```
|
||||
|
||||
企业解析接口只改善入口、服务发现和品牌配置,不能代替 `{hi}` 的租户绑定。即使 HTTP 解析成功,WebSocket 第一帧仍必须带相同 `tenant_code`。
|
||||
企业解析接口只改善入口、服务发现和品牌配置,不能代替 `{hi}` 的租户绑定。即使 HTTP 解析成功,WebSocket 第一帧仍必须带相同 `tenant_code`。如果解析响应中的 `endpoints` 不符合当前发布环境,客户端应优先使用内置的公网环境配置,并上报脱敏诊断。
|
||||
|
||||
### 0.6 Topic 与产品消息
|
||||
|
||||
@@ -304,19 +305,19 @@ X-Tinode-APIKey: <预置的普通 API Key>
|
||||
|
||||
<img src="images/企业码登录.png" alt="企业码登录" width="360">
|
||||
|
||||
**状态:新增企业解析接口;WebSocket 租户握手已实现。**
|
||||
**状态:企业解析接口与 WebSocket 租户握手均已实现。**
|
||||
|
||||
**当前系统的执行方式:**
|
||||
|
||||
- 前端动作:输入企业码并点击“进入企业”。只去掉首尾空格并校验非空、最长 64 字符;当前服务端没有自动转小写,客户端不得擅自改变用户输入的大小写。
|
||||
- 客户端从构建/环境配置读取 `host` 和普通 API Key,使用 `wss://{host}/{api_path}v0/channels?apikey={api_key}` 建立 WebSocket。
|
||||
- 客户端从构建/环境配置读取 `host` 和普通 API Key,可先调用 `POST /v1/im/tenants/resolve` 获取企业公开信息,再使用 `wss://{host}/{api_path}v0/channels?apikey={api_key}` 建立 WebSocket。
|
||||
- Upgrade 成功后第一帧发送 `{"hi":{"id":"hi-1","ver":"0.25","ua":"android/1.0.0","platf":"android","lang":"zh-CN","tenant":"用户输入的企业码"}}`。
|
||||
- 服务端通过 `im_tenant.code` 查询企业;成功返回 `{ctrl code:201}` 和 `params.tenant{code,name}`,同时固定 Session 的内部 `tenant_id`。客户端保存服务端返回的标准 `tenant.code/name`,再进入 1.2 登录。
|
||||
- 当前失败表现:企业码缺失/超过 64 字符返回 400,企业不存在按存储错误返回,企业停用返回 403。当前还没有统一 `TENANT_UNAVAILABLE` 产品错误码。
|
||||
|
||||
**目标产品新增方式:**
|
||||
**企业解析接口:**
|
||||
|
||||
- 新增 `POST /v1/im/tenants/resolve`,未登录但必须带 `X-Tinode-APIKey`,Header 传 `X-Tenant-Code: acme`;Body 为 `{"tenant_code":"acme","platform":"android","app_version":"1.0.0"}`。
|
||||
- `POST /v1/im/tenants/resolve` 未登录可调用,但必须带 `X-Tinode-APIKey` 和 `X-IM-Schema: 1`;Header 传 `X-Tenant-Code: acme`,Body 可传 `{"tenant_code":"acme","platform":"android","app_version":"1.0.0"}`。
|
||||
- 成功数据:`tenant{code,name,tenant_desc,branding}`、`endpoints{websocket,api,file}`、`auth_methods`、`password_policy`、`app_update`。不得返回 `tenant_id` 或 `tenant_ticket`。
|
||||
- 后续:保存公开企业信息,连接响应给出的 WebSocket 地址,仍然发送 0.5 节的 `hi(tenant)` 做最终租户绑定;`hi` 成功后才进入 1.2。
|
||||
- 目标失败协议:企业不存在、停用或不允许登录统一返回 `TENANT_UNAVAILABLE`;客户端不得区分不存在和停用。网络失败保留输入并允许重试。
|
||||
@@ -346,12 +347,13 @@ X-Tinode-APIKey: <预置的普通 API Key>
|
||||
|
||||
<img src="images/登录页面.png" alt="登录页面" width="360">
|
||||
|
||||
**状态:基本账号密码登录已实现;协议勾选和安全 Token 生命周期需改造。**
|
||||
**状态:基本账号密码登录已实现;WS Token 可复用于产品 HTTP。**
|
||||
|
||||
- 前端动作:账号、密码非空且已勾选协议后启用登录;密码仅驻留当前页面内存。
|
||||
- 协议:已完成 `hi` 的 WebSocket 发送 `{login scheme:"basic"}`,`secret=base64(username:password)`;不得先登录再传企业码。
|
||||
- 成功:读取 `{ctrl params.user/token/expires/authlvl}`,Token 存 Keychain/Keystore;立即 `sub me`,再加载会话。前端不解码 Token。
|
||||
- 待改造:登录成功后签发短期 access token 与可轮换 refresh token;Token 绑定 `tenant_id/user_id/auth_version/device_id`。修改密码、封禁账号后旧 Token 失效。
|
||||
- 成功:读取 `{ctrl params.user/token/expires/authlvl}`,Token 存 Keychain/Keystore;立即 `sub me`,再加载会话。前端不解码 Token,也不再次 Base64 编码。
|
||||
- 产品 HTTP:使用同一个 `params.token` 作为 `Authorization: Bearer <params.token>`;所有 `/v1/im/*` HTTP 请求仍必须带 `X-Tinode-APIKey`、`X-IM-Schema: 1`,写请求再带 `Idempotency-Key`。
|
||||
- 待改造:后续可演进为独立短期 access token 与可轮换 refresh token;当前版本先复用 Tinode 登录 Token,并已绑定 `tenant_id/user_id/auth_version`。修改密码、封禁账号后旧 Token 应失效。
|
||||
- 错误:`AUTH_FAILED` 统一显示账号或密码错误;`TENANT_MISMATCH` 清 Token 并回企业码页;`ACCOUNT_DISABLED`、`REALNAME_REQUIRED`、`YOUTH_RESTRICTED` 按专用页面处理。
|
||||
|
||||
登录成功示例:
|
||||
@@ -389,17 +391,17 @@ X-Tinode-APIKey: <预置的普通 API Key>
|
||||
|
||||
<img src="images/注册页面.png" alt="注册页面" width="360">
|
||||
|
||||
**状态:Tinode `{acc}` 基础注册已实现;短信预校验与租户配额需改造。**
|
||||
**状态:Tinode `{acc}` 基础注册和验证码预校验已实现;短信供应商接入前固定测试码为 `123456`。**
|
||||
|
||||
- 获取验证码:调用 `POST /v1/im/auth/verification-codes`,Body `{"scene":"register","phone":"138****0000"}`;成功返回 `challenge_id`、`expires_in_seconds`、`resend_after_seconds`。
|
||||
- 获取验证码:调用 `POST /v1/im/auth/verification-codes`,必须带 `X-Tinode-APIKey`、`X-Tenant-Code`、`X-IM-Schema: 1`,Body `{"scene":"register","phone":"+86138..."}`;成功返回 `challenge_id`、`expires_in_seconds`、`resend_after_seconds`。
|
||||
- 提交注册:前端校验密码一致和协议后,先调用 `POST /v1/im/auth/verification-codes/{challenge_id}/verify`,Body `{"code":"123456"}`,获得一次性 `verification_token`。
|
||||
- WebSocket:把一次性验证结果放在 `{acc}.cred`,不得写入 `desc.private`,也不得在 `{acc}` 顶层传 `tags`。服务端会根据已校验的 `cred` 建立手机号凭证;客户端主动设置 `tags:["tel:..."]` 会被 Tinode 权限策略拒绝并返回 `403 params.what=tags`。
|
||||
- WebSocket:已完成同一企业 `hi` 后发送 `{acc}`。把一次性验证结果放在 `{acc}.cred[0].resp`,它必须是 HTTP verify 返回的 `verification_token`,不是短信验证码 `123456`。不得写入 `desc.private`,也不得在 `{acc}` 顶层传 `tags`。服务端会根据已校验的 `cred` 建立手机号凭证;客户端主动设置 `tags:["tel:..."]` 会被 Tinode 权限策略拒绝并返回 `403 params.what=tags`。
|
||||
- 手机号格式:HTTP 发码、验证码校验、`{acc}.secret` 中的 username、`cred.val` 必须使用同一个规范化号码,推荐 E.164 格式如 `+86138...`;不要一处使用 `+86138...`、另一处使用 `86138...`。
|
||||
|
||||
```json
|
||||
{"acc":{"id":"acc-1","user":"new","scheme":"basic","secret":"base64(+86138...:password)","login":true,"cred":[{"meth":"tel","val":"+86138...","resp":"verification_token","params":{"challenge_id":"challenge-id"}}],"desc":{"public":{"fn":"用户昵称"}}}}
|
||||
```
|
||||
- 成功:返回新 `user` 和登录 Token,自动 `sub me`。创建用户前原子校验 `im_tenant_config.max_user_count`,超限返回 `TENANT_USER_QUOTA_EXCEEDED`。
|
||||
- 成功:返回新 `user` 和登录 Token;客户端保存 `params.token`,随后执行 `sub me` 和会话恢复。创建用户前原子校验 `im_tenant_config.max_users`,超限返回 `TENANT_USER_QUOTA_EXCEEDED`。
|
||||
- 安全:手机号在租户内唯一;验证码只保存摘要、单次消费;密码按服务端策略校验并使用强哈希保存。
|
||||
|
||||
### 1.5 注册页面-倒计时.png
|
||||
@@ -856,6 +858,7 @@ X-Tinode-APIKey: <预置的普通 API Key>
|
||||
**状态:新增产品通讯录接口。**
|
||||
|
||||
- 好友列表:`GET /v1/im/friends?query=&initial=&cursor=`,返回 `user_id,display_name,remark,avatar_file_id,initial,online_visibility`;显示名优先级为备注名、昵称、账号。
|
||||
- 分页:`next_cursor` 当前按最后一条好友的 `peer_id` 生成,客户端必须作为不透明字符串原样回传,不要自行解析、排序或拼接。
|
||||
- 组织成员:`GET /v1/im/organization/members?query=&department_id=&cursor=`;好友和组织成员是两种关系,前端不得混为一个可发消息权限。
|
||||
- 新朋友未处理数、群列表、文件助手和系统消息入口由 `GET /v1/im/bootstrap` 返回。列表缓存以租户和用户隔离。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user