@veid/agent-identity
v0.6.0
Published
Agent Identity: UserPool (用户池) login, TIP token (工作负载令牌), credential hosting (凭据托管 OAuth2/API key), optional tool/skill permission control (CheckPermission) and risk approval. Integrates with Volcengine 智能体身份和权限管理平台.
Maintainers
Readme
Agent Identity Plugin
UserPool OIDC login, TIP (Trusted Identity Provider) token via Identity GetWorkloadAccessTokenForJWT, credential 3LO (GetResourceOauth2Token/Oauth2Callback), and session management for OpenClaw.
中文文档请参阅 [README-cn.md]
Features
- OIDC Login:
/identity loginreturns IdP auth URL (no HTTP start endpoint). User opens URL, IdP redirects to/identity/oauth/callback. - TIP Token:
before_agent_starthook fetches TIP token when session has a logged-in user - Credential 3LO:
/identity fetch <provider>returns auth URL. IdP redirects to Identity-provided callback (control-plane config). - Credential Binding:
/identity set <provider> <envVar>binds stored credential to env var. Credentials are injected intoprocess.envinbefore_agent_startwhen tools run. - Dynamic UserPool: Resolve OIDC config by
userPoolName+clientName(no manual clientId) - Credentials: Load AK/SK from env, file, or STS AssumeRole (veadk-style)
HTTP Endpoints
Only the OIDC login callback is exposed. Credential OAuth uses Identity callback. All other logic runs in slash commands.
| Path | Method | Description |
| -------------------------- | ------ | ---------------------------------------- |
| /identity/oauth/callback | GET | OIDC login callback (IdP redirects here) |
Slash Commands
Single command /identity (alias /id) with subcommands. Default with no args: status.
| Subcommand | Description |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| (none) | Show help. |
| whoami | Show current session identity (sub, TIP status). |
| login | If logged in: refresh TIP. If not: return OIDC IdP URL to open. |
| status | Show login status, TIP, credentials. Tries to refresh TIP when session exists. |
| logout | Clear session and TIP for current session. |
| list-tips | List all valid TIP tokens with delegation chain, expiry, and env bindings. |
| config | Show identity plugin config (sensitive values redacted). |
| list-credentials or list [page] | List providers from control plane (paginated) and your credentials with bound env. Use list 2 to load more. |
| fetch <provider> [--flow=...] | Add credential. Flow auto-inferred from provider type (api_key/oauth2/m2m); override with --flow. |
| set <provider> <envVar> | Bind credential to env var for tool injection. If no credential, import from process.env[envVar]. |
| unset <provider> | Remove env binding for provider. |
| approve <approval_id> | Approve a pending high-risk tool call. |
| reject <approval_id> | Reject a pending high-risk tool call. |
OIDC Login Flow
- User sends
/identity loginin chat (e.g. Telegram, Discord) - Command derives sessionKey from channel/sender, builds IdP authorize URL, stores state
- Command returns the IdP URL; user opens it in browser
- User completes login at UserPool IdP
- IdP redirects to
/identity/oauth/callbackwithcodeandstate - Plugin exchanges code, creates session, shows success page and sends message to chat
Credential Fetch Flow
OAuth2 (user federation or M2M):
- User sends
/identity fetch googleor/identity fetch google --flow=oauth2-m2m(after/identity login) - Command uses TIP to call Identity API; returns auth URL or direct token
- If auth URL: user opens it; IdP redirects to Identity callback (control-plane provider config)
- Identity handles callback; token obtained via Identity; user may re-run fetch to pull credential
API Key:
- User sends
/identity fetch openai(provider type api_key in control plane) or/identity fetch openai --flow=apikey - Command uses TIP to call GetResourceApiKey; API key stored directly
Flow is auto-inferred from ListCredentialProviders (Type + Flow). Override with --flow=oauth2-user|oauth2-m2m|apikey when needed.
Enable Volcengine Agent Identity Service
Before using this plugin, you must enable Agent Identity and Permission Management in Volcengine and complete authorization. See:
Get Volcengine AK/SK
The plugin requires Volcengine Access Key and Secret Key to call the Identity API. To create credentials:
After creating AK/SK in the console, pass them via config or use environment variables VOLCENGINE_ACCESS_KEY and VOLCENGINE_SECRET_KEY.
Installation
openclaw plugins install @veid/agent-identityOr with link for development:
openclaw plugins install --link .Configuration
Step 2: Configure plugin (minimal setup)
The plugin typically needs three types of config:
A. Platform access (Identity): For TIP Token, credential fetch/hosting, and optional permission checks.
endpoint: Identity API URL (e.g.https://id.cn-beijing.volcengineapi.com). Default when omitted.accessKeyId/secretAccessKey: For Identity API access. Optional when using env vars or credential file (see below).workloadPoolName/workloadName: For issuing TIP Token. Defaults:default,openclaw-agent.audience/durationSeconds: Optional, token audience and validity.credentialsFile: Path to credential JSON. Default:VOLCENGINE_CREDENTIALS_FILEenv or/var/run/secrets/iam/credential.roleTrn: Role TRN for STS AssumeRole. When set, workload name is omitted; backend uses roleName.sessionToken: STS session token (or useVOLCENGINE_SESSION_TOKENenv).
Credential resolution order (AK/SK): 1) Explicit config → 2) Env vars (VOLCENGINE_ACCESS_KEY, VOLCENGINE_SECRET_KEY, VOLCENGINE_SESSION_TOKEN) → 3) Credential file (credentialsFile config, or VOLCENGINE_CREDENTIALS_FILE env, or /var/run/secrets/iam/credential). Credential file format (VeFaaS): access_key_id, secret_access_key, session_token (optional), role_trn (optional for AssumeRole). RUNTIME_IAM_ROLE_TRN env can supply role TRN when loading from file.
B. User login (UserPool / OIDC): For /identity login and session setup.
discoveryUrl(oruserPoolName+clientNamefor dynamic resolution)clientId/clientSecret(auto-resolved in dynamic mode)callbackUrl: Public callback URL for OpenClaw gateway, e.g.http://127.0.0.1:18789/identity/oauth/callbackscope: Typicallyopenid profile email
C. Tool call AuthZ and risk approval (optional): For TIP + CheckPermission + risk evaluation and user approval in before_tool_call. Each flag is independent; no single "enable" switch.
toolCheck: Run CheckPermission for tools (resource type tool). Default false.skillReadCheck: Run CheckPermission for read of SKILL.md (resource type skill). Parses available_skills from system prompt. Default false.requireRiskApproval: Require user approval for high-risk tool calls. Default false.namespaceName: CheckPermission Cedar policy namespace. Defaultdefault.lowRiskBypass: Skip TIP+CheckPermission for built-in low-risk tools. Default true.lowRiskTools: Extra tool names treated as low-risk.enableLlmRiskCheck: Use LLM to re-evaluate when rules return medium. Default false.llmRiskCheck: LLM config (endpoint,api,model,apiKey,timeoutMs,cacheTtlMs). Required whenenableLlmRiskCheckis true.approvalTtlSeconds: Approval link/command TTL (seconds). Default 300.
Expected outcome: After config, the plugin can initiate login and obtain TIP Token. With AuthZ flags enabled, tool/skill permission checks and high-risk approvals apply; use /identity approve <approval_id> to approve blocked calls.
Add to openclaw.json under plugins.entries.agent-identity.config:
{
"plugins": {
"entries": {
"agent-identity": {
"config": {
"identity": {
"endpoint": "https://id.cn-beijing.volcengineapi.com",
"workloadPoolName": "default",
"workloadName": "openclaw-agent"
},
"userpool": {
"discoveryUrl": "https://userpool-xxx.userpool.auth.id.cn-beijing.volces.com",
"clientId": "<client-id>",
"clientSecret": "<client-secret>",
"callbackUrl": "https://gateway.example.com/identity/oauth/callback",
"scope": "openid profile email"
},
"authz": {
"toolCheck": false,
"skillReadCheck": false,
"requireRiskApproval": false,
"namespaceName": "default",
"lowRiskBypass": true,
"enableLlmRiskCheck": false,
"approvalTtlSeconds": 300
}
}
}
}
}
}Identity credentials: Omit accessKeyId/secretAccessKey to use env vars (VOLCENGINE_ACCESS_KEY, VOLCENGINE_SECRET_KEY) or credential file (VOLCENGINE_CREDENTIALS_FILE or /var/run/secrets/iam/credential).
identity config (required vs optional)
| Param | Type | Required | Description |
|-------|------|----------|--------------|
| endpoint | string | Yes | Identity API URL, e.g. https://id.cn-beijing.volcengineapi.com |
| accessKeyId | string | No* | Volcengine Access Key. Omit to load from VOLCENGINE_ACCESS_KEY or credentialsFile |
| secretAccessKey | string | No* | Volcengine Secret Key. Omit to load from VOLCENGINE_SECRET_KEY or credentialsFile |
| workloadPoolName | string | No | Workload pool name, default default |
| workloadName | string | No | Workload name. Default: agentId or openclaw-agent |
| audience | string[] | No | TIP token audience |
| durationSeconds | number | No | TIP token TTL (seconds), default 3600 |
| roleTrn | string | No | Role TRN for STS AssumeRole. When set, workload name is omitted; backend uses roleName |
| credentialsFile | string | No | Path to credential JSON. Default: VOLCENGINE_CREDENTIALS_FILE or /var/run/secrets/iam/credential |
| sessionToken | string | No | STS session token (or VOLCENGINE_SESSION_TOKEN) |
* AK/SK must be provided via accessKeyId+secretAccessKey, environment variables, or credentialsFile.
Environment variables: VOLCENGINE_ACCESS_KEY, VOLCENGINE_SECRET_KEY, VOLCENGINE_SESSION_TOKEN, VOLCENGINE_CREDENTIALS_FILE, RUNTIME_IAM_ROLE_TRN (for AssumeRole when loading from file).
userpool config (OIDC login)
Explicit mode (required): discoveryUrl, clientId, clientSecret, callbackUrl, scope
Dynamic mode (required): userPoolName, clientName, callbackUrl; autoCreate defaults to true
OAuth2 credential fetch uses control-plane redirect URL and scopes. Override via /identity fetch <provider> --redirectUrl and --scopes.
authz config (optional, each flag independent)
| Param | Type | Description |
|-------|------|-------------|
| toolCheck | boolean | Run CheckPermission for tools (resource type tool). Default false. |
| skillReadCheck | boolean | Run CheckPermission for read of SKILL.md (resource type skill). Default false. |
| requireRiskApproval | boolean | Require user approval for high-risk tools. Default false. |
| namespaceName | string | CheckPermission Cedar namespace. Default default. |
| lowRiskBypass | boolean | Skip TIP+CheckPermission for built-in low-risk tools. Default true. |
| lowRiskTools | string[] | Extra tool names treated as low-risk. |
| enableLlmRiskCheck | boolean | Re-evaluate with LLM when rules return medium. Default false. |
| llmRiskCheck | object | LLM config: endpoint, api, model, etc. Required when enableLlmRiskCheck is true. |
| approvalTtlSeconds | number | Approval TTL (seconds). Default 300. |
Workload and TIP
TIP token is obtained via GetWorkloadAccessTokenForJWT. Workload behavior:
- workloadName (optional): Workload name sent to the API. Default:
agentIdfrom session, or"openclaw-agent". Used only when not usingroleTrn. - roleTrn (optional): When set (AssumeRole), the plugin does not pass workload name; the backend uses the role name. Use this for delegated execution (e.g. VeFaaS, K8s IRSA).
- Auto-create workload: When
GetWorkloadAccessTokenForJWTreturns 404 (workload not found), the plugin callsCreateWorkloadIdentityto create the workload (Category: Agent), then retries. Only applies when a workload name is used (noroleTrn). Duplicated (409) from concurrent create is ignored.
Feishu notifications
Login success and credential fetch follow-up messages (e.g. "✓ Credential for google added.") are sent via Feishu when the user runs /identity from a Feishu chat. Credentials are read from channels.feishu in openclaw.json (same as feishu extension: appId, appSecret, optional accounts). No extra config in agent-identity is required.
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.
WebChat / TUI
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.
Tools
- identity_whoami - Show current session identity (sub, TIP status)
- identity_status - Login status, credentials, bindings
- identity_login - Start OIDC login or refresh TIP
- identity_logout - Clear session and TIP
- identity_list_credentials - List providers and credentials (paginated)
- identity_list_tips - List valid TIP tokens and bindings
- identity_config - Show plugin config (redacted)
- identity_config_suggest - Generate config snippets for openclaw.json (intent, lang)
- identity_fetch - Add credential (provider, flow?, redirectUrl?, scopes?)
- identity_set_binding - Bind provider → env var
- identity_unset_binding - Remove env binding
- identity_approve_tool - Optional tool; approve high-risk tool call by approval_id. Prefer
/identity approve <id>(human-only; agent must not self-approve). - identity_risk_check - Diagnose risk for a command or tool call (command, or toolName+params)
- identity_list_risk_patterns - List built-in dangerous commands and sensitive paths
Hooks
- before_agent_start - Fetch TIP token; inject credentials into
process.envper credential-env-bindings (per-session) - subagent_spawned - Propagate TIP to child session on subagent spawn
- before_tool_call - Optional AuthZ when authz.toolCheck, authz.skillReadCheck, or authz.requireRiskApproval. TIP + CheckPermission for tools/skills; risk approval for high-risk tools. Evaluates user-provided commands/paths (rules + optional LLM via authz.enableLlmRiskCheck).
Data Storage
Plugin data at ~/.openclaw/plugins/identity/:
sessions.json- sessionKey → userToken mapping (expired pruned on load/save)tip-tokens.json- sessionKey → TIP token cache (expired pruned on load/save)- Credentials - in-memory only, per-session (api_key, oauth2); lost on gateway restart; cleared on logout
credential-env-bindings.json- per-session:{ [sessionKey]: { [provider]: envVar } }- OIDC state - in-memory only (ephemeral, 5 min TTL)
