@agentunion/fastaun-browser 0.4.12 → 0.4.13
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.
- package/CHANGELOG.md +27 -0
- package/_packed_docs/CHANGELOG.md +27 -0
- package/_packed_docs/sdk/09-group-rpc-manual.md +10 -12
- package/_packed_docs/sdk/09-message-rpc-manual.md +6 -7
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +98 -9
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +298 -40
- package/dist/client/delivery.d.ts +3 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +117 -7
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +8 -3
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +67 -8
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +31 -3
- package/dist/client.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,33 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.4.13 — 2026-06-09
|
|
10
|
+
|
|
11
|
+
### 新功能
|
|
12
|
+
- **发送结果回填 envelope**:`message.send` / `group.send` / `message.thought.put` / `group.thought.put` 的返回结果新增规范化 `envelope` 字段,使发送方回执与接收端信封元数据一致;新增 `APP_SEND_ENVELOPE_METHODS` 与 `sendResultEnvelope()` / `attachSendResultEnvelope()`,rpc-pipeline 在 `postprocess` 后统一附加,V2 加密路径经 `_skipSendResultEnvelope` 标志跳过明文附加、由外层 V2 协调器在加密后补挂避免重复(四语言对齐)
|
|
13
|
+
|
|
14
|
+
### 修复
|
|
15
|
+
- **refresh_token 失效自愈**:新增需重登判定(命中 `relogin_required` 或 `missing refresh_token` / `invalid_or_expired_refresh_token` / `refresh not supported`)与清缓存逻辑;刷新失败需重登时清空本地 `access_token`/`refresh_token`/`kite_token` 并持久化,client 层 token 刷新循环检测到需重登时发布 `token.refresh_exhausted` 事件(带 `relogin_required: true`)、断连触发重连,下次 `connectSession` 因无可用 token 自动走两步登录;refresh RPC 补传 `aid`/`device_id`/`slot_id`/`access_token`/`sdk_lang`/`sdk_version`,`AuthError` 携带 `data` 透传服务端响应(四语言对齐)
|
|
16
|
+
- **token 刷新日志文案修正**:修复连续刷新失败日志拼写错误(`token refreshconsecutivefailed` → 规范文案),token 循环中 `publish`/`_handleTransportDisconnect` 补齐 `await`
|
|
17
|
+
- **protected_headers 类型归一**:V2 发送方法的 envelope 元数据归一时识别 `ProtectedHeaders` 对象(经 `toObject()` 探测)并转 dict,修复传入对象(非 dict)时 protected_headers 被丢弃的问题(四语言对齐)
|
|
18
|
+
|
|
19
|
+
### 优化
|
|
20
|
+
- **app_message_envelope 字段收窄**:envelope 键收窄为可转发元数据(`from`/`to`/`group_id`/`type`/`kind`/`version`/`timestamp`/`encrypted`/`context`/`protected_headers`/`payload_type`),移除 `message_id`/`seq`/`device_id`/`slot_id` 等本地与传输层字段,不再向应用层泄漏内部字段并剔除 `_auth`(四语言对齐)
|
|
21
|
+
- **RPC 日志 token 脱敏**:短 RPC 请求/响应 debug 日志对 `access_token`/`refresh_token`/`kite_token`/`token` 及 `_token` 结尾键递归脱敏,避免明文 token 落日志;浏览器环境受 crypto 限制只输出 `<redacted len=N>`(不含 sha256,与 Node/Go 略有差异)
|
|
22
|
+
|
|
23
|
+
### 测试
|
|
24
|
+
- `refresh 业务失败且要求重登时应清掉本地 token 缓存`
|
|
25
|
+
- `connectSession 在 refresh 失败后应继续走两步登录`
|
|
26
|
+
- `应用层消息 envelope 只保留可转发字段并归一化 headers`
|
|
27
|
+
- 更新 `publishAppEvent` 与群撤回相关用例断言 envelope 收窄、不含 `message_id`/`seq`/`device_id`/`slot_id`
|
|
28
|
+
|
|
29
|
+
### 服务端协同(本版本配套)
|
|
30
|
+
- **`auth.refresh_token` 响应结构化**:新增 `relogin_required` / `retryable` / `diagnostic` / `aid` / `refresh_count` 字段,SDK 据 `relogin_required`/`retryable` 区分"重新登录"与"退避重试",不再单纯按 `error` 字符串判断
|
|
31
|
+
- **JWT 老 SDK 兼容与即时吊销**:`AUN_JWT_LEGACY_ACCESS_TOKEN_COMPAT`(默认开)放行合法 JWT 避免迁移期重连风暴;`auth.token.revoked` 事件携带 `jti`/`expires_at`,gateway 按真实 TTL 缓存吊销
|
|
32
|
+
- **V2 P2P 写入幂等**:服务端 `v2_write_peer_message`/`v2_write_peer_wrap` 捕获唯一键冲突逐字段比对,重发相同 message_id 幂等返回、不再产生 seq 空洞;自发自收跳过重复 self-sync 推送
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
9
36
|
## 0.4.12 — 2026-06-08
|
|
10
37
|
|
|
11
38
|
### 新功能
|
|
@@ -6,6 +6,33 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.4.13 — 2026-06-09
|
|
10
|
+
|
|
11
|
+
### 新功能
|
|
12
|
+
- **发送结果回填 envelope**:`message.send` / `group.send` / `message.thought.put` / `group.thought.put` 的返回结果新增规范化 `envelope` 字段,使发送方回执与接收端信封元数据一致;新增 `APP_SEND_ENVELOPE_METHODS` 与 `sendResultEnvelope()` / `attachSendResultEnvelope()`,rpc-pipeline 在 `postprocess` 后统一附加,V2 加密路径经 `_skipSendResultEnvelope` 标志跳过明文附加、由外层 V2 协调器在加密后补挂避免重复(四语言对齐)
|
|
13
|
+
|
|
14
|
+
### 修复
|
|
15
|
+
- **refresh_token 失效自愈**:新增需重登判定(命中 `relogin_required` 或 `missing refresh_token` / `invalid_or_expired_refresh_token` / `refresh not supported`)与清缓存逻辑;刷新失败需重登时清空本地 `access_token`/`refresh_token`/`kite_token` 并持久化,client 层 token 刷新循环检测到需重登时发布 `token.refresh_exhausted` 事件(带 `relogin_required: true`)、断连触发重连,下次 `connectSession` 因无可用 token 自动走两步登录;refresh RPC 补传 `aid`/`device_id`/`slot_id`/`access_token`/`sdk_lang`/`sdk_version`,`AuthError` 携带 `data` 透传服务端响应(四语言对齐)
|
|
16
|
+
- **token 刷新日志文案修正**:修复连续刷新失败日志拼写错误(`token refreshconsecutivefailed` → 规范文案),token 循环中 `publish`/`_handleTransportDisconnect` 补齐 `await`
|
|
17
|
+
- **protected_headers 类型归一**:V2 发送方法的 envelope 元数据归一时识别 `ProtectedHeaders` 对象(经 `toObject()` 探测)并转 dict,修复传入对象(非 dict)时 protected_headers 被丢弃的问题(四语言对齐)
|
|
18
|
+
|
|
19
|
+
### 优化
|
|
20
|
+
- **app_message_envelope 字段收窄**:envelope 键收窄为可转发元数据(`from`/`to`/`group_id`/`type`/`kind`/`version`/`timestamp`/`encrypted`/`context`/`protected_headers`/`payload_type`),移除 `message_id`/`seq`/`device_id`/`slot_id` 等本地与传输层字段,不再向应用层泄漏内部字段并剔除 `_auth`(四语言对齐)
|
|
21
|
+
- **RPC 日志 token 脱敏**:短 RPC 请求/响应 debug 日志对 `access_token`/`refresh_token`/`kite_token`/`token` 及 `_token` 结尾键递归脱敏,避免明文 token 落日志;浏览器环境受 crypto 限制只输出 `<redacted len=N>`(不含 sha256,与 Node/Go 略有差异)
|
|
22
|
+
|
|
23
|
+
### 测试
|
|
24
|
+
- `refresh 业务失败且要求重登时应清掉本地 token 缓存`
|
|
25
|
+
- `connectSession 在 refresh 失败后应继续走两步登录`
|
|
26
|
+
- `应用层消息 envelope 只保留可转发字段并归一化 headers`
|
|
27
|
+
- 更新 `publishAppEvent` 与群撤回相关用例断言 envelope 收窄、不含 `message_id`/`seq`/`device_id`/`slot_id`
|
|
28
|
+
|
|
29
|
+
### 服务端协同(本版本配套)
|
|
30
|
+
- **`auth.refresh_token` 响应结构化**:新增 `relogin_required` / `retryable` / `diagnostic` / `aid` / `refresh_count` 字段,SDK 据 `relogin_required`/`retryable` 区分"重新登录"与"退避重试",不再单纯按 `error` 字符串判断
|
|
31
|
+
- **JWT 老 SDK 兼容与即时吊销**:`AUN_JWT_LEGACY_ACCESS_TOKEN_COMPAT`(默认开)放行合法 JWT 避免迁移期重连风暴;`auth.token.revoked` 事件携带 `jti`/`expires_at`,gateway 按真实 TTL 缓存吊销
|
|
32
|
+
- **V2 P2P 写入幂等**:服务端 `v2_write_peer_message`/`v2_write_peer_wrap` 捕获唯一键冲突逐字段比对,重发相同 message_id 幂等返回、不再产生 seq 空洞;自发自收跳过重复 self-sync 推送
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
9
36
|
## 0.4.12 — 2026-06-08
|
|
10
37
|
|
|
11
38
|
### 新功能
|
|
@@ -2082,11 +2082,9 @@ SDK 收到后自动调用 `group.pull` 拉取最新消息并逐条解密后交
|
|
|
2082
2082
|
{
|
|
2083
2083
|
"envelope": {
|
|
2084
2084
|
"group_id": "g-abc123.agentid.pub",
|
|
2085
|
-
"
|
|
2086
|
-
"
|
|
2087
|
-
"
|
|
2088
|
-
"message_type": "group.message",
|
|
2089
|
-
"dispatch_mode": "broadcast"
|
|
2085
|
+
"from": "alice.agentid.pub",
|
|
2086
|
+
"type": "text",
|
|
2087
|
+
"timestamp": 1234567890000
|
|
2090
2088
|
},
|
|
2091
2089
|
"group_id": "g-abc123.agentid.pub",
|
|
2092
2090
|
"seq": 42,
|
|
@@ -2098,7 +2096,7 @@ SDK 收到后自动调用 `group.pull` 拉取最新消息并逐条解密后交
|
|
|
2098
2096
|
}
|
|
2099
2097
|
```
|
|
2100
2098
|
|
|
2101
|
-
SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;群消息信封字段统一放在 `envelope
|
|
2099
|
+
SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;群消息信封字段统一放在 `envelope`。`envelope` 只保留可转发的归一化元数据,`from` 由 `sender_aid` 归一化而来,`timestamp` 由 `created_at` / `t_server` 归一化而来。0.4.x 兼容期仍保留顶层 `group_id` / `seq` / `message_id` / `sender_aid` / `dispatch_mode` 等别名,下一个大版本 0.5.* 将移除这些顶层别名;请通过 `msg["envelope"]["from"]`、`msg["envelope"]["timestamp"]` 等路径访问。
|
|
2102
2100
|
|
|
2103
2101
|
### event/group.message_recalled
|
|
2104
2102
|
|
|
@@ -2109,11 +2107,11 @@ SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;群消息信
|
|
|
2109
2107
|
```json
|
|
2110
2108
|
{
|
|
2111
2109
|
"envelope": {
|
|
2112
|
-
"module_id": "group",
|
|
2113
2110
|
"group_id": "g-abc123.agentid.pub",
|
|
2114
|
-
"
|
|
2115
|
-
"
|
|
2116
|
-
"
|
|
2111
|
+
"from": "alice.agentid.pub",
|
|
2112
|
+
"type": "group.message_recalled",
|
|
2113
|
+
"kind": "group.message_recalled",
|
|
2114
|
+
"timestamp": 1234567890000
|
|
2117
2115
|
},
|
|
2118
2116
|
"module_id": "group",
|
|
2119
2117
|
"group_id": "g-abc123.agentid.pub",
|
|
@@ -2130,11 +2128,11 @@ SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;群消息信
|
|
|
2130
2128
|
}
|
|
2131
2129
|
```
|
|
2132
2130
|
|
|
2133
|
-
SDK 交付给应用层的撤回事件同样带 `envelope`。`envelope` 表示当前交付的撤回 tombstone / 通知自身信封,不是被撤回原消息的信封;业务侧被撤回的原消息列表继续使用 `message_ids` / `target_message_seqs`。0.4.x 兼容期仍保留顶层 `group_id` / `seq` / `message_id` / `sender_aid` 等别名,下一个大版本 0.5.* 将移除这些顶层别名。
|
|
2131
|
+
SDK 交付给应用层的撤回事件同样带 `envelope`。`envelope` 表示当前交付的撤回 tombstone / 通知自身信封,不是被撤回原消息的信封;业务侧被撤回的原消息列表继续使用 `message_ids` / `target_message_seqs`。`message_id` / `seq` 继续只保留在顶层兼容字段中,不进入 `envelope`。0.4.x 兼容期仍保留顶层 `group_id` / `seq` / `message_id` / `sender_aid` 等别名,下一个大版本 0.5.* 将移除这些顶层别名。
|
|
2134
2132
|
|
|
2135
2133
|
| 字段 | 类型 | 说明 |
|
|
2136
2134
|
|------|------|------|
|
|
2137
|
-
| `envelope` | object | 撤回 tombstone / 通知自身信封,包含 `
|
|
2135
|
+
| `envelope` | object | 撤回 tombstone / 通知自身信封,包含 `group_id`、`from`、`type`、`kind`、`timestamp`、`encrypted`、`context`、`protected_headers` 等存在的字段 |
|
|
2138
2136
|
| `module_id` | string | 固定 `"group"` |
|
|
2139
2137
|
| `group_id` | string | 群组 ID |
|
|
2140
2138
|
| `seq` | integer | 当前交付的撤回 tombstone / 通知 seq;在线 push 为 notice_seq,原 seq 占位 tombstone 为原消息 seq |
|
|
@@ -513,10 +513,9 @@ result = await client.call("message.ack", {"seq": 150})
|
|
|
513
513
|
```json
|
|
514
514
|
{
|
|
515
515
|
"envelope": {
|
|
516
|
-
"message_id": "uuid-1",
|
|
517
516
|
"from": "alice.agentid.pub",
|
|
518
517
|
"to": "bob.agentid.pub",
|
|
519
|
-
"
|
|
518
|
+
"type": "text",
|
|
520
519
|
"timestamp": 1234567890000,
|
|
521
520
|
"encrypted": false
|
|
522
521
|
},
|
|
@@ -531,7 +530,7 @@ result = await client.call("message.ack", {"seq": 150})
|
|
|
531
530
|
}
|
|
532
531
|
```
|
|
533
532
|
|
|
534
|
-
SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;信封字段统一放在 `envelope
|
|
533
|
+
SDK 交付给应用层的 `payload` 是明文业务 JSON 对象;信封字段统一放在 `envelope`。`envelope` 只保留可转发的归一化元数据,`from` 由 `from_aid` / `sender_aid` 归一化而来,`timestamp` 由 `created_at` / `t_server` 归一化而来。0.4.x 兼容期仍保留顶层 `message_id` / `from` / `to` / `seq` / `timestamp` 等别名,下一个大版本 0.5.* 将移除这些顶层别名;请通过 `msg["envelope"]["from"]`、`msg["envelope"]["timestamp"]` 等路径访问。
|
|
535
534
|
|
|
536
535
|
### 订阅
|
|
537
536
|
|
|
@@ -550,10 +549,10 @@ client.on("message.received", lambda msg: print(msg["payload"]))
|
|
|
550
549
|
```json
|
|
551
550
|
{
|
|
552
551
|
"envelope": {
|
|
553
|
-
"message_id": "recall-uuid",
|
|
554
552
|
"from": "alice.agentid.pub",
|
|
555
553
|
"to": "bob.agentid.pub",
|
|
556
|
-
"
|
|
554
|
+
"type": "message.recalled",
|
|
555
|
+
"kind": "message.recalled",
|
|
557
556
|
"timestamp": 1234567890000
|
|
558
557
|
},
|
|
559
558
|
"message_id": "recall-uuid",
|
|
@@ -565,11 +564,11 @@ client.on("message.received", lambda msg: print(msg["payload"]))
|
|
|
565
564
|
}
|
|
566
565
|
```
|
|
567
566
|
|
|
568
|
-
SDK 交付给应用层的撤回事件同样带 `envelope`。`envelope` 表示撤回 tombstone / 通知自身的信封,不是被撤回原消息的信封;被撤回的原消息继续通过 `message_ids`
|
|
567
|
+
SDK 交付给应用层的撤回事件同样带 `envelope`。`envelope` 表示撤回 tombstone / 通知自身的信封,不是被撤回原消息的信封;被撤回的原消息继续通过 `message_ids` 表达。`message_id` / `seq` 继续只保留在顶层兼容字段中,不进入 `envelope`。0.4.x 兼容期仍保留顶层 `message_id` / `from` / `to` / `seq` / `timestamp` 等别名,下一个大版本 0.5.* 将移除这些顶层别名。
|
|
569
568
|
|
|
570
569
|
| 字段 | 类型 | 说明 |
|
|
571
570
|
|------|------|------|
|
|
572
|
-
| `envelope` | object | 撤回 tombstone / 通知自身信封,包含 `
|
|
571
|
+
| `envelope` | object | 撤回 tombstone / 通知自身信封,包含 `from`、`to`、`type`、`kind`、`timestamp`、`encrypted`、`context`、`protected_headers` 等存在的字段 |
|
|
573
572
|
| `message_id` | string | 撤回 tombstone / 通知自身的 message_id |
|
|
574
573
|
| `tombstone_message_id` | string | 兼容别名,等同于撤回 tombstone / 通知自身的 `message_id` |
|
|
575
574
|
| `from` | string | 发送方(撤回者)AID |
|
package/dist/auth.d.ts
CHANGED
|
@@ -159,6 +159,8 @@ export declare class AuthFlow {
|
|
|
159
159
|
addTrustedRootPem(pem: string): void;
|
|
160
160
|
private _loadTrustedRoots;
|
|
161
161
|
private _rememberTokens;
|
|
162
|
+
private _refreshFailureRequiresRelogin;
|
|
163
|
+
private _clearCachedTokens;
|
|
162
164
|
/** 验证服务端返回的 new_cert,通过后正式接受 */
|
|
163
165
|
private _validateNewCert;
|
|
164
166
|
/** 获取缓存的有效 access_token */
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,cAAc,EAAkE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.js
CHANGED
|
@@ -7,6 +7,32 @@ import { ROOT_CA_PEM } from './certs/root.js';
|
|
|
7
7
|
import { VERSION as AUN_SDK_VERSION } from './version.js';
|
|
8
8
|
const _noopLog = { error: () => { }, warn: () => { }, info: () => { }, debug: () => { } };
|
|
9
9
|
const AUN_SDK_LANG = 'javascript';
|
|
10
|
+
const SENSITIVE_RPC_LOG_KEYS = new Set([
|
|
11
|
+
'access_token',
|
|
12
|
+
'refresh_token',
|
|
13
|
+
'kite_token',
|
|
14
|
+
'token',
|
|
15
|
+
]);
|
|
16
|
+
function redactRpcLogPayload(value, key = '', depth = 0) {
|
|
17
|
+
const keyLower = key.toLowerCase();
|
|
18
|
+
if (SENSITIVE_RPC_LOG_KEYS.has(keyLower) || keyLower.endsWith('_token')) {
|
|
19
|
+
const text = String(value ?? '');
|
|
20
|
+
return text ? `<redacted len=${text.length}>` : '';
|
|
21
|
+
}
|
|
22
|
+
if (depth >= 6)
|
|
23
|
+
return '<max-depth>';
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
return value.map((item) => redactRpcLogPayload(item, '', depth + 1));
|
|
26
|
+
}
|
|
27
|
+
if (value && typeof value === 'object') {
|
|
28
|
+
const out = {};
|
|
29
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
30
|
+
out[childKey] = redactRpcLogPayload(childValue, childKey, depth + 1);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
10
36
|
import { isJsonObject, } from './types.js';
|
|
11
37
|
// ── ASN.1 / PEM 工具 ────────────────────────────────────
|
|
12
38
|
/** 拆分 PEM bundle 为独立的 PEM 字符串数组 */
|
|
@@ -669,6 +695,9 @@ export class AuthFlow {
|
|
|
669
695
|
catch (e) {
|
|
670
696
|
if (!(e instanceof AuthError))
|
|
671
697
|
throw e;
|
|
698
|
+
if (this._refreshFailureRequiresRelogin(e)) {
|
|
699
|
+
await this._clearCachedTokens(identity, e.message);
|
|
700
|
+
}
|
|
672
701
|
// refresh 失败,重新登录
|
|
673
702
|
}
|
|
674
703
|
}
|
|
@@ -702,9 +731,11 @@ export class AuthFlow {
|
|
|
702
731
|
this._log.debug(`refreshCachedTokens enter: aid=${identity.aid} gateway=${gatewayUrl}`);
|
|
703
732
|
try {
|
|
704
733
|
const refreshToken = String(identity.refresh_token ?? '');
|
|
705
|
-
if (!refreshToken)
|
|
734
|
+
if (!refreshToken) {
|
|
735
|
+
await this._clearCachedTokens(identity, 'missing refresh_token');
|
|
706
736
|
throw new AuthError('missing refresh_token');
|
|
707
|
-
|
|
737
|
+
}
|
|
738
|
+
const refreshed = await this._refreshAccessToken(gatewayUrl, refreshToken, identity);
|
|
708
739
|
this._rememberTokens(identity, refreshed);
|
|
709
740
|
await this._validateNewCert(identity, gatewayUrl);
|
|
710
741
|
await this._persistIdentity(identity);
|
|
@@ -712,6 +743,9 @@ export class AuthFlow {
|
|
|
712
743
|
return identity;
|
|
713
744
|
}
|
|
714
745
|
catch (err) {
|
|
746
|
+
if (this._refreshFailureRequiresRelogin(err)) {
|
|
747
|
+
await this._clearCachedTokens(identity, err instanceof Error ? err.message : String(err));
|
|
748
|
+
}
|
|
715
749
|
this._log.debug(`refreshCachedTokens exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
716
750
|
throw err;
|
|
717
751
|
}
|
|
@@ -798,13 +832,13 @@ export class AuthFlow {
|
|
|
798
832
|
method,
|
|
799
833
|
params,
|
|
800
834
|
});
|
|
801
|
-
this._log.debug(`short RPC request full: ${requestPayload}`);
|
|
835
|
+
this._log.debug(`short RPC request full: ${JSON.stringify(redactRpcLogPayload(JSON.parse(requestPayload)))}`);
|
|
802
836
|
ws.send(requestPayload);
|
|
803
837
|
return;
|
|
804
838
|
}
|
|
805
839
|
// 第二条消息是 RPC 响应
|
|
806
840
|
globalThis.clearTimeout(timeout);
|
|
807
|
-
this._log.debug(`short RPC response full: method=${method} ${JSON.stringify(msg)}`);
|
|
841
|
+
this._log.debug(`short RPC response full: method=${method} ${JSON.stringify(redactRpcLogPayload(msg))}`);
|
|
808
842
|
try {
|
|
809
843
|
ws.close();
|
|
810
844
|
}
|
|
@@ -819,7 +853,7 @@ export class AuthFlow {
|
|
|
819
853
|
return;
|
|
820
854
|
}
|
|
821
855
|
if (result.success === false) {
|
|
822
|
-
reject(new AuthError(String(result.error ?? `${method} failed`)));
|
|
856
|
+
reject(new AuthError(String(result.error ?? `${method} failed`), { data: result }));
|
|
823
857
|
return;
|
|
824
858
|
}
|
|
825
859
|
resolve(result);
|
|
@@ -963,12 +997,32 @@ export class AuthFlow {
|
|
|
963
997
|
return phase2;
|
|
964
998
|
}
|
|
965
999
|
/** 刷新 access token */
|
|
966
|
-
async _refreshAccessToken(gatewayUrl, refreshToken) {
|
|
967
|
-
const
|
|
1000
|
+
async _refreshAccessToken(gatewayUrl, refreshToken, identity) {
|
|
1001
|
+
const params = {
|
|
968
1002
|
refresh_token: refreshToken,
|
|
969
|
-
|
|
1003
|
+
sdk_lang: AUN_SDK_LANG,
|
|
1004
|
+
sdk_version: AUN_SDK_VERSION,
|
|
1005
|
+
};
|
|
1006
|
+
if (identity) {
|
|
1007
|
+
const aid = String(identity.aid ?? '').trim();
|
|
1008
|
+
const deviceId = String(identity.device_id ?? '').trim();
|
|
1009
|
+
const slotId = String(identity.slot_id ?? '').trim();
|
|
1010
|
+
const accessToken = String(identity.access_token ?? '').trim();
|
|
1011
|
+
if (aid)
|
|
1012
|
+
params.aid = aid;
|
|
1013
|
+
if (deviceId)
|
|
1014
|
+
params.device_id = deviceId;
|
|
1015
|
+
if (slotId)
|
|
1016
|
+
params.slot_id = slotId;
|
|
1017
|
+
if (accessToken)
|
|
1018
|
+
params.access_token = accessToken;
|
|
1019
|
+
if (typeof identity.access_token_expires_at === 'number') {
|
|
1020
|
+
params.access_token_expires_at = identity.access_token_expires_at;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
const result = await this._shortRpc(gatewayUrl, 'auth.refresh_token', params);
|
|
970
1024
|
if (!result.success) {
|
|
971
|
-
throw new AuthError(String(result.error ?? 'refresh failed'));
|
|
1025
|
+
throw new AuthError(String(result.error ?? 'refresh failed'), { data: result });
|
|
972
1026
|
}
|
|
973
1027
|
return result;
|
|
974
1028
|
}
|
|
@@ -1354,6 +1408,41 @@ export class AuthFlow {
|
|
|
1354
1408
|
if (typeof activeCert === 'string' && activeCert)
|
|
1355
1409
|
identity._pending_active_cert = activeCert;
|
|
1356
1410
|
}
|
|
1411
|
+
_refreshFailureRequiresRelogin(err) {
|
|
1412
|
+
if (!(err instanceof AuthError))
|
|
1413
|
+
return false;
|
|
1414
|
+
const data = err.data;
|
|
1415
|
+
if (isJsonObject(data)) {
|
|
1416
|
+
if (data.relogin_required === true)
|
|
1417
|
+
return true;
|
|
1418
|
+
const error = String(data.error ?? '').trim().toLowerCase();
|
|
1419
|
+
return ['missing refresh_token', 'invalid_or_expired_refresh_token', 'refresh not supported'].includes(error);
|
|
1420
|
+
}
|
|
1421
|
+
const message = err.message.trim().toLowerCase();
|
|
1422
|
+
return ['missing refresh_token', 'invalid_or_expired_refresh_token', 'refresh not supported'].includes(message);
|
|
1423
|
+
}
|
|
1424
|
+
async _clearCachedTokens(identity, reason = '') {
|
|
1425
|
+
const aid = String(identity.aid ?? '');
|
|
1426
|
+
const hadToken = Boolean(identity.access_token ||
|
|
1427
|
+
identity.refresh_token ||
|
|
1428
|
+
identity.kite_token ||
|
|
1429
|
+
identity.token ||
|
|
1430
|
+
identity.access_token_expires_at);
|
|
1431
|
+
if (!hadToken)
|
|
1432
|
+
return;
|
|
1433
|
+
identity.access_token = '';
|
|
1434
|
+
identity.refresh_token = '';
|
|
1435
|
+
identity.kite_token = '';
|
|
1436
|
+
identity.token = '';
|
|
1437
|
+
identity.access_token_expires_at = 0;
|
|
1438
|
+
try {
|
|
1439
|
+
await this._persistIdentity(identity);
|
|
1440
|
+
this._log.warn(`cleared cached tokens after refresh failure: aid=${aid} reason=${reason}`);
|
|
1441
|
+
}
|
|
1442
|
+
catch (persistErr) {
|
|
1443
|
+
this._log.warn(`failed to persist token cleanup: aid=${aid} err=${persistErr instanceof Error ? persistErr.message : String(persistErr)}`);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1357
1446
|
/** 验证服务端返回的 new_cert,通过后正式接受 */
|
|
1358
1447
|
async _validateNewCert(identity, gatewayUrl = '') {
|
|
1359
1448
|
const newCertPem = identity._pending_new_cert;
|