@m1a0rz/agent-identity 0.4.0 → 0.4.1
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/README-cn.md +33 -0
- package/README.md +33 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/src/gateway/identity-session-methods.d.ts +67 -0
- package/dist/src/gateway/identity-session-methods.d.ts.map +1 -0
- package/dist/src/gateway/identity-session-methods.js +130 -0
- package/dist/src/store/sender-session-store.d.ts.map +1 -1
- package/dist/src/store/sender-session-store.js +13 -11
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/derive-session-key.d.ts +10 -1
- package/dist/src/utils/derive-session-key.d.ts.map +1 -1
- package/dist/src/utils/derive-session-key.js +18 -2
- package/openclaw.plugin.json +5 -0
- package/package.json +23 -5
package/README-cn.md
CHANGED
|
@@ -193,6 +193,8 @@ openclaw plugins install --link .
|
|
|
193
193
|
| `credentialsFile` | string | 否 | 凭证 JSON 文件路径。默认 `VOLCENGINE_CREDENTIALS_FILE` 或 `/var/run/secrets/iam/credential` |
|
|
194
194
|
| `credentialsMetadataUrl` | string | 否 | 远程凭据拉取的完整 URL。与 `roleTrn` 同时配置时拉取后做 AssumeRole。404 时回退到 `credentialsFile` |
|
|
195
195
|
| `sessionToken` | string | 否 | STS 临时会话令牌(或 `VOLCENGINE_SESSION_TOKEN`) |
|
|
196
|
+
| `subagentTipPropagation` | boolean | 否 | 将 TIP 和 session 传播到子 agent。默认 false |
|
|
197
|
+
| `webchatSessionExchange` | boolean | 否 | 启用 `identity.session.put` / `identity.session.get` gateway WS 方法供 webchat 客户端使用。默认 false |
|
|
196
198
|
|
|
197
199
|
\* AK/SK 至少通过 `accessKeyId`+`secretAccessKey`、环境变量、`credentialsMetadataUrl`+`roleTrn` 或 `credentialsFile` 之一提供。
|
|
198
200
|
|
|
@@ -235,6 +237,37 @@ TIP token 通过 `GetWorkloadAccessTokenForJWT` 获取。工作负载行为:
|
|
|
235
237
|
|
|
236
238
|
**审批消息**(当高风险工具被拦截时):若要向飞书(或 Telegram、Slack 等)推送审批请求,请在 openclaw.json 中将 `session.dmScope` 设置为 `per-channel-peer` 或 `per-account-channel-peer`。默认 `session.dmScope: "main"` 时,sessionKey 不包含 channel/peer 信息,插件无法推导推送目标,审批消息不会推送。用户仍可在 agent 的错误回复中看到 block/approval_id;使用 `/identity approve <id>` 审批。
|
|
237
239
|
|
|
240
|
+
### WebChat Session Exchange(Gateway WS 方法)
|
|
241
|
+
|
|
242
|
+
当 `identity.webchatSessionExchange` 为 `true` 时,插件注册两个 gateway WebSocket 方法,允许 webchat 客户端直接注入和获取 session token,无需走 OIDC 重定向流程:
|
|
243
|
+
|
|
244
|
+
| 方法 | 参数 | 响应 | 描述 |
|
|
245
|
+
| --- | --- | --- | --- |
|
|
246
|
+
| `identity.session.put` | `{ sessionKey, idToken, senderId?, channel? }` | `{ sub, expiresAt, effectiveSessionKey, hasTip }` | 将 OIDC id_token 注入到插件 session。通过 `buildEffectiveSessionKey` 解析实际存储 key(与 hooks/commands 相同的隔离逻辑)。 |
|
|
247
|
+
| `identity.session.get` | `{ sessionKey, senderId?, channel? }` | `{ userToken, sub, expiresAt, effectiveSessionKey }` | 获取指定 session 已存储的 user token。 |
|
|
248
|
+
|
|
249
|
+
- `senderId` 默认值为 `"openclaw-control-ui"`。对于 main session,实际存储 key 为 `agent:main:main:user:<senderId>`。
|
|
250
|
+
- `channel` 可选;当 session 来源于可发送消息的渠道(feishu、telegram 等)时传入,可启用 per-channel-peer key 提升。
|
|
251
|
+
|
|
252
|
+
两个方法均**限制为 webchat WS 连接**(`isWebchatConnect` 检查),非 webchat 客户端会收到 `FORBIDDEN` 错误。
|
|
253
|
+
|
|
254
|
+
**配置:**
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"identity": {
|
|
259
|
+
"webchatSessionExchange": true
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**典型流程(BFF → webchat → plugin):**
|
|
265
|
+
|
|
266
|
+
1. BFF 完成 3LO 登录并获取用户的 OIDC `id_token`
|
|
267
|
+
2. Webchat 客户端调用 `identity.session.put`,传入 session key 和 `id_token`
|
|
268
|
+
3. 插件校验 token,存储 session,并获取 TIP
|
|
269
|
+
4. 后续该 session 中的 agent 运行拥有有效身份——无需手动登录
|
|
270
|
+
|
|
238
271
|
### WebChat / TUI
|
|
239
272
|
|
|
240
273
|
用户从 WebChat 或 TUI 运行 `/identity` 时,跟进消息不会投递;插件无 API 推送至这些渠道。请使用 `/identity status` 确认结果。
|
package/README.md
CHANGED
|
@@ -193,6 +193,8 @@ Add to `openclaw.json` under `plugins.entries.agent-identity.config`:
|
|
|
193
193
|
| `credentialsFile` | string | No | Path to credential JSON. Default: `VOLCENGINE_CREDENTIALS_FILE` or `/var/run/secrets/iam/credential` |
|
|
194
194
|
| `credentialsMetadataUrl` | string | No | Full URL for remote credential fetch. When set with `roleTrn`, fetches then AssumeRole. 404 falls through to `credentialsFile` |
|
|
195
195
|
| `sessionToken` | string | No | STS session token (or `VOLCENGINE_SESSION_TOKEN`) |
|
|
196
|
+
| `subagentTipPropagation` | boolean | No | Propagate TIP and session to subagents. Default false |
|
|
197
|
+
| `webchatSessionExchange` | boolean | No | Enable `identity.session.put` / `identity.session.get` gateway WS methods for webchat clients. Default false |
|
|
196
198
|
|
|
197
199
|
\* AK/SK must be provided via `accessKeyId`+`secretAccessKey`, environment variables, `credentialsMetadataUrl`+`roleTrn`, or `credentialsFile`.
|
|
198
200
|
|
|
@@ -235,6 +237,37 @@ Login success and credential fetch follow-up messages (e.g. "✓ Credential for
|
|
|
235
237
|
|
|
236
238
|
**Approval messages** (when a high-risk tool is blocked): For approval requests to be delivered to Feishu (or Telegram, Slack, etc.), set `session.dmScope` to `per-channel-peer` or `per-account-channel-peer` in openclaw.json. With default `session.dmScope: "main"`, the sessionKey does not include channel/peer info, so the plugin cannot derive a delivery target and approval messages are not pushed. The user will still see the block/approval_id in the agent's error reply; use `/identity approve <id>` to approve.
|
|
237
239
|
|
|
240
|
+
### WebChat Session Exchange (Gateway WS Methods)
|
|
241
|
+
|
|
242
|
+
When `identity.webchatSessionExchange` is `true`, the plugin registers two gateway WebSocket methods for webchat clients to inject and retrieve session tokens without going through the OIDC redirect flow:
|
|
243
|
+
|
|
244
|
+
| Method | Params | Response | Description |
|
|
245
|
+
| --- | --- | --- | --- |
|
|
246
|
+
| `identity.session.put` | `{ sessionKey, idToken, senderId?, channel? }` | `{ sub, expiresAt, effectiveSessionKey, hasTip }` | Inject an OIDC id_token into a plugin session. Resolves effective storage key via `buildEffectiveSessionKey` (same sender isolation as hooks/commands). |
|
|
247
|
+
| `identity.session.get` | `{ sessionKey, senderId?, channel? }` | `{ userToken, sub, expiresAt, effectiveSessionKey }` | Retrieve the stored user token for a session. |
|
|
248
|
+
|
|
249
|
+
- `senderId` defaults to `"openclaw-control-ui"`. The effective storage key is `agent:main:main:user:<senderId>` for main sessions.
|
|
250
|
+
- `channel` is optional; when the session originates from a sendable channel (feishu, telegram, etc.), pass it to enable per-channel-peer key promotion.
|
|
251
|
+
|
|
252
|
+
Both methods are **restricted to webchat WS connections only** (`isWebchatConnect` check). Non-webchat clients receive a `FORBIDDEN` error.
|
|
253
|
+
|
|
254
|
+
**Config:**
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"identity": {
|
|
259
|
+
"webchatSessionExchange": true
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Typical flow (BFF → webchat → plugin):**
|
|
265
|
+
|
|
266
|
+
1. BFF completes 3LO login and obtains an OIDC `id_token` for the user
|
|
267
|
+
2. Webchat client calls `identity.session.put` with the session key and `id_token`
|
|
268
|
+
3. Plugin verifies the token, stores the session, and acquires TIP
|
|
269
|
+
4. Subsequent agent runs in that session have a valid identity — no manual login needed
|
|
270
|
+
|
|
238
271
|
### WebChat / TUI
|
|
239
272
|
|
|
240
273
|
Follow-up messages (login success, credential fetch done) are not delivered when the user runs `/identity` from WebChat or TUI; the plugin has no API to push to those channels. Use `/identity status` to confirm results.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA0E7D,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QAyatD"}
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ import { createIdentityStatusTool } from "./src/tools/identity-status.js";
|
|
|
43
43
|
import { createIdentityUnsetBindingTool } from "./src/tools/identity-unset-binding.js";
|
|
44
44
|
import { createIdentityWhoamiTool } from "./src/tools/identity-whoami.js";
|
|
45
45
|
import { parseSessionKeyToDeliveryTarget, } from "./src/utils/derive-session-key.js";
|
|
46
|
+
import { createSessionPutHandler, createSessionGetHandler, } from "./src/gateway/identity-session-methods.js";
|
|
46
47
|
import { logDebug, logInfo, logWarn } from "./src/utils/logger.js";
|
|
47
48
|
import { initEncryptionKey } from "./src/store/encryption.js";
|
|
48
49
|
const PLUGIN_STORE_DIR = "~/.openclaw/plugins/identity";
|
|
@@ -410,4 +411,17 @@ export default function register(api) {
|
|
|
410
411
|
}));
|
|
411
412
|
// Companion after_tool_call: restore env snapshot set by credential injection
|
|
412
413
|
api.on("after_tool_call", createAfterToolCallHandler({ logger: api.logger }));
|
|
414
|
+
// Gateway WS methods: webchat session exchange (inject / retrieve user token)
|
|
415
|
+
if (identityCfg?.webchatSessionExchange && hasIdentity) {
|
|
416
|
+
const sessionMethodDeps = {
|
|
417
|
+
storeDir,
|
|
418
|
+
identityService,
|
|
419
|
+
getOidcConfigForRefresh: getOidcConfigForRefresh ?? undefined,
|
|
420
|
+
configWorkloadName: identityCfg?.workloadName,
|
|
421
|
+
logger: api.logger,
|
|
422
|
+
};
|
|
423
|
+
api.registerGatewayMethod("identity.session.put", createSessionPutHandler(sessionMethodDeps));
|
|
424
|
+
api.registerGatewayMethod("identity.session.get", createSessionGetHandler(sessionMethodDeps));
|
|
425
|
+
logInfo(api.logger, "gateway methods: identity.session.put, identity.session.get (webchat session exchange)");
|
|
426
|
+
}
|
|
413
427
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway WS methods for webchat session exchange:
|
|
3
|
+
*
|
|
4
|
+
* identity.session.put — inject an OIDC id_token into a plugin session
|
|
5
|
+
* identity.session.get — retrieve the stored user token for a session
|
|
6
|
+
*
|
|
7
|
+
* Both methods are restricted to webchat WS connections and gated by
|
|
8
|
+
* config.identity.webchatSessionExchange.
|
|
9
|
+
*/
|
|
10
|
+
import type { IdentityService } from "../services/identity-service.js";
|
|
11
|
+
import type { OIDCConfigForRefresh } from "../services/session-refresh.js";
|
|
12
|
+
type RespondFn = (ok: boolean, payload?: unknown, error?: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
type GatewayMethodOptions = {
|
|
17
|
+
req: {
|
|
18
|
+
id: string;
|
|
19
|
+
method: string;
|
|
20
|
+
params?: unknown;
|
|
21
|
+
};
|
|
22
|
+
params: Record<string, unknown>;
|
|
23
|
+
client: {
|
|
24
|
+
connect?: {
|
|
25
|
+
client?: {
|
|
26
|
+
mode?: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
} | null;
|
|
30
|
+
isWebchatConnect: (params: unknown) => boolean;
|
|
31
|
+
respond: RespondFn;
|
|
32
|
+
context: Record<string, unknown>;
|
|
33
|
+
};
|
|
34
|
+
export type GatewayMethodHandler = (opts: GatewayMethodOptions) => Promise<void> | void;
|
|
35
|
+
export type IdentitySessionMethodsDeps = {
|
|
36
|
+
storeDir: string;
|
|
37
|
+
identityService: IdentityService;
|
|
38
|
+
getOidcConfigForRefresh?: () => Promise<OIDCConfigForRefresh>;
|
|
39
|
+
configWorkloadName?: string;
|
|
40
|
+
logger: {
|
|
41
|
+
info?: (msg: string) => void;
|
|
42
|
+
debug?: (msg: string) => void;
|
|
43
|
+
warn?: (msg: string) => void;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* identity.session.put — inject id_token into a session.
|
|
48
|
+
*
|
|
49
|
+
* Params: { sessionKey: string, idToken: string, senderId?: string, channel?: string }
|
|
50
|
+
* Response: { sub: string, expiresAt: number, effectiveSessionKey: string, hasTip: boolean }
|
|
51
|
+
*
|
|
52
|
+
* senderId defaults to "openclaw-control-ui". The effective storage key is derived
|
|
53
|
+
* via buildEffectiveSessionKey (same logic as hooks/commands) to ensure correct
|
|
54
|
+
* sender isolation.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createSessionPutHandler(deps: IdentitySessionMethodsDeps): GatewayMethodHandler;
|
|
57
|
+
/**
|
|
58
|
+
* identity.session.get — retrieve stored user token for a session.
|
|
59
|
+
*
|
|
60
|
+
* Params: { sessionKey: string, senderId?: string, channel?: string }
|
|
61
|
+
* Response: { userToken: string, sub: string, expiresAt: number | null, effectiveSessionKey: string }
|
|
62
|
+
*
|
|
63
|
+
* senderId defaults to "openclaw-control-ui".
|
|
64
|
+
*/
|
|
65
|
+
export declare function createSessionGetHandler(deps: IdentitySessionMethodsDeps): GatewayMethodHandler;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=identity-session-methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-session-methods.d.ts","sourceRoot":"","sources":["../../../src/gateway/identity-session-methods.ts"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAQ3E,KAAK,SAAS,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAErG,KAAK,oBAAoB,GAAG;IAC1B,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5D,gBAAgB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAC/C,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAExF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACvG,CAAC;AAUF;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,0BAA0B,GAAG,oBAAoB,CA8D9F;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,0BAA0B,GAAG,oBAAoB,CAqC9F"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { getSession, setSession } from "../store/session-store.js";
|
|
17
|
+
import { buildEffectiveSessionKey } from "../store/sender-session-store.js";
|
|
18
|
+
import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
|
|
19
|
+
import { logDebug, logInfo, logWarn } from "../utils/logger.js";
|
|
20
|
+
const DEFAULT_WEBCHAT_SENDER_ID = "openclaw-control-ui";
|
|
21
|
+
function respondError(respond, code, message) {
|
|
22
|
+
respond(false, undefined, { code, message });
|
|
23
|
+
}
|
|
24
|
+
function isWebchat(opts) {
|
|
25
|
+
return opts.isWebchatConnect(opts.client?.connect ?? null);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* identity.session.put — inject id_token into a session.
|
|
29
|
+
*
|
|
30
|
+
* Params: { sessionKey: string, idToken: string, senderId?: string, channel?: string }
|
|
31
|
+
* Response: { sub: string, expiresAt: number, effectiveSessionKey: string, hasTip: boolean }
|
|
32
|
+
*
|
|
33
|
+
* senderId defaults to "openclaw-control-ui". The effective storage key is derived
|
|
34
|
+
* via buildEffectiveSessionKey (same logic as hooks/commands) to ensure correct
|
|
35
|
+
* sender isolation.
|
|
36
|
+
*/
|
|
37
|
+
export function createSessionPutHandler(deps) {
|
|
38
|
+
const { storeDir, identityService, getOidcConfigForRefresh, configWorkloadName, logger } = deps;
|
|
39
|
+
return async (opts) => {
|
|
40
|
+
if (!isWebchat(opts)) {
|
|
41
|
+
return respondError(opts.respond, "FORBIDDEN", "identity.session.put is only available for webchat connections");
|
|
42
|
+
}
|
|
43
|
+
const { params } = opts;
|
|
44
|
+
const sessionKey = typeof params.sessionKey === "string" ? params.sessionKey.trim() : "";
|
|
45
|
+
const idToken = typeof params.idToken === "string" ? params.idToken.trim() : "";
|
|
46
|
+
const senderId = typeof params.senderId === "string" && params.senderId.trim()
|
|
47
|
+
? params.senderId.trim()
|
|
48
|
+
: DEFAULT_WEBCHAT_SENDER_ID;
|
|
49
|
+
const channel = typeof params.channel === "string" && params.channel.trim()
|
|
50
|
+
? params.channel.trim()
|
|
51
|
+
: undefined;
|
|
52
|
+
if (!sessionKey) {
|
|
53
|
+
return respondError(opts.respond, "INVALID_PARAMS", "sessionKey is required");
|
|
54
|
+
}
|
|
55
|
+
if (!idToken) {
|
|
56
|
+
return respondError(opts.respond, "INVALID_PARAMS", "idToken is required");
|
|
57
|
+
}
|
|
58
|
+
const effectiveKey = buildEffectiveSessionKey(sessionKey, senderId, channel);
|
|
59
|
+
const parsed = identityService.parseUserToken(idToken);
|
|
60
|
+
if (!parsed.valid || !parsed.sub) {
|
|
61
|
+
return respondError(opts.respond, "INVALID_TOKEN", "idToken is not a valid JWT or missing sub claim");
|
|
62
|
+
}
|
|
63
|
+
let expiresAt = Date.now() + 3600 * 1000;
|
|
64
|
+
try {
|
|
65
|
+
const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString("utf-8"));
|
|
66
|
+
if (payload.exp)
|
|
67
|
+
expiresAt = payload.exp * 1000;
|
|
68
|
+
}
|
|
69
|
+
catch { /* use default */ }
|
|
70
|
+
await setSession(storeDir, effectiveKey, {
|
|
71
|
+
userToken: idToken,
|
|
72
|
+
sub: parsed.sub,
|
|
73
|
+
loginAt: Date.now(),
|
|
74
|
+
expiresAt,
|
|
75
|
+
});
|
|
76
|
+
logInfo(logger, `identity.session.put: session injected for effectiveKey=${effectiveKey} sub=${parsed.sub}`);
|
|
77
|
+
const tipRefreshOptions = getOidcConfigForRefresh
|
|
78
|
+
? { identityService, getOidcConfigForRefresh, configWorkloadName, logger }
|
|
79
|
+
: undefined;
|
|
80
|
+
const tip = await getOrRefreshTIPToken(storeDir, effectiveKey, tipRefreshOptions).catch((err) => {
|
|
81
|
+
logWarn(logger, `identity.session.put: TIP acquisition failed after inject: ${String(err)}`);
|
|
82
|
+
return null;
|
|
83
|
+
});
|
|
84
|
+
opts.respond(true, {
|
|
85
|
+
sub: parsed.sub,
|
|
86
|
+
expiresAt,
|
|
87
|
+
effectiveSessionKey: effectiveKey,
|
|
88
|
+
hasTip: Boolean(tip),
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* identity.session.get — retrieve stored user token for a session.
|
|
94
|
+
*
|
|
95
|
+
* Params: { sessionKey: string, senderId?: string, channel?: string }
|
|
96
|
+
* Response: { userToken: string, sub: string, expiresAt: number | null, effectiveSessionKey: string }
|
|
97
|
+
*
|
|
98
|
+
* senderId defaults to "openclaw-control-ui".
|
|
99
|
+
*/
|
|
100
|
+
export function createSessionGetHandler(deps) {
|
|
101
|
+
const { storeDir, logger } = deps;
|
|
102
|
+
return async (opts) => {
|
|
103
|
+
if (!isWebchat(opts)) {
|
|
104
|
+
return respondError(opts.respond, "FORBIDDEN", "identity.session.get is only available for webchat connections");
|
|
105
|
+
}
|
|
106
|
+
const { params } = opts;
|
|
107
|
+
const sessionKey = typeof params.sessionKey === "string" ? params.sessionKey.trim() : "";
|
|
108
|
+
const senderId = typeof params.senderId === "string" && params.senderId.trim()
|
|
109
|
+
? params.senderId.trim()
|
|
110
|
+
: DEFAULT_WEBCHAT_SENDER_ID;
|
|
111
|
+
const channel = typeof params.channel === "string" && params.channel.trim()
|
|
112
|
+
? params.channel.trim()
|
|
113
|
+
: undefined;
|
|
114
|
+
if (!sessionKey) {
|
|
115
|
+
return respondError(opts.respond, "INVALID_PARAMS", "sessionKey is required");
|
|
116
|
+
}
|
|
117
|
+
const effectiveKey = buildEffectiveSessionKey(sessionKey, senderId, channel);
|
|
118
|
+
const session = await getSession(storeDir, effectiveKey);
|
|
119
|
+
if (!session) {
|
|
120
|
+
return respondError(opts.respond, "NOT_FOUND", `No session found for effectiveKey=${effectiveKey}`);
|
|
121
|
+
}
|
|
122
|
+
logDebug(logger, `identity.session.get: returning token for effectiveKey=${effectiveKey} sub=${session.sub}`);
|
|
123
|
+
opts.respond(true, {
|
|
124
|
+
userToken: session.userToken,
|
|
125
|
+
sub: session.sub,
|
|
126
|
+
expiresAt: session.expiresAt ?? null,
|
|
127
|
+
effectiveSessionKey: effectiveKey,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sender-session-store.d.ts","sourceRoot":"","sources":["../../../src/store/sender-session-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sender-session-store.d.ts","sourceRoot":"","sources":["../../../src/store/sender-session-store.ts"],"names":[],"mappings":"AAmCA,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAIF,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAGpE;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAQpE;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEpD;AAkBD;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAUrE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAQR;AASD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAInE;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAElD"}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* per-channel-peer format (agent:{id}:{channel}:direct:{senderId}), making it
|
|
22
22
|
* parseable for message delivery after OIDC login and approval flows.
|
|
23
23
|
*/
|
|
24
|
-
import { needsSenderIsolation, isMainSharedSessionKey, parseAgentIdFromSessionKey, isSendableChannel, } from "../utils/derive-session-key.js";
|
|
24
|
+
import { needsSenderIsolation, isMainSharedSessionKey, parseAgentIdFromSessionKey, isSendableChannel, normalizeMainSessionKey, } from "../utils/derive-session-key.js";
|
|
25
25
|
const SESSION_TTL_MS = 2 * 60 * 60 * 1000;
|
|
26
26
|
const store = new Map();
|
|
27
27
|
export function setSender(sessionKey, info) {
|
|
@@ -67,27 +67,29 @@ function promoteMainToChannelPeerKey(sessionKey, channel, senderId) {
|
|
|
67
67
|
* appending :user:<senderId>.
|
|
68
68
|
*/
|
|
69
69
|
export function resolveEffectiveSessionKey(sessionKey) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const normalized = normalizeMainSessionKey(sessionKey);
|
|
71
|
+
if (!needsSenderIsolation(normalized))
|
|
72
|
+
return normalized;
|
|
73
|
+
const sender = getSender(normalized);
|
|
73
74
|
if (!sender?.senderId)
|
|
74
|
-
return
|
|
75
|
-
const promoted = promoteMainToChannelPeerKey(
|
|
75
|
+
return normalized;
|
|
76
|
+
const promoted = promoteMainToChannelPeerKey(normalized, sender.channelId, sender.senderId);
|
|
76
77
|
if (promoted)
|
|
77
78
|
return promoted;
|
|
78
|
-
return `${
|
|
79
|
+
return `${normalized}:user:${sender.senderId}`;
|
|
79
80
|
}
|
|
80
81
|
/**
|
|
81
82
|
* Same as resolveEffectiveSessionKey but takes explicit senderId and channel
|
|
82
83
|
* (used by commands where ctx.senderId and ctx.channel are directly available).
|
|
83
84
|
*/
|
|
84
85
|
export function buildEffectiveSessionKey(sessionKey, senderId, channel) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
const normalized = normalizeMainSessionKey(sessionKey);
|
|
87
|
+
if (!senderId || !needsSenderIsolation(normalized))
|
|
88
|
+
return normalized;
|
|
89
|
+
const promoted = promoteMainToChannelPeerKey(normalized, channel, senderId);
|
|
88
90
|
if (promoted)
|
|
89
91
|
return promoted;
|
|
90
|
-
return `${
|
|
92
|
+
return `${normalized}:user:${senderId}`;
|
|
91
93
|
}
|
|
92
94
|
// --------------- freeze-by-runId ---------------
|
|
93
95
|
const RUN_TTL_MS = 2 * 60 * 60 * 1000;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export type IdentityConfig = {
|
|
|
31
31
|
durationSeconds?: number;
|
|
32
32
|
/** Propagate TIP and session to subagents (sessions_spawn, sessions_send to subagent). Default: false. */
|
|
33
33
|
subagentTipPropagation?: boolean;
|
|
34
|
+
/** Enable identity.session.put / identity.session.get gateway WS methods for webchat clients. Default: false. */
|
|
35
|
+
webchatSessionExchange?: boolean;
|
|
34
36
|
};
|
|
35
37
|
export type UserPoolConfig = {
|
|
36
38
|
/** Explicit: discovery URL for OIDC. */
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,12 @@ type DeriveSessionKeyParams = {
|
|
|
22
22
|
accountId?: string;
|
|
23
23
|
config: ConfigWithSession;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Normalize 3-segment gateway-assigned session keys to canonical main form.
|
|
27
|
+
* `agent:{id}:<uuid>` → `agent:{id}:main`.
|
|
28
|
+
* Keys that are already `agent:{id}:main` or have 4+ segments are returned as-is.
|
|
29
|
+
*/
|
|
30
|
+
export declare function normalizeMainSessionKey(sessionKey: string): string;
|
|
25
31
|
/**
|
|
26
32
|
* Derive agentId from config. Fallback when ctx.agentId and sessionKey are not available.
|
|
27
33
|
*/
|
|
@@ -59,7 +65,10 @@ export declare function isSubagentSessionKey(sessionKey: string | undefined | nu
|
|
|
59
65
|
*/
|
|
60
66
|
export declare function isGroupOrChannelSessionKey(sessionKey: string | undefined | null): boolean;
|
|
61
67
|
/**
|
|
62
|
-
* Check whether sessionKey is the default "main" shared session
|
|
68
|
+
* Check whether sessionKey is the default "main" shared session.
|
|
69
|
+
* Matches any 3-segment key: `agent:{id}:main` as well as gateway-assigned
|
|
70
|
+
* variants like `agent:{id}:<uuid>` (e.g. `agent:main:db222689-...`).
|
|
71
|
+
*
|
|
63
72
|
* When dmScope is unset, all DM users share this key — including:
|
|
64
73
|
* - Feishu/Telegram/etc. DMs (have senderId, will be isolated)
|
|
65
74
|
* - webchat/tui (typically no senderId; single-user, so no isolation needed)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-session-key.d.ts","sourceRoot":"","sources":["../../../src/utils/derive-session-key.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;
|
|
1
|
+
{"version":3,"file":"derive-session-key.d.ts","sourceRoot":"","sources":["../../../src/utils/derive-session-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,19 @@
|
|
|
15
15
|
*/
|
|
16
16
|
const DEFAULT_AGENT_ID = "main";
|
|
17
17
|
const DEFAULT_ACCOUNT_ID = "default";
|
|
18
|
+
/**
|
|
19
|
+
* Normalize 3-segment gateway-assigned session keys to canonical main form.
|
|
20
|
+
* `agent:{id}:<uuid>` → `agent:{id}:main`.
|
|
21
|
+
* Keys that are already `agent:{id}:main` or have 4+ segments are returned as-is.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeMainSessionKey(sessionKey) {
|
|
24
|
+
const parts = sessionKey.split(":");
|
|
25
|
+
if (parts.length !== 3 || parts[0]?.toLowerCase() !== "agent")
|
|
26
|
+
return sessionKey;
|
|
27
|
+
if (parts[2].toLowerCase() === "main")
|
|
28
|
+
return sessionKey;
|
|
29
|
+
return `${parts[0]}:${parts[1]}:main`;
|
|
30
|
+
}
|
|
18
31
|
function normalizeAgentId(value) {
|
|
19
32
|
const trimmed = (value ?? "").trim();
|
|
20
33
|
return trimmed ? trimmed.toLowerCase() : DEFAULT_AGENT_ID;
|
|
@@ -84,7 +97,10 @@ export function isGroupOrChannelSessionKey(sessionKey) {
|
|
|
84
97
|
return scope === "group" || scope === "channel";
|
|
85
98
|
}
|
|
86
99
|
/**
|
|
87
|
-
* Check whether sessionKey is the default "main" shared session
|
|
100
|
+
* Check whether sessionKey is the default "main" shared session.
|
|
101
|
+
* Matches any 3-segment key: `agent:{id}:main` as well as gateway-assigned
|
|
102
|
+
* variants like `agent:{id}:<uuid>` (e.g. `agent:main:db222689-...`).
|
|
103
|
+
*
|
|
88
104
|
* When dmScope is unset, all DM users share this key — including:
|
|
89
105
|
* - Feishu/Telegram/etc. DMs (have senderId, will be isolated)
|
|
90
106
|
* - webchat/tui (typically no senderId; single-user, so no isolation needed)
|
|
@@ -93,7 +109,7 @@ export function isMainSharedSessionKey(sessionKey) {
|
|
|
93
109
|
const raw = (sessionKey ?? "").trim().toLowerCase();
|
|
94
110
|
if (!raw)
|
|
95
111
|
return false;
|
|
96
|
-
return /^agent:[^:]+:
|
|
112
|
+
return /^agent:[^:]+:[^:]+$/.test(raw);
|
|
97
113
|
}
|
|
98
114
|
/**
|
|
99
115
|
* Whether the sessionKey may be shared by multiple users and thus requires
|
package/openclaw.plugin.json
CHANGED
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"type": "boolean",
|
|
61
61
|
"default": false,
|
|
62
62
|
"description": "Propagate TIP and session to subagents (sessions_spawn, sessions_send). When true, subagents get their own TIP token."
|
|
63
|
+
},
|
|
64
|
+
"webchatSessionExchange": {
|
|
65
|
+
"type": "boolean",
|
|
66
|
+
"default": false,
|
|
67
|
+
"description": "Enable identity.session.put / identity.session.get gateway WS methods for webchat clients. Allows BFF to inject OIDC id_token into plugin sessions without redirect flow."
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m1a0rz/agent-identity",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Agent Identity: UserPool (用户池) login, TIP token (工作负载令牌), credential hosting (凭据托管 OAuth2/API key), optional tool/skill permission control (CheckPermission) and risk approval. Integrates with Volcengine 智能体身份和权限管理平台.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,7 +9,16 @@
|
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"prepublishOnly": "npm run build"
|
|
11
11
|
},
|
|
12
|
-
"keywords": [
|
|
12
|
+
"keywords": [
|
|
13
|
+
"openclaw",
|
|
14
|
+
"identity",
|
|
15
|
+
"volcengine",
|
|
16
|
+
"userpool",
|
|
17
|
+
"credential",
|
|
18
|
+
"TIP",
|
|
19
|
+
"auth",
|
|
20
|
+
"agent"
|
|
21
|
+
],
|
|
13
22
|
"license": "Apache-2.0",
|
|
14
23
|
"dependencies": {
|
|
15
24
|
"@sinclair/typebox": "0.34.48",
|
|
@@ -17,7 +26,8 @@
|
|
|
17
26
|
},
|
|
18
27
|
"devDependencies": {
|
|
19
28
|
"@types/node": "^22.0.0",
|
|
20
|
-
"typescript": "^5.7.0"
|
|
29
|
+
"typescript": "^5.7.0",
|
|
30
|
+
"ws": "^8.19.0"
|
|
21
31
|
},
|
|
22
32
|
"peerDependencies": {
|
|
23
33
|
"openclaw": ">=2026.3.8"
|
|
@@ -28,7 +38,15 @@
|
|
|
28
38
|
}
|
|
29
39
|
},
|
|
30
40
|
"openclaw": {
|
|
31
|
-
"extensions": [
|
|
41
|
+
"extensions": [
|
|
42
|
+
"./dist/index.js"
|
|
43
|
+
]
|
|
32
44
|
},
|
|
33
|
-
"files": [
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"README.md",
|
|
48
|
+
"README-cn.md",
|
|
49
|
+
"skills/SKILL.md",
|
|
50
|
+
"openclaw.plugin.json"
|
|
51
|
+
]
|
|
34
52
|
}
|