You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(internet-identity): use mo:core/CallerAttributes on the Motoko path
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.
Copy file name to clipboardExpand all lines: evaluations/internet-identity.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -87,7 +87,7 @@
87
87
"Requests verified_email (not just email) for access-gating use cases",
88
88
"Explains that the email key is the raw value from the user's II-linked account and is NOT checked by II (treat as user-supplied input)",
89
89
"Explains that verified_email is only present when the source OpenID provider (e.g. Google) marked the email as verified and II surfaced that signal — that is what makes verified_email trustworthy for authorisation",
90
-
"On the backend, looks up implicit:nonce, implicit:origin, signer (against rdmx6-jaaaa-aaaaa-aaadq-cai), and the verified_email attribute key (or the openid:<provider>:verified_email scoped variant) before the allowlist check",
90
+
"On the backend, verifies the bundle signer is the Internet Identity backend canister (`rdmx6-jaaaa-aaaaa-aaadq-cai`) — either via an explicit `msg_caller_info_signer()` check (Rust) or via `mo:core/CallerAttributes` with the `trusted_attribute_signers` env var configured (Motoko) — and looks up implicit:nonce, implicit:origin, and the verified_email attribute key (or the openid:<provider>:verified_email scoped variant) before the allowlist check",
91
91
"Does NOT recommend reading the email field as a substitute for verified_email"
Copy file name to clipboardExpand all lines: skills/internet-identity/SKILL.md
+65-41Lines changed: 65 additions & 41 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,7 @@ Internet Identity (II) is the Internet Computer's native authentication system.
17
17
## Prerequisites
18
18
19
19
-`@icp-sdk/auth` (>= 7.0.0), `@icp-sdk/core` (>= 5.3.0) (`AttributesIdentity` was added in core v5.3.0)
20
+
- For the Motoko backend example: `mo:core` >= 2.5.0 (the `CallerAttributes` module that wraps the caller-info primitives behind a single trusted-signer-aware call)
20
21
21
22
## Canister IDs
22
23
@@ -43,7 +44,9 @@ Internet Identity (II) is the Internet Computer's native authentication system.
43
44
44
45
8.**Generating the attribute nonce on the frontend.** The nonce passed to `requestAttributes` MUST come from a backend canister call. A frontend-generated nonce defeats replay protection: the canister cannot verify that the bundle's `implicit:nonce` matches an action it actually started. Have the backend mint and return the nonce from a `registerBegin`-style method, and check it against the bundle's implicit fields when the user calls the protected method.
45
46
46
-
9.**Reading attribute data without verifying the signer.**`msg_caller_info_data` (Rust) and `Prim.callerInfoData` (Motoko) return whatever bundle the caller provided. The IC verifies the signature, not the identity of the signer — any canister can produce a valid bundle. Check `msg_caller_info_signer` / `Prim.callerInfoSigner` against `rdmx6-jaaaa-aaaaa-aaadq-cai` (Internet Identity) before trusting any attribute, otherwise an attacker canister can forge attributes like `email = "admin@you.com"`.
47
+
9.**Reading attribute data without verifying the signer.** The IC verifies the signature, not the identity of the signer — any canister can produce a valid bundle. The trusted signer is `rdmx6-jaaaa-aaaaa-aaadq-cai` (Internet Identity). The check looks different per language:
48
+
-**Motoko**: prefer `mo:core/CallerAttributes`. `CallerAttributes.getAttributes<system>()` returns `?Blob` and traps if the signer isn't listed in the canister's `trusted_attribute_signers` env var. Configure that env var in `icp.yaml` (see "Backend: Reading Identity Attributes"). Don't roll your own check on top of `Prim.callerInfoSigner` unless you have a reason to.
49
+
-**Rust**: there is no CDK wrapper yet. Always check `msg_caller_info_signer()` against the trusted issuer principal before reading `msg_caller_info_data()`. Skipping this lets an attacker canister forge attributes like `email = "admin@you.com"`.
47
50
48
51
10.**Substituting `{tid}` in the Microsoft scoped-key prefix.** The `microsoft` OpenID provider URL is the literal string `https://login.microsoftonline.com/{tid}/v2.0` — `{tid}` is part of the URL, not a tenant-ID placeholder you fill in. Bundle keys returned by `scopedKeys({ openIdProvider: 'microsoft' })` look like `openid:https://login.microsoftonline.com/{tid}/v2.0:email` exactly, and the backend must look up that literal key. Replacing `{tid}` with a tenant GUID will silently miss every attribute lookup.
49
52
@@ -249,42 +252,64 @@ const authClient = new AuthClient({
249
252
openIdProvider: "google",
250
253
});
251
254
252
-
const nonce = await backend.registerBegin();
255
+
// Wrap the flow in an async function so this code works with any bundler
256
+
// target (Vite defaults to es2020 which lacks top-level await).
257
+
async function registerWithGoogle(backend, appCanisterId, appIdl) {
- **Motoko** (compiler with caller_info prims, e.g. >= 0.16): `Prim.callerInfoData<system>() : Blob`, `Prim.callerInfoSigner<system>() : Blob` (empty when no signer).
291
+
- **Rust** (ic-cdk >= 0.20.1): `ic_cdk::api::msg_caller_info_data() -> Vec<u8>`, `ic_cdk::api::msg_caller_info_signer() -> Option<Principal>`. There is no CDK wrapper for the trusted-signer check yet; do it explicitly in your code.
292
+
- **Motoko** (mo:core >= 2.5.0): `CallerAttributes.getAttributes<system>() : ?Blob` from `mo:core/CallerAttributes`. The wrapper returns `null` when no attributes are attached and **traps** when the signer isn't listed in the canister's `trusted_attribute_signers` env var, so you don't write the signer check yourself. Underlying primitives `Prim.callerInfoData<system>` / `Prim.callerInfoSigner<system>` are still exposed by the compiler but the wrapper is preferred.
293
+
294
+
**Always verify the signer.** The IC checks that the bundle is signed; it does not check *who* signed it. Any canister can produce a valid bundle. Trust the data only when the signer matches the trusted issuer (`rdmx6-jaaaa-aaaaa-aaadq-cai` for Internet Identity). Motoko handles this for you via the env var; Rust requires an explicit `msg_caller_info_signer()` check.
286
295
287
-
**Always verify the signer first.** The IC checks that the bundle is signed; it does not check *who* signed it. Any canister can produce a valid bundle. Trust the data only when the signer matches the trusted issuer (`rdmx6-jaaaa-aaaaa-aaadq-cai` for Internet Identity).
`CallerAttributes.getAttributes` reads the trusted signer list from the canister's `trusted_attribute_signers` environment variable (a comma-separated list of principal texts). Set it in your `icp.yaml` so `icp deploy` configures the canister automatically:
299
+
300
+
```yaml
301
+
canisters:
302
+
- name: backend
303
+
settings:
304
+
environment_variables:
305
+
# Mainnet II principal. List both the mainnet principal and your local II
306
+
# canister principal if your tests run against a locally deployed II.
If the env var is unset, `getAttributes` traps with `"trusted_attribute_signers environment variable is not set"`. That trap is the right behavior: an unconfigured canister should not trust attribute bundles.
311
+
312
+
#### Reading the bundle
288
313
289
314
The data is Candid-encoded as an ICRC-3 `Value::Map` whose entries are:
290
315
- `implicit:nonce`(Blob) — must match a nonce your canister minted for this user/action.
@@ -294,13 +319,12 @@ The data is Candid-encoded as an ICRC-3 `Value::Map` whose entries are:
294
319
- OpenID-scoped keys (e.g., `"openid:https://accounts.google.com:email"`) when `scopedKeys` was used on the frontend.
0 commit comments