Improve youth mode action token flow

This commit is contained in:
richjohnrambo
2026-09-02 11:25:25 +08:00
parent 7f6995d8fe
commit 79947a2c6f
5 changed files with 387 additions and 13 deletions
@@ -0,0 +1,344 @@
# IM 青少年模式前后端对接文档
## 1. 基本约定
青少年模式相关接口均为登录后接口。客户端需要携带用户登录 token、普通 API Key 和协议版本头。
公共 Header:
```http
Authorization: Bearer <access_token>
X-MeetingIM-Auth: Bearer <access_token>
X-MeetingIM-APIKey: <api_key>
X-IM-Schema: 1
X-Request-ID: <uuid>
X-Tenant-Code: default
```
所有写接口还需要携带:
```http
Idempotency-Key: <uuid>
```
测试环境验证码固定为 `123456`。`action_token` 为 5 分钟有效、单次消费,只能用于指定动作,不能在多个接口之间重复使用。
## 2. 开启青少年模式
### 2.1 查询当前状态
```http
GET /v1/im/settings/youth-mode
```
成功响应:
```json
{
"code": 0,
"data": {
"enabled": false,
"policy_version": 1,
"restrictions": ["night_quiet", "content_filter", "payment_guard"],
"password_set": false,
"can_enable": false,
"updated_at": "2026-08-20T09:00:00Z"
}
}
```
前端使用 `policy_version` 和 `restrictions` 渲染开启确认页。最终开启时需要提交用户确认过的 `policy_version`。
### 2.2 发起短信身份验证
```http
POST /v1/im/security/challenges
```
请求体:
```json
{
"scene": "set_independent_password",
"channel": "sms"
}
```
成功响应:
```json
{
"code": 0,
"data": {
"challenge_id": "sec_chg_1",
"phone_mask": "+861****000",
"expires_in_seconds": 300,
"resend_after_seconds": 60
}
}
```
手机号由服务端从当前登录用户读取,客户端不提交目标手机号。
### 2.3 校验短信验证码
```http
POST /v1/im/security/challenges/{challenge_id}/verify
```
请求体:
```json
{
"code": "123456"
}
```
成功响应:
```json
{
"code": 0,
"data": {
"action": "set_independent_password",
"action_token": "set_password_token",
"expires_at": "2026-08-20T09:05:00Z"
}
}
```
这里拿到的 `action_token` 只用于设置独立密码,不能直接用于开启青少年模式。
### 2.4 设置独立密码
```http
POST /v1/im/settings/independent-password
```
请求体:
```json
{
"password": "6439",
"purpose": "youth_mode",
"action_token": "set_password_token"
}
```
成功响应:
```json
{
"code": 0,
"data": {
"status": "ok",
"password_set": true,
"purpose": "youth_mode",
"enable_action_token": "enable_token",
"enable_action_expires_at": "2026-08-20T09:05:00Z",
"next_action": "enable_youth_mode"
}
}
```
服务端会消费 `set_password_token`,并返回新的 `enable_action_token`。前端下一步必须使用 `enable_action_token`,不要复用 `set_password_token`。
独立密码规则:
- 必须是 4 位数字。
- 不能是明显连续或重复组合。
- 服务端只保存强哈希,不返回明文密码。
- 前端离开页面后应清空输入内容,不落普通存储。
### 2.5 开启青少年模式
```http
POST /v1/im/settings/youth-mode/enable
```
请求体:
```json
{
"policy_version": 1,
"action_token": "enable_token"
}
```
成功响应:
```json
{
"code": 0,
"data": {
"enabled": true,
"policy_version": 1,
"password_set": true,
"restrictions": ["night_quiet", "content_filter", "payment_guard"],
"updated_at": "2026-08-20T09:00:00Z"
}
}
```
开启成功后,客户端应刷新本地设置页和 `bootstrap` 缓存。服务端会持久化状态,前端不能只依赖本地标志位。
## 3. 关闭青少年模式
### 3.1 校验独立密码
```http
POST /v1/im/settings/independent-password/verify
```
请求体:
```json
{
"password": "6439",
"purpose": "youth_mode",
"action": "close_youth_mode"
}
```
成功响应:
```json
{
"code": 0,
"data": {
"action": "close_youth_mode",
"action_token": "close_token",
"expires_at": "2026-08-20T09:05:00Z"
}
}
```
### 3.2 关闭模式
```http
POST /v1/im/settings/youth-mode/disable
X-Action-Token: close_token
```
请求体:
```json
{}
```
成功响应:
```json
{
"code": 0,
"data": {
"enabled": false,
"policy_version": 1,
"password_set": true,
"updated_at": "2026-08-20T09:00:00Z"
}
}
```
关闭接口的 `action_token` 通过 Header `X-Action-Token` 传递,不放在 Body 中。
## 4. Token 使用规则
| 来源 | action | 用途 | 传递方式 | 是否单次消费 |
| --- | --- | --- | --- | --- |
| `security/challenges/{id}/verify` | `set_independent_password` | 设置独立密码 | Body `action_token` | 是 |
| `independent-password` | `enable_youth_mode` | 开启青少年模式 | Body `action_token` | 是 |
| `independent-password/verify` | `close_youth_mode` | 关闭青少年模式 | Header `X-Action-Token` | 是 |
常见错误:
- `ACTION_TOKEN_INVALID`:token 已消费、过期、action 不匹配、target 不匹配或用户不匹配。
- `CODE_INVALID`:短信验证码错误。
- `CHALLENGE_EXPIRED`:短信 challenge 已过期。
- `TOO_MANY_ATTEMPTS`:独立密码或验证码错误次数过多。
- `VERIFICATION_RATE_LIMITED`:发码过于频繁,按 `retry_after_seconds` 倒计时。
## 5. curl 示例
开启流程:
```bash
curl -sS 'https://api.118im.com/v1/im/security/challenges' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access_token>' \
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
-H 'X-MeetingIM-APIKey: <api_key>' \
-H 'X-IM-Schema: 1' \
-H 'X-Tenant-Code: default' \
-H 'X-Request-ID: <uuid>' \
-H 'Idempotency-Key: <uuid>' \
--data '{"scene":"set_independent_password","channel":"sms"}'
```
```bash
curl -sS 'https://api.118im.com/v1/im/security/challenges/<challenge_id>/verify' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access_token>' \
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
-H 'X-MeetingIM-APIKey: <api_key>' \
-H 'X-IM-Schema: 1' \
-H 'X-Tenant-Code: default' \
-H 'X-Request-ID: <uuid>' \
-H 'Idempotency-Key: <uuid>' \
--data '{"code":"123456"}'
```
```bash
curl -sS 'https://api.118im.com/v1/im/settings/independent-password' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access_token>' \
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
-H 'X-MeetingIM-APIKey: <api_key>' \
-H 'X-IM-Schema: 1' \
-H 'X-Tenant-Code: default' \
-H 'X-Request-ID: <uuid>' \
-H 'Idempotency-Key: <uuid>' \
--data '{"password":"6439","purpose":"youth_mode","action_token":"<set_password_token>"}'
```
```bash
curl -sS 'https://api.118im.com/v1/im/settings/youth-mode/enable' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access_token>' \
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
-H 'X-MeetingIM-APIKey: <api_key>' \
-H 'X-IM-Schema: 1' \
-H 'X-Tenant-Code: default' \
-H 'X-Request-ID: <uuid>' \
-H 'Idempotency-Key: <uuid>' \
--data '{"policy_version":1,"action_token":"<enable_action_token>"}'
```
关闭流程:
```bash
curl -sS 'https://api.118im.com/v1/im/settings/independent-password/verify' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access_token>' \
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
-H 'X-MeetingIM-APIKey: <api_key>' \
-H 'X-IM-Schema: 1' \
-H 'X-Tenant-Code: default' \
-H 'X-Request-ID: <uuid>' \
-H 'Idempotency-Key: <uuid>' \
--data '{"password":"6439","purpose":"youth_mode","action":"close_youth_mode"}'
```
```bash
curl -sS 'https://api.118im.com/v1/im/settings/youth-mode/disable' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access_token>' \
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
-H 'X-MeetingIM-APIKey: <api_key>' \
-H 'X-IM-Schema: 1' \
-H 'X-Tenant-Code: default' \
-H 'X-Request-ID: <uuid>' \
-H 'Idempotency-Key: <uuid>' \
-H 'X-Action-Token: <close_token>' \
--data '{}'
```