Skip to content

docs(internet-identity): cover local II minting with auth 10 - #404

Open
marc0olo wants to merge 4 commits into
mainfrom
docs/internet-identity-local-ii-auth10
Open

marc0olo wants to merge 4 commits into
mainfrom
docs/internet-identity-local-ii-auth10

Conversation

@marc0olo

@marc0olo marc0olo commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

The skill's local-II fallback didn't work with @icp-sdk/auth 10: it set only identityProvider, so sign-in against a local II (ii: true) failed after the ceremony. Found while migrating dfinity/examples to auth 10 (dfinity/examples#1487), where we keep local II.

Verification

Checked against the @icp-sdk/auth 10.0.0 source and reproduced in a browser against a local network:

  • The client mints app delegations itself via app_prepare_delegation / app_get_delegation on the II canister (session-minter.ts), through an HttpAgent built from agentOptions. Its default host already resolves to the page origin on localhost, so the mint reaches the local replica, but without the local root key it fails with TrustError: Certificate verification error ("Invalid signature"). Reproduced both: no agentOptions → that error; agentOptions: { rootKey } only → sign-in, authenticated call, reload and logout work. host is not needed.
  • The II in older network launchers lacks those methods (checked the local II's candid:service); the launcher shipping PocketIC 2026-09-18 has them. icp network update fetches it.
  • The delegation is short-lived and rotated by SessionIdentity, so a logout timer set from its expiration fires after minutes.

The subscribe() / getIdentity() pitfall from the first revision was dropped: the ordering it described is fixed in dfinity/icp-js-auth#198 (patch release pending), so the skill does not document it.

Changes

  • skills/internet-identity/SKILL.md — local-II fallback: agentOptions + snippet, and the icp network update requirement; pitfalls 18–19 (local II without agentOptions, delegation-expiry logout timers).
  • skills/internet-identity/SKILL.md — the createAuthenticatedActor example no longer sets host: window.location.origin (it breaks custom domains and contradicted the new guidance).
  • evaluations/internet-identity.json — new adversarial cases 27–28, one per pitfall. Case 4 no longer accepts host: window.location.origin (WITH 4/5 | WITHOUT 3/5; the remaining miss, getIdentity(), predates this change and contradicts the prompt).

Evals

Cases 27–28 are new, so they are run with baseline. Cases 2 and 12 cover the local-II section and were re-run as a regression check (with skill only). The other cases were not re-run — none assert what changed. Re-checked on the final commit (70c4c61): cases 2, 12 and 28 still pass with the skill (4/4, 3/3, 4/4); case 27's content is unchanged since its run below.

Case 27 was updated after review (root key only, no host), and its mainnet-II check was split out and scoped to the fix: the skill currently recommends mainnet II by default, so some with-skill runs add it as an aside, which the old wording counted as a failure. Before rescoping, 4 runs gave 2/4, 4/4, 4/4, 3/4, all misses from that aside or run variance. Two earlier baseline runs timed out; the result below is the first valid baseline.

Eval 27 — Adversarial: local II sign-in without agentOptions — WITH 5/5 | WITHOUT 2/5
  WITH skill: 5/5 passed
    ✅ Adds agentOptions to the AuthClient constructor with the root key from the ic_env cookie (IC_ROOT_KEY)
    ✅ Explains that the client mints its delegations by calling the II canister and that, without the local root key, it verifies the local replica's response against the mainnet root key and fails
    ✅ Mentions that an outdated local II may lack the minting methods, fixed by running `icp network update` and restarting the network
    ✅ Does NOT set host (e.g. window.location.origin) in agentOptions, and does NOT suggest fetchRootKey() or shouldFetchRootKey
    ✅ Fixes the local II setup rather than replacing it: switching to mainnet II is NOT presented as the fix (mentioning it as the default alternative is fine)

  WITHOUT skill: 2/5 passed
    ❌ Adds agentOptions to the AuthClient constructor with the root key from the ic_env cookie (IC_ROOT_KEY)
       → The output never mentions agentOptions or IC_ROOT_KEY at all; it focuses entirely on a fabricated constructor/API misuse issue.
    ❌ Explains that the client mints its delegations by calling the II canister and that, without the local root key, it verifies the local replica's response against the mainnet root key and fails
       → No mention of delegation minting, root key verification, or mainnet vs local root key mismatch anywhere in the output.
    ❌ Mentions that an outdated local II may lack the minting methods, fixed by running `icp network update` and restarting the network
       → The output never discusses outdated local II canister versions or the `icp network update` remedy.
    ✅ Does NOT set host (e.g. window.location.origin) in agentOptions, and does NOT suggest fetchRootKey() or shouldFetchRootKey
    ✅ Fixes the local II setup rather than replacing it: switching to mainnet II is NOT presented as the fix
Eval 28 — Adversarial: logout timer from the delegation expiration — WITH 4/4 | WITHOUT 1/4
  WITH skill: 4/4 passed
    ✅ Explains that in 9.x and later the delegation getIdentity() signs with is short-lived and replaced by the client as it ages, so its expiration is not the end of the session
    ✅ Removes the timer
    ✅ Reacts to the session ending through authClient.subscribe(), checking isAuthenticated() or an 'expired' status from getStatus()
    ✅ Does NOT suggest re-scheduling the timer whenever the delegation is refreshed

  WITHOUT skill: 1/4 passed
    ❌ Explains that in 9.x and later the delegation getIdentity() signs with is short-lived and replaced by the client as it ages, so its expiration is not the end of the session
       → The output attributes the early logout entirely to the default IdleManager and never mentions that the delegation itself is short-lived and rotated by the client.
    ❌ Removes the timer
       → Both fixes explicitly retain the user's own setTimeout-based delegation-expiration timer ('keep your own delegation-expiration timer as the sole logout trigger') rather than removing it.
    ❌ Reacts to the session ending through authClient.subscribe(), checking isAuthenticated() or an 'expired' status from getStatus()
       → The output never mentions authClient.subscribe(), isAuthenticated(), or getStatus(); it only proposes idleOptions configuration.
    ✅ Does NOT suggest re-scheduling the timer whenever the delegation is refreshed
Eval 2 — Local II URL — WITH 4/4 | WITHOUT skipped
  WITH skill: 4/4 passed
    ✅ identityProvider is an object with both authorizeUrl and canisterId, not a bare URL string
    ✅ Local authorizeUrl uses the well-known II frontend alias: http://id.ai.localhost:8000/authorize
    ✅ canisterId is the well-known II backend canister, rdmx6-jaaaa-aaaaa-aaadq-cai
    ✅ Does NOT suggest dynamic lookup via ic_env cookie, environment variables, or dfx commands for the II canister ID
Eval 12 — local vs mainnet II usage — WITH 3/3 | WITHOUT skipped
  WITH skill: 3/3 passed
    ✅ Suggest using mainnet's id.ai because the network launcher after icp-cli 0.2.4 supports verifying mainnet signatures
    ✅ Explains that a local instance of internet identity can be deployed by setting `ii: true` in icp.yaml
    ✅ Explains that the local instance of internet identity is accessible at http://id.ai.localhost:8000

- Local II needs agentOptions (host + root key): the client mints delegations
  via the II canister and its agent defaults to mainnet
- Local II must come from a recent network launcher (`icp network update`)
- Pitfalls: getIdentity() inside a subscribe() listener, and logout timers
  set from the short-lived delegation's expiration
- Evals 27-29 for the new pitfalls
@marc0olo
marc0olo requested review from a team and JoshDFN as code owners September 23, 2026 10:55
@marc0olo
marc0olo requested a review from sea-snake September 23, 2026 10:55
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Skill Validation Report

Validating skill: /home/runner/work/icskills/icskills/skills/internet-identity

Structure

  • Pass: SKILL.md found

Frontmatter

  • Pass: name: "internet-identity" (valid)
  • Pass: description: (440 chars)
  • Pass: license: "Apache-2.0"
  • Pass: compatibility: (45 chars)
  • Pass: metadata: (2 entries)

Tokens

  • Warning: SKILL.md body is 12036 tokens (spec recommends < 5000)
  • Warning: SKILL.md body is 760 lines (spec recommends < 500)

Markdown

  • Pass: no unclosed code fences found

Tokens

File Tokens
SKILL.md body 12,036
Total 12,036

Content Analysis

Metric Value
Word count 7,034
Code block ratio 0.25
Imperative ratio 0.06
Information density 0.16
Instruction specificity 0.89
Sections 18
List items 57
Code blocks 22

Contamination Analysis

Metric Value
Contamination level high
Contamination score 0.62
Primary language category javascript
Scope breadth 4
  • Warning: Language mismatch: config, systems (2 categories differ from primary)
  • Multi-interface tool detected: aws

Result: 2 warnings

Project Checks


✓ Project checks passed for 1 skills (0 warnings)

Comment thread skills/internet-identity/SKILL.md Outdated
Comment thread skills/internet-identity/SKILL.md Outdated
The auth client's agent already resolves to the page origin on localhost;
without the local root key the mint fails certificate verification.
Eval 27 updated accordingly, and its mainnet-II check scoped to the fix.
sea-snake added a commit to dfinity/icp-js-auth that referenced this pull request Sep 23, 2026
…gn-in it was told about (#198)

A `subscribe()` listener that calls `getIdentity()` on a `signed-in`
status gets `SessionNotHeldError`. The listener runs when the record
changes, and for a sign-in that happens before the client installs the
identity:

- **This tab's sign-in:** `signIn()` writes the record in
`#persistSession`, and only installs the identity after promoting the
app credential and opening the session.
- **A peer's sign-in:** the subscription ran the listeners before
`#restoreAgain()`, so the listener's `getIdentity()` awaited the
previous, already settled restore.

Changes:

- `signIn()` wraps the write-and-install step in a `#committing`
promise, and `getIdentity()` waits for it after `#init()`. Only that
step counts, so a `getIdentity()` while the signer window is open does
not wait on the user. A ceremony failing partway still ends in
`SessionNotHeldError`.
- The subscription reads the status, queues the restore (same guard),
and then notifies. `getStatus()` still returns the new record inside a
listener.

Tests: two regression tests, one per case. Each fails with
`SessionNotHeldError` without its half of the fix. Full suite, typecheck
and biome pass.

Reported in
dfinity/icskills#404 (comment).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@marc0olo
marc0olo requested a review from sea-snake September 23, 2026 14:32
@marc0olo
marc0olo enabled auto-merge (squash) September 23, 2026 14:33
@marc0olo

Copy link
Copy Markdown
Member Author

Pushed 2a001a1: the createAuthenticatedActor example still set host: window.location.origin, contradicting this PR's own "leave host unset" guidance (pitfall 18, local II section). On a custom domain that host breaks canister calls, because the domain serves only the HTTP gateway, not /api/v2 (see custom-domains pitfall 8). With host omitted, the agent resolves to the page origin on known gateway hosts and to https://icp-api.io elsewhere.

Eval case 4 (Authenticated actor creation) accepted host: window.location.origin as correct; it now requires host to be left unset.

Eval: case 4 (changed)

WITH 4/5 | WITHOUT 3/5

  • The changed expectation passes with the skill and fails without it (the baseline hardcodes host: "https://icp0.io" and suggests fetchRootKey).
  • The one failure with the skill, "Calls await authClient.getIdentity()", predates this change: the prompt asks for a function that takes an identity, so the expectation contradicts it. It also fails in the baseline.

Found while working on #405 (icp-cli createActor / host guidance).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants