关于 agent-web-identity
agent-web-identity 指导 Agent 使用 `icp` CLI 将 Internet Identity 关联到终端会话。它适用于以应用专属主体身份执行经过用户认证的 canister 调用,同时避免 Agent 接触身份私钥,并要求状态变更前取得确认。
主要功能
- 01通过 `icp identity link web` 关联
- 02使用应用专属主体
- 03避免暴露身份私钥
- 04说明委托与会话安全措施
适用场景
- 01以应用用户身份认证 CLI canister 调用
- 02通过 whoami 验证关联主体
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dfinity/ icskills --skill agent-web-identity安装后在 Claude Code 中输入/agent-web-identity确认已加载。
详细介绍
Agent Web 身份登录
这是什么
Internet Identity(II)支持一种 CLI 身份验证流程,可为保存在浏览器之外的会话密钥签发委托。icp identity link web 使用此流程,让终端会话(包括 Claude Code 这样的 AI Agent)以用户的应用专属主体身份签署 canister 调用:也就是用户登录该应用 Web 界面时使用的同一主体(通过 --app <domain> 选择)。
用户仍需在自己的浏览器中使用通行密钥进行交互式身份验证;Agent 最终只会持有有时限的委托。用户 II 身份的私钥绝不会泄露。
前置条件
- 已安装
icpCLI(https://cli.internetcomputer.org)。一般用法请参阅icp-cli技能。 - 用户必须拥有 Internet Identity(id.ai),并在场完成浏览器登录。
- 首次使用时,用户需要在 II 设置中为其身份启用 CLI access 开关(见常见问题 1)。
- Agent 和用户的浏览器必须位于同一台机器上:该流程通过 localhost 回调传递委托。此流程无法在远程或无头环境中使用(容器、CI、Web 会话)。
常见问题
-
首次登录时出现“CLI access disabled”屏幕。 这是预期的首次运行流程,并非错误。请告知用户在 II 设置中为其身份启用 CLI access,然后从头重新启动流程——之前的登录 URL 只能使用一次(其 nonce 和 localhost 监听器都已失效)。首次使用时请预留两轮操作。
-
链接命令在执行任何操作前会等待按下 Enter 键。 在 0.3.x 中,它会先输出
Press Enter to log in at https://id.ai/cli,然后阻塞并读取 stdin;只有在此之后才会启动流程并尝试打开浏览器。在后台或非交互式运行时,请向其输入实际的换行符(printf '\n' | icp identity link web ...)。不要将输入重定向到/dev/null:在 0.3.2 中,单独的 EOF 无法满足提示要求——命令会停在Press Enter to log in,流程始终不会启动,导致第一次尝试白白浪费,还得重新开始并再次提示用户。第一次尝试时输入换行符,流程就会立即启动。 -
不要假设浏览器已经打开。 沙盒或非交互式 shell 通常无法启动图形界面浏览器。请查看命令输出:如果输出了完整的登录 URL,请将其提供给用户,让用户自行打开。如果输出既未显示浏览器已启动,也未显示 URL,请如实说明——不要猜测并构造 URL,并询问用户是否出现了浏览器窗口。
-
命令会一直阻塞到登录完成,并可能因工具超时而被终止。 请将
icp identity link web作为后台任务运行,然后等用户确认已完成登录后再检查结果。命令还必须能够绑定 localhost 端口;如果沙盒阻止此操作,请在取消沙盒限制后重新运行之前先询问用户。 -
依赖默认身份会导致使用错误的主体进行签名。 所有以用户身份执行的
icp命令都必须显式传入--identity <NAME>。绝不要依赖项目或全局默认身份设置,也不要更改这些设置。 -
重复使用或覆盖身份。 永远不要重复使用现有身份,即使
icp identity list已显示有一个身份关联到同一应用——它的委托可能已过期,而且归创建者所有。每个会话始终创建一个新身份:检查icp identity list,然后选择一个以 Agent 自身标识符为前缀的唯一名称,格式为<agent>-<app>-<YYYYMMDD-HHMM>——例如,Claude 使用claude-oisy-20260611-1530,Cursor 使用cursor-oisy-...;如果不知道标识符,则使用agent-。永远不要 覆盖现有身份。icp identity list只是用于选择空闲名称的只读上下文——绝不要删除、重命名、重新授权或以其他方式修改任何不是你在本次会话中创建的身份,包括以前运行时遗留的、带有 Agent 自身前缀的身份。仅操作$NAME。 为什么要为每个会话创建身份,而不是每个应用重复使用一个身份:对于给定的 II 身份,--app会派生出相同的应用主体,因此新身份不会带来额外的权限隔离——重点在于来源可追溯性和影响范围。按会话创建身份可让icp identity list便于审计(哪个会话创建了哪个有效委托),让清理时可以只删除对应的那个身份,并避免多个会话共用一个身份,导致其他会话的reauth悄悄修改本会话正在使用的委托。扫描
icp identity list以选择名称时,如果看到同一应用有多个来自过去会话的过期 Agent 前缀身份(例如,多个claude-oisy-*条目),请在继续之前提出清理:列出这些身份,等待用户逐一确认,然后使用icp identity delete <NAME>删除。未经明确确认,绝不要删除。 -
遗漏
--app导致主体错误。 如果不指定--app,提供方会使用其默认派生源,生成的主体将与用户在目标应用中的主体不匹配。传入应用的裸域名(不含 scheme、端口或路径),例如--app oisy.com。 -
委托过期。 委托有时效限制;有效期由身份提供方在登录时设置。
icp identity link web没有任何可用于控制有效期的标志——不要臆造标志(例如--ttl;0.3.x 会拒绝该标志并导致整个命令失败)。当调用开始因签名或过期错误而失败时,运行icp identity reauth <NAME>——用户必须以同一身份重新登录。 -
未显式传入参数的 Canister 调用会自动取消。 如果省略调用参数,
icp canister call会打开交互式提示,以构建参数并确认发送(Do you want to send this message? [y/N]),而在非交互式 shell 中会立即取消(User cancelled.)。始终显式传入参数——包括零参数方法(如whoami)的空元组'()'。
实现
将用户关联到 <APP_DOMAIN>(例如 oisy.com、nns.ic0.app)的通用流程:
# 1. Pick a fresh name (never reuse an existing identity, even one
# already linked for this app); confirm it doesn't exist
icp identity list
NAME="agent-<app>-$(date +%Y%m%d-%H%M)"
# substitute your own agent prefix for "agent" if known,
# e.g. claude-oisy-20260611-1530, cursor-oisy-...
# 2. Start the link flow IN THE BACKGROUND (keeps the localhost
# listener alive while the user signs in). Use your harness's
# background-execution mode if it has one; in a plain shell:
printf '\n' | icp identity link web "$NAME" --app <APP_DOMAIN> \
> "/tmp/icp-link-$NAME.log" 2>&1 &
# Pipe a real newline to answer the "Press Enter to log in" prompt.
# Do NOT use `< /dev/null`: on 0.3.2 a bare EOF does not satisfy the
# prompt, so the command hangs and you'd have to restart it — which
# prompts the user a second time. The newline starts the flow on the
# first try.
# 3. Read the command's output (the log file above). After the Enter
# prompt it tries to open the user's browser; if a sign-in URL is
# printed instead, relay it to the user. Wait for them to confirm
# sign-in (first run may require enabling CLI access in II settings
# — restart from step 2 if so)
# 4. Confirm the identity was created
icp identity list
验证是否有效
以已关联身份调用公共 whoami canister:
icp canister call --identity "$NAME" --network ic \
ivcos-eqaaa-aaaab-qablq-cai whoami '()'
(根据已安装的 CLI 版本调整标志——参见 icp canister call --help——但始终保留显式的 --identity 和显式的 '()' 参数;省略参数会触发交互式提示,并在非交互式 shell 中自动取消,见陷阱 9。)返回的主体应与用户登录 <APP_DOMAIN> 时应用向用户显示的主体一致;请用户确认。
面向 Agent 的安全规则
- 委派在过期前授予用户应用主体的完整权限——它没有限定到特定的 canister。应将其视为该应用的一张已签署空白支票。
- 除 whoami 验证外,每次使用已关联身份进行会更改状态的 canister 调用前,都要明确征求用户同意。
- 委派的有效期由身份提供方决定,而非 CLI——没有可缩短有效期的标志。
- 绝不导出、打印或记录身份的密钥材料;将其保留在 CLI 的默认密钥环存储中。
- 为身份指定一个在本次会话中唯一的名称。保存会话密钥的密钥环条目由服务
icp-cli和身份名称共同标识;该密钥在整个 OS 用户范围内共享——设置ICP_HOME并不能将其隔离。两个并发 Agent 若以相同名称关联 Web 身份,就会相互覆盖会话密钥。请使用 Agent 和应用名称作为前缀(例如claude-oisy-<short-id>);更全面的隔离说明,请参阅icp-cli技能中的“并行 Agent 和工作树”。 - 尽量减少输出。步骤成功时,不要回显命令输出或叙述进度——只向用户展示必要内容:登录 URL、确认请求、错误以及最终验证的主体。
- 完成后,不要主动删除已关联的身份——用户可能想在后续调用中继续使用仍有效的委派。询问用户是否要清理,并且仅在用户确认后运行
icp identity delete <NAME>(子命令是delete;在 0.3.x 中,remove不是有效的子命令)。如果用户拒绝,只需提醒他们身份名称以及之后可用于删除身份的命令。 - 在会话开始时,如果
icp identity list显示累积了过去会话遗留的、以 Agent 名称为前缀的身份(例如多个claude-oisy-*条目),请在创建新身份前提出删除这些身份。向用户列出这些身份,等待确认,并仅使用icp identity delete <NAME>删除获批的条目——绝不批量删除,也绝不在未经确认的情况下删除。