feat(internet-identity): update for @icp-sdk/auth v7 API and identity attributes#182
feat(internet-identity): update for @icp-sdk/auth v7 API and identity attributes#182
Conversation
Skill Validation ReportValidating skill: /home/runner/work/icskills/icskills/skills/internet-identityStructure
Frontmatter
Tokens
Markdown
Tokens
Content Analysis
Contamination Analysis
Result: 1 warning Project Checks |
There was a problem hiding this comment.
The frontend API migration and the new identity-attributes section are solid and consistent with the merged developer-docs PR #193. A few things worth addressing before merging:
PR title mismatch
Title says "@icp-sdk/auth v6 API" but the compatibility field is bumped to >= 7.0.0, which matches what the developer-docs PR calls it. Minor, but worth correcting so the history is clear.
@icp-sdk/core version not bumped
Prerequisites updates @icp-sdk/auth to >= 7.0.0 but leaves @icp-sdk/core at >= 5.0.0. The new example imports AttributesIdentity from @icp-sdk/core/identity — if that export requires a newer core release, agents following this skill will hit a missing-module error. Worth confirming the minimum is still 5.0.0.
Missing maxTimeToLive in registerWithEmail
The base signIn() example sets an 8-hour expiry; the registerWithEmail example calls authClient.signIn() with no options. Agents copying that pattern will use whatever the default delegation lifetime is. Should be consistent, or the omission explicitly noted.
OpenID scopedKeys example is incomplete
The snippet shows how to start the sign-in + attribute request in parallel for OpenID but doesn't show awaiting the promises or consuming the results. Compared to the full registerWithEmail example above it, this leaves agents guessing how to finish the flow.
Nonce verification is "omitted" in both backend examples
Mistake #8 makes a strong case that the nonce check is security-critical, but both registerFinish examples have // (omitted) where the check should be. That teaches agents to skip it. Even a minimal if (nonce != expected_nonce) { trap } stub would help.
Evaluations
Mistakes #8 and #9 are exactly the adversarial, non-obvious pitfalls the eval suite is designed to catch — an agent without the skill would almost certainly get both wrong. Two new output evals would be appropriate:
- Mistake
#8: agent is asked to implementrequestAttributeswith a frontend-generated nonce → must flag/reject that approach - Mistake
#9: agent is asked to readmsg_caller_info_datain Rust orPrim.callerInfoDatain Motoko → must verify the signer before reading the data
Also, eval #4 ("Authenticated actor creation") has the expected behavior "Gets the identity from authClient.getIdentity()" — now that getIdentity() is async it should read "Calls await authClient.getIdentity()". And eval #5's expected behaviors should reflect the updated failure modes from Mistake #5 (constructor-passed identityProvider, unhandled signIn() rejection).
- bump @icp-sdk/core prerequisite to >= 5.3.0 (AttributesIdentity was introduced in core v5.3.0) - pass maxTimeToLive to signIn() in registerWithEmail for consistency with the other examples - complete the OpenID scopedKeys example through AttributesIdentity wrapping and the protected-method call - replace omitted nonce/timestamp checks with explicit lookup helpers and comparisons in both Motoko and Rust register_finish examples - add evals for Mistake 8 (frontend-generated nonce) and Mistake 9 (reading attribute data without verifying signer) - update eval 4 expected behavior to reflect async getIdentity() - update eval 5 (anonymous principal) for the v6+ failure modes
|
Feedback addressed in 95c5376:
New eval results (with-skill vs baseline)Both new adversarial evals show a strong skill-vs-baseline gap, exactly the pattern the eval suite is designed to catch. Eval 6 — Adversarial: frontend-generated attribute nonce
Eval 7 — Adversarial: reading attribute data without verifying signer
|
…s verified_email
- list the unscoped attribute keys (name, email, verified_email) with
guidance on when to use each: email for soft uses (mailing lists,
contact), verified_email for access gating (admin allowlists)
- document scopedKeys() resolution including the literal Microsoft
provider URL (the {tid} segment is part of the URL, not a tenant
placeholder)
- add Mistake 10: substituting {tid} into the Microsoft URL silently
breaks attribute lookups
- add Mistake 11: treating email as verified — only verified_email
carries the source provider's verification signal
- new evals: Microsoft tid substitution, email-vs-verified_email for
access gating
|
Follow-up in 65a1dd7: agents had no way to know which attribute keys exist or which to pick. Added an "Available attribute keys" subsection that lists Two new pitfalls:
New eval results (with-skill vs baseline)Eval 8 — Adversarial: substituting tid in the Microsoft scoped-key URL
Eval 9 — Adversarial: using email instead of verified_email for access gating
|
- table column renamed to "What it IS" and rows lead with the explicit definition (raw vs verified) so agents echo the distinction in their answers - Mistake 11 split into two bullets that define each key independently - eval 9 expected behaviour split: agents now have to articulate the email definition AND the verified_email definition separately eval 9 now passes 5/5 with-skill (was 3/4) vs 1/5 baseline
|
fc65fef tightens the Updated eval 9 resultEval 9 — Adversarial: using email instead of verified_email for access gating
|
marc0olo
left a comment
There was a problem hiding this comment.
Follow-up on the latest commits — the original six concerns are all cleanly resolved and the eval additions are excellent. Three issues in the new code before this is merge-ready:
OpenID scopedKeys example: bare await at module scope
The entire code block sits at module level with multiple top-level await calls (backend.registerBegin(), signInPromise, attributesPromise, HttpAgent.create()). That triggers the exact Vite es2020 error that eval #6 already tests for. Needs the same async function wrapper as registerWithEmail.
Motoko: pendingNonces referenced as live code but declared only in comments
// var pendingNonces : PendingNonces = ...; ← commented out
...
let ?expected = pendingNonces.remove(caller) ... ← live codeWon't compile. The Rust version handles this cleanly with an explicit consume_pending_nonce stub + unimplemented!(). Motoko should do the same — a func consumePendingNonce(caller : Principal) : ?Blob { /* see Storing the nonce */ } stub.
Motoko: Nat64.toNat(Prim.time()) — use Time.now() from mo:core/Time instead
Nat64 is not imported, so Nat64.toNat won't compile. But more broadly: since mo:prim should only be used where no mo:core equivalent exists, prefer Time.now() from mo:core/Time for the timestamp. Time.now() returns Int (nanoseconds), and since Nat <: Int in Motoko the freshness comparison works directly without any conversion:
import Time "mo:core/Time";
...
let nowNs : Int = Time.now();
if (nowNs > issuedAt + 300_000_000_000) Runtime.trap("Bundle too old");Prim is still required — callerInfoData<system> and callerInfoSigner<system> have no mo:core equivalent yet. Worth adding a short comment to that effect so future readers don't try to remove the import:
import Prim "mo:prim"; // callerInfoData / callerInfoSigner not yet in mo:core|
One more finding that affects the Motoko backend example directly:
caffeinelabs/motoko-core#491 added a import CallerAttributes "mo:core/CallerAttributes";
// ...
let ?data = CallerAttributes.getAttributes<system>() else Runtime.trap("no trusted attributes");The signer check is baked in — canisters:
backend:
# ...
env:
trusted_attribute_signers: "rdmx6-jaaaa-aaaaa-aaadq-cai"This resolves the Also note this bumps the minimum |
|
Feedback addressed in 6a8a4cc: Motoko: switch to
canisters:
- name: backend
settings:
environment_variables:
trusted_attribute_signers: "rdmx6-jaaaa-aaaaa-aaadq-cai"With a note that an unset env var traps Prerequisites bumped to
Mistake 9 split per language to make the asymmetry explicit:
Companion fixes folded in while the Motoko example was being rewritten:
Eval 9 updated so the expected behavior accepts either the explicit Rust signer check or the Motoko env-var-based check (otherwise a correct Motoko answer using Companion PR for developer-docs with the same Motoko swap: dfinity/developer-docs#207
|
…kend example (#207) ## Summary Companion to dfinity/icskills#182. Updates the Motoko backend example on the Internet Identity guide to use `mo:core/CallerAttributes` (motoko-core v2.5.0+) instead of the manual `Prim.callerInfoSigner` / `Prim.callerInfoData` dance. - `CallerAttributes.getAttributes<system>() : ?Blob` returns the bundle and traps when the signer isn't listed in the canister's `trusted_attribute_signers` env var. The hardcoded II principal moves out of code and into deploy config. - Drops the `mo:prim` import on the Motoko path (the wrapper handles primitives + signer comparison). - Adds an `icp.yaml` `settings.environment_variables` snippet for declaring `trusted_attribute_signers`. - Splits the per-language intro and the 'common mistakes' bullet so the Rust path (still requires explicit `msg_caller_info_signer()` check, no CDK wrapper yet) stays distinct from Motoko. `npm run build` passes; no new agent-docs warnings on the affected file. ## Sync recommendation informed by caffeinelabs/motoko-core (`src/CallerAttributes.mo`); dfinity/icp-cli (`docs/reference/canister-settings.md#environment_variables`); dfinity/icskills (`skills/internet-identity/SKILL.md` companion PR #182)
Replaces the manual Prim.callerInfoSigner / Prim.callerInfoData dance with CallerAttributes.getAttributes<system>() from mo:core (>= 2.5.0). The wrapper bakes in the trusted-signer check via the canister's trusted_attribute_signers env var, so the example no longer hardcodes the II principal in code: it moves to icp.yaml as deploy-time config. Notable changes: - Motoko example now imports mo:core/CallerAttributes (no more mo:prim) and reads time via mo:core/Time (Time.now() : Int) instead of the broken Nat64.toNat(Prim.time()) which had no Nat64 import. - consumePendingNonce stub mirrors the Rust register_finish pattern so the example compiles standalone. - New "Configuring trusted_attribute_signers" subsection shows the icp.yaml settings.environment_variables snippet. - Mistake #9 split per language: Motoko points at the env-var-based check, Rust still requires explicit msg_caller_info_signer. - Prerequisites bumps mo:core minimum to >= 2.5.0. - OpenID scopedKeys example wrapped in an async function to avoid bare top-level await at module scope (fixes the same Vite es2020 failure mode eval #6 already covers). - Eval #9 expected behavior accepts either the explicit Rust signer check or the Motoko env-var check. Rust path is unchanged: there is no ic-cdk wrapper yet.
Summary
Brings the
internet-identityskill in line with the latest@icp-sdk/auth(v6) and the new system APIs for verified identity attributes.Frontend (
@icp-sdk/authv6):AuthClient.create()becomesnew AuthClient(options);identityProvideris now passed to the constructor.login({ onSuccess, onError })becomes promise-basedawait signIn({ maxTimeToLive })returning theIdentitydirectly.isAuthenticated()is now sync;getIdentity()is now async.openIdProvider: 'google' | 'apple' | 'microsoft'.requestAttributes({ keys, nonce })flow withAttributesIdentityfor sending signed attribute bundles alongside canister calls.Backend (new
msg_caller_info_*system APIs):ic_cdk::api::msg_caller_info_data()/msg_caller_info_signer()(ic-cdk >= 0.20.1).Prim.callerInfoData<system>()/Prim.callerInfoSigner<system>().rdmx6-jaaaa-aaaaa-aaadq-cai(Internet Identity) before trusting the bundle: the IC checks the signature, not who signed it.Value::Mapwith implicit fields (implicit:nonce,implicit:origin,implicit:issued_at_timestamp_ns) plus the requested attribute keys.Pitfalls added:
onSuccess/onError).Code example rewritten end-to-end against the new API.
npm run validatepasses (warnings only, all pre-existing missing-evaluations warnings unrelated to this change).Companion PR: dfinity/developer-docs#189 updates the consumer page that this skill backs.