@xgent-ai/skills 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,11 +2,11 @@
2
2
  "schemaVersion": 1,
3
3
  "distribution": "release",
4
4
  "toolVersion": "0.1.0",
5
- "npmPackageVersion": "0.7.2",
6
- "sourceCommit": "cbf2002ab1f712ef3909e07baf5487a9c097b43c",
7
- "sourceTree": "eff0aee2cace6a8f4d366ff4cb69f92bc79b84c6",
5
+ "npmPackageVersion": "0.7.3",
6
+ "sourceCommit": "2d0c0a2b1f299ae3c67d1e20ed0647fb443ef2c5",
7
+ "sourceTree": "ca1a69cd0a3e75401ac3ff141203eebd3dc0f3d1",
8
8
  "sourceState": "dirty",
9
- "sourceInputSha256": "a8af549cde900838d9b226cbd30cd0b66aa041412a3584d821801b21eb16e57d",
9
+ "sourceInputSha256": "78f9a468d3385300cee2df3f29d3188e927424da76c81045d169ff1916019552",
10
10
  "bundleSchema": 1,
11
11
  "bundleSha256": "26ac1c512c9a8ea78902639260cefcb1fb7e05a2deb9f2060757e58be5b6adcb",
12
12
  "reviewSchema": 1,
@@ -15,25 +15,25 @@
15
15
  "asset": "phoenix-ui-darwin-arm64",
16
16
  "sha256": "f3f634a18c6acbf446702394623a8d1e569a42d62dbe6d558174319cc8aa2cab",
17
17
  "size": 18284080,
18
- "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.2/darwin-arm64/phoenix-ui-darwin-arm64"
18
+ "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.3/darwin-arm64/phoenix-ui-darwin-arm64"
19
19
  },
20
20
  "darwin-x64": {
21
21
  "asset": "phoenix-ui-darwin-x64",
22
22
  "sha256": "108b5e02edf8a66723dcd82c00d9ab7cedfbf9c15e4de3ea038ba11d7a798cde",
23
23
  "size": 20772536,
24
- "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.2/darwin-x64/phoenix-ui-darwin-x64"
24
+ "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.3/darwin-x64/phoenix-ui-darwin-x64"
25
25
  },
26
26
  "linux-x64": {
27
27
  "asset": "phoenix-ui-linux-x64",
28
28
  "sha256": "425c300353779498c1954af4c5f6cb87c8f8ab70a55a69dca86e20668ad4a1ce",
29
29
  "size": 22855792,
30
- "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.2/linux-x64/phoenix-ui-linux-x64"
30
+ "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.3/linux-x64/phoenix-ui-linux-x64"
31
31
  },
32
32
  "linux-arm64": {
33
33
  "asset": "phoenix-ui-linux-arm64",
34
34
  "sha256": "fe61b42262cb9409849342e5ea3116c17c6054e13da5c1425580ed73ff242eca",
35
35
  "size": 19702656,
36
- "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.2/linux-arm64/phoenix-ui-linux-arm64"
36
+ "url": "https://download.xgent.ai/vendor/phoenix-ui/v0.7.3/linux-arm64/phoenix-ui-linux-arm64"
37
37
  }
38
38
  }
39
39
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: portal-external-app
3
- description: '用于把独立仓库、以 Docker 镜像交付的外部服务接入 XGENT 门户或评审交付物。覆盖对接契约、manifest、注册、鉴权、原生客户端登录、/svc 路由、健康检查与一盒联调。'
3
+ description: '用于把独立仓库、以 Docker 镜像交付的外部服务接入 XGENT 门户或评审交付物。覆盖 manifest、注册、鉴权、成员客户端与终端用户身份、/svc 路由、健康检查与一盒联调。'
4
4
  ---
5
5
 
6
6
  # portal-external-app · 外部镜像服务类应用接入
@@ -20,7 +20,8 @@ description: '用于把独立仓库、以 Docker 镜像交付的外部服务接
20
20
  | 匿名页、后端直出 HTML、公开站点/独立域名、旧链接或 listingKey 迁移 | [references/public-entrypoints.md](references/public-entrypoints.md):现有 /apps、/svc、Sites 三种通路及各自责任 |
21
21
  | 将本 App 的图片/视频/PDF 发布为不过期、可吊销的固定内容 URL | [references/files-stable-links.md](references/files-stable-links.md):特权 scope 申请、服务态签发、轮询、公开读取与生命周期 |
22
22
  | 服务账号写进应用空间的文件,要让作者本人在文件管理里看得到 | [references/files-member-docs.md](references/files-member-docs.md):`onBehalfOf` 署名、成员只读可见、成员内容隐私开关 |
23
- | 原生客户端(iOS/Android/桌面)登录与刷新、设备凭证、停用后失效时效、服务端成员查询与事件 | [references/native-clients.md](references/native-clients.md) |
23
+ | 门户成员的原生客户端(iOS/Android/桌面)登录与刷新、设备凭证、停用后失效时效、服务端成员查询与事件 | [references/native-clients.md](references/native-clients.md) |
24
+ | 顾客/学生等终端用户的完整能力、租户用户中心、托管注册登录、账号与隐私治理 | [references/end-users.md](references/end-users.md):先区分成员与终端身份,再按任务读认证协议或生命周期接口 |
24
25
  | 用户以 CLI、MCP、个人自动化调用 App,或接入门户多服务用户密钥与服务发现 | [references/cli-user-keys.md](references/cli-user-keys.md):受审 `cliAccess` 声明、标准绑定与通用目录;不改门户业务代码 |
25
26
  | 为外部服务写/审对接契约文档 | [references/contract-doc-template.md](references/contract-doc-template.md) |
26
27
  | 有「每租户最多几个 X」的配额诉求(选模型、数值谁配、已用怎么来) | [references/quota-and-seats.md](references/quota-and-seats.md) |
@@ -36,6 +37,7 @@ description: '用于把独立仓库、以 Docker 镜像交付的外部服务接
36
37
  租户永远勾不到(症状:功能上线了、租户面却没有任何可选项)。判断口径与两条路的差别见
37
38
  [integration-contract.md §3.1](references/integration-contract.md)。
38
39
  - 它自带认证体系(API-Key/节点 Token/gRPC)?→ 按 integration-contract.md §6 划界:门户面必走 TDT 四道闸;自有节点面保持原认证但**不得**进 `/svc`(`/svc` 只转发 HTTP :8080)。
40
+ - 用户是租户旗下网站/App 的顾客、学生等终端用户?→ 先读 [终端身份](references/end-users.md)。它是 `kind=enduser`,不使用门户成员的 `/oauth/*`、成员角色或席位;不能把普通后台路由一起开放给它。
39
41
 
40
42
  ## 交付物评审 checklist(收外部团队东西时逐项验)
41
43
 
@@ -49,6 +51,7 @@ description: '用于把独立仓库、以 Docker 镜像交付的外部服务接
49
51
  8. **配额诉求**:有没有「每租户能建多少」这类上限?有就走平台套餐(见 [references/quota-and-seats.md](references/quota-and-seats.md)),**不接受对方自建**;
50
52
  角色本身该由对方在自己的 `app.manifest.json` 里声明 `seatRoles`;`serviceScopes` 里出现 `seats.read` 仍是当场打回的信号,但对方可以在 `privilegedServiceScopes` 里**申请**它(带 reason,进「发布审核」逐条确认批准后授予);
51
53
  9. 对接契约文档:按 [references/contract-doc-template.md](references/contract-doc-template.md) 的结构与精度。**没有这份文档的接入不算完成**——新服务(如任务网关)要先补。
54
+ 10. **终端用户**:如有注册、登录、找回、密码管理、注销或个人数据请求,按 [终端用户参考](references/end-users.md) 核对身份 owner、realm、profile/client、服务权限、容量与业务数据分工;区分管理 UI、App API 和本人流程,不把成员 OAuth 当顾客认证。
52
55
 
53
56
  ## 资源服务器硬契约(外部实现最常炸的四处)
54
57
 
@@ -264,6 +267,13 @@ compose 网络** —— 一份在一盒里能跑的 descriptor,`env` 里写着
264
267
  | 用户级设置 | `settings.read` / `settings.write` | `/api/v1/settings/me` |
265
268
  | Dashboard 卡片 | `widget.write`(**用户态**,服务态令牌推不了) | `PUT /api/v1/dashboard/widgets/:key/data`;清单顶层 `dashboardWidgets` 声明(不是 `dashboard.widgets`)。有用户关心的状态就该暴露至少一个,前端用 SDK 推;完整契约见 portal-micro-app skill 的 Widget 一节 |
266
269
 
270
+ ### 终端用户身份也是门户能力,但使用专门服务身份
271
+
272
+ 注册、凭据、会话、条款与隐私接口在 `/api/v1/end-users*` 等基座路径,管理入口为「用户中心」。
273
+ 它们不走 `/svc/user-center`,也不走成员的交换流程;`endusers.*` 和 `privacy.requests.*`
274
+ 是 SERVICE_ONLY scope,按需经 `privilegedServiceScopes` 申请。终端身份不是成员通讯录,
275
+ 登录、当前限制和完整分工见 [终端用户参考](references/end-users.md)。
276
+
267
277
  ### B. App 型公共服务 —— 走 `/svc/<key>` + 令牌交换
268
278
 
269
279
  这些是**独立 App**,不是基座端点:要在清单里声明 `exchangeTargets`,用交换来的
@@ -315,6 +325,9 @@ compose 网络** —— 一份在一盒里能跑的 descriptor,`env` 里写着
315
325
  - 两个模型**二选一**,都只有 **backed listing** 可声明:`seatBased`(计门户成员用不用得了这个
316
326
  App,门户在 TDT 签发时收口,App 侧零代码)/ `seatRoles`(App 自管账号或资源,门户只发数、
317
327
  App 自己在建号路径上收口)。
328
+ - 上面是 App 席位/业务资源模型;平台托管终端身份另用租户共享的 `portal.end_users`,
329
+ 由平台在身份创建/删除时占用/归还,归档仍占额。不要拿成员席位代替或再扣一份身份容量,
330
+ 见 [终端用户容量](references/end-users.md)。
318
331
  - **role 由对方在 `app.manifest.json` 里声明**(不需要门户改代码);**各套餐的数值进不了
319
332
  manifest**,只有平台管理员在控制台能写 —— 应用侧一条写路径都没有。
320
333
  这是有意的:数值是商务面,改它等于改可售卖档位。
@@ -0,0 +1,206 @@
1
+ # 终端用户托管认证协议(enduser-auth-v1)
2
+
3
+ > 顾客身份先读 [能力与启用条件](end-users.md)。本文适用于已登记的 Web BFF / iOS 终端认证,
4
+ > 不使用成员的 `/oauth/authorize`、`/oauth/token`、`publicClient` 或 `redirectUrls`。
5
+ > `<auth-origin>` 是 profile 登记的精确 HTTPS 认证源;`<portal-origin>` 是门户 API 公开源。
6
+
7
+ ## 1. 登记与发现
8
+
9
+ 租户在用户中心维护 profile;App 后端持 `endusers.clients` 登记客户端:
10
+
11
+ ```http
12
+ POST <portal-origin>/api/v1/end-users/clients
13
+ Authorization: Bearer <服务态令牌>
14
+ Content-Type: application/json
15
+
16
+ {
17
+ "siteKey": "customers",
18
+ "clientId": "example-web",
19
+ "kind": "web_bff",
20
+ "name": "网页客户端",
21
+ "redirectUris": ["https://app.example.com/auth/callback"],
22
+ "protocolVersions": ["enduser-auth-v1"],
23
+ "status": "active",
24
+ "expectedRevision": 0
25
+ }
26
+ ```
27
+
28
+ 租户和 owner 从服务票确定;profile 须已存在。新增 `expectedRevision=0`,更新先回读提交当前
29
+ revision,冲突处理后再试;已有客户端不能改绑 profile 或 kind。成功 data 返回
30
+ `id,kind,name,redirectUris,protocolVersions,status,revision`,持久保存 revision。
31
+ `GET /api/v1/end-users/realm?siteKey=...` 可核对本空间及客户端配置,但当前不返回 revision;
32
+ 公开能力发现也不返回它。遇到后台修改引起的 CONFLICT,需由有权操作人从用户中心核验当前修订,
33
+ 不能凭空增加服务回读端点、盲猜 revision 或循环覆盖。iOS 使用 `kind:"ios"`,
34
+ 可登记反向域名形式的 scheme;Web 必须 HTTPS。回调精确匹配,拒绝通配符、userinfo、fragment、
35
+ 以及占用 `code/state/error` 的查询参数;不要套用成员的回环 HTTP 例外。
36
+
37
+ 从认证源获取:
38
+
39
+ ```text
40
+ GET <auth-origin>/api/public/end-user-auth/clients/<clientId>
41
+ ```
42
+
43
+ 成功 Envelope 的 data 包含 `protocolVersion`、`clientId`、`authorizationEndpoint`、
44
+ `tokenEndpoint`、`refreshEndpoint`、`logoutEndpoint`、`targetedTicketEndpoint`、`methods`、
45
+ `pkceMethods:["S256"]`、`responseModes:["query"]`、`codeTtlSeconds:60`、`reauthSupported`。
46
+ 未知/停用客户端、停用 profile、host 不符回 `200 {ok:true,data:null}`。
47
+ 按元数据 methods 判可用方法(password/apple);不能从协议版本推断供应商已配通,
48
+ 也不能从 reauthSupported 推断所有重新认证用途均已上线。
49
+
50
+ ## 2. 普通授权码流程
51
+
52
+ 1. BFF 为本次请求保存随机 state、PKCE verifier、应用浏览器会话、回调和期限。
53
+ 浏览器只持应用会话句柄,Portal access/refresh 与服务账号 secret 留在 BFF;
54
+ 原生端使用系统浏览器与系统安全存储,不把服务账号 secret 放进安装包。
55
+ 2. 顶层打开元数据给出的授权地址,不能嵌入 iframe:
56
+
57
+ ```text
58
+ GET <auth-origin>/api/public/end-user-auth/authorize
59
+ ?client_id=example-web
60
+ &response_type=code
61
+ &redirect_uri=<已登记的精确回调,URL编码>
62
+ &state=<随机state>
63
+ &code_challenge=<BASE64URL(SHA256(verifier))>
64
+ &code_challenge_method=S256
65
+ ```
66
+
67
+ 以上六项必须各出现一次。state 是 22–512 字符无填充 base64url,由密码学随机源生成;
68
+ verifier 为 43–128 字符 `A-Z a-z 0-9 -._~`,challenge 为 43 字符无填充 base64url。
69
+ 拒绝 plain、未知/重复参数及自报 tenant/owner/site/audience/scope/profile/subject;
70
+ authorize 不接受 lang/view,托管页自己提供语言切换。
71
+
72
+ 3. 平台创建最长 600 秒的浏览器绑定 flow,303 到同一认证源 interaction 页面。
73
+ 登录可复用同 profile 的托管浏览器会话,仍要确认账号与目标应用;可换账号或取消。
74
+ 注册走邮箱→验证码→密码及必选条款;找回走已验证邮箱验证码;
75
+ 首登须改密时完成前不给可用会话。Apple 按 provider sub 绑定,不按同邮箱自动合并账号。
76
+ 当前托管 password 表单使用邮箱;不要假定用户名/手机号标识也能直接在此表单登录。
77
+ 4. 回调成功带 code/state;取消带 `error=access_denied` 与原 state。BFF 核验本地事务、state、
78
+ 浏览器绑定和期限并单次消费,然后在服务端换码;非法回调不跳转。
79
+
80
+ ```http
81
+ POST <auth-origin>/api/public/end-user-auth/code-token
82
+ Content-Type: application/x-www-form-urlencoded
83
+
84
+ grant_type=authorization_code&client_id=example-web&redirect_uri=<同一回调>&code=<code>&code_verifier=<verifier>
85
+ ```
86
+
87
+ 换码 URL 不带查询参数,表单只收这五项,各一次。授权码最多 60 秒、单次使用。
88
+ 错误 verifier 不消费码;并发正确请求只有一个成功。丢回包后重新授权,不重放旧码领取 refresh。
89
+ 成功 data 的字段名是:
90
+
91
+ ```json
92
+ {
93
+ "protocolVersion": "enduser-auth-v1",
94
+ "endUserId": "<主体UUID>",
95
+ "ownerAppKey": "<身份owner>",
96
+ "siteKey": "customers",
97
+ "sessionId": "<设备会话UUID>",
98
+ "accessToken": "<owner访问票>",
99
+ "refreshToken": "<刷新凭据>",
100
+ "expiresIn": 900,
101
+ "mustChangePassword": false
102
+ }
103
+ ```
104
+
105
+ 这是 `{ok:true,data:...}` 内的 data,不是成员 OAuth 的 snake_case token 响应。
106
+ 授权码只能交给 code-token,不能交给下面的 token 定向票端点。
107
+ 成功或取消后转到不含 code/state 的应用 URL。
108
+
109
+ ## 3. 自省、资源鉴权与脱敏资料
110
+
111
+ BFF/资源后端用获准 `token.introspect` 的服务账号 Basic 调:
112
+
113
+ ```http
114
+ POST <portal-origin>/api/tokens/introspect
115
+ Authorization: Basic <服务账号clientId:secret的base64>
116
+ Content-Type: application/json
117
+
118
+ {"token":"<owner访问票>"}
119
+ ```
120
+
121
+ 先解 Envelope,再校验 data。owner access 必须同时满足:
122
+
123
+ - `active=true`、`kind=enduser`、`credential_type=tdt`,且 `exp` 未过期;
124
+ - `end_user_id=principal_id`,与已绑定应用会话的主体相同;`session_id` 有效;
125
+ - `tenant_id/owner_app_key/site_key` 与登记值一致;
126
+ - `aud=listingKey=azp=owner_app_key`,`scopes=[]`;
127
+ - `user_id/role/groups/permissions` 为 null,`bypass/isPlatformAdmin=false`。
128
+
129
+ HTTP 自省字段为 `end_user_id/session_id/owner_app_key/site_key`;SDK 上下文对应
130
+ `endUserId/sessionId/ownerAppKey/siteKey`。成员票、服务票即使 active 也不能替代。
131
+ files 定向票不能当 owner access 登录。仅解 JWT 或相信浏览器提交的主体都不构成认证。
132
+
133
+ TS 后端使用 `@xgent/portal-server-sdk`,仅在具名终端用户端点配置
134
+ `acceptedPrincipalKinds:["enduser"]`、`acceptedCredentialTypes:["tdt"]`,再用
135
+ `requireEndUser`、校验目标 audience、登记空间与 App 本地业务权限;SDK 缺省只开放 user/service。
136
+ 终端用户没有成员 PID/管理员角色,不能直接套用成员的四道闸或放开所有后台路由。
137
+ 自省正缓存上限 **50 秒**,还须受真实 exp 截断;端到端撤销预算 60 秒。
138
+ 敏感操作可用 `strictIntrospection:true`。合法 inactive 才是认证失效;超时、坏 JSON、
139
+ 失败信封或依赖故障拒绝为服务故障,不降级匿名。
140
+
141
+ 读取资料另用 `endusers.read` 服务 Bearer 调 `GET /api/v1/end-users/<id>`;
142
+ 普通 DTO 只有 `identifierMasked/contacts[].addressMasked` 等脱敏字段,
143
+ 不提供通用 userinfo 或明文联系方式端点。专门的本人数据导出另有授权与范围。
144
+
145
+ ## 4. 刷新、退出、定向票
146
+
147
+ | 操作 | 请求 | 语义 |
148
+ | --- | --- | --- |
149
+ | 刷新 | `POST <auth-origin>/api/public/end-user-auth/refresh`,JSON `{"refreshToken":"<当前值>"}` | 返回 accessToken、**新** refreshToken、expiresIn;同会话串行轮换并原子保存,旧值不可重放 |
150
+ | 设备退出 | 同前缀 `POST /logout`,JSON `{"refreshToken":"<当前值>"}` | 撤销当前设备会话;丢弃本地凭据,退出结果不明按应用状态处理 |
151
+ | 定向票 | 同前缀 `POST /token`,Bearer 终端用户 access,JSON `{"target":"owner"}` 或 `{"target":"files"}` | 只能选平台白名单 target;files 仅 files.read,仍需逐文件业务授权 |
152
+
153
+ access 当前为 15 分钟,refresh 为 30 天滑动并轮换;始终使用实际 expiresIn。
154
+ sessionId 只在设备会话创建时给出,刷新不改变也不再返回。应用设备退出不清认证域的托管
155
+ 浏览器会话,因此退出后再登录不能代替重新认证。管理员全部下线会连托管浏览器会话一起失效;
156
+ 单会话撤销不影响其他设备。
157
+
158
+ ## 5. 敏感操作重新认证
159
+
160
+ 持**当前 owner 设备会话**的 access(不是成员/服务/files 票或托管 Cookie)在认证源申请:
161
+
162
+ ```http
163
+ POST <auth-origin>/api/public/end-user-auth/reauth/requests
164
+ Authorization: Bearer <当前owner访问票>
165
+ Content-Type: application/json
166
+
167
+ {"client_id":"example-web","redirect_uri":"https://app.example.com/auth/callback","purpose":"confirm_action","operationKey":"example-web:export:<本次操作号>"}
168
+ ```
169
+
170
+ operationKey 使用字母、数字、`._:-`,1–128 字符;示例的尖括号占位须替换成合法操作号。
171
+ 每次尝试新号并与具体业务操作绑定。body 只收这四项,不带查询参数。
172
+ 申请回 requestId、expiresIn(≤600)、authorizationEndpoint。建立新的 state/verifier,
173
+ 按 §2 六参数额外加 `reauth_request=<requestId>`。平台锁定原主体,要求重新输入密码或完成 Apple
174
+ 重新认证,不复用 Cookie 免密。回调仍经 code-token 兑换,但 data **仅**包含:
175
+
176
+ ```json
177
+ {"reauthToken":"<不透明证明>","expiresIn":300,"purpose":"confirm_action","operationKey":"<原操作号>","endUserId":"<当前主体UUID>"}
178
+ ```
179
+
180
+ 不含 access/refresh/sessionId,不新建会话。证明 TTL 不超过实际认证时间后 300 秒及 flow 剩余期限。
181
+
182
+ - `confirm_action`:App 自己消费。可信证据是 BFF 持自己保存的 verifier 直接换码的成功响应,
183
+ 不是浏览器单独提交一个字符串。核对 purpose、主体、原发起 client/应用会话、操作号和原操作内容;
184
+ 本地 TTL 取实际剩余值与 300 秒较小值并扣网络耗时,原子单次消费;执行时再校验会话和业务权限。
185
+ Portal 没有此证明的验证端点,也不替 App 消费它;不要拿它调用 token introspect。
186
+ - `delete_passport`:只供 [删除命令](end-user-lifecycle.md) 消费,不能替代 confirm_action。
187
+ - `bind_apple/add_password/unlink_apple` 当前返回 `END_USER_AUTH_UNAVAILABLE`,不要做成可用功能。
188
+
189
+ 取消、换账号、退出或放弃操作时清理 App 本地待执行证明。操作号不承载个人资料或业务敏感内容。
190
+
191
+ ## 6. 站点认证的另一条入口
192
+
193
+ 平台 Sites 主域的 `/_xgent/auth/*` 提供 HTML/JSON 认证入口,同样属于终端身份:密码、邮箱数字验证码
194
+ `verify_contact/login/reset_password`、首登改密、退出;首次邮箱验证由所属 App 服务签发
195
+ registration-ticket,不能拿它直接换会话。站点 Cookie 只信反代注入的真实站点上下文,
196
+ 不能自报头取得;设备 JSON 密码入口使用 tenantId/ownerAppKey/siteKey 与凭据,仍检查资格。
197
+ 这些入口不是 profile 的额外登录方法,也不能把普通站点 Cookie 混作托管认证域的浏览器会话。
198
+
199
+ 已有手机 OTP 绑定/登录/恢复接口,但当前缺生产发送适配;详见 [能力限制](end-users.md)。
200
+ 已有用户名等账号的设备 JSON 密码入口为 `POST /api/public/end-user-auth/login`,
201
+ body 含 `tenantId,ownerAppKey,siteKey,identifierKind,identifier,password,deviceId?`;
202
+ 成功设备会话字段见 §2,须首改时仅回 changePasswordTicket,不能提前建立应用登录状态。
203
+ 新 Web BFF / iOS 集成优先按本文托管协议完成,不能直接套用成员登录或把站点表单当 PKCE 回调。
204
+
205
+ JSON 成功为 `200 {ok:true,data}`;业务失败为 `200 {ok:false,error}`,按 error.code 处理。
206
+ 400/401/403/429 和服务故障另按传输/鉴权语义处理;404 只表示路径不存在。