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
docs(authentication): use mo:core/CallerAttributes for the Motoko backend 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)
When the frontend wraps an identity with `AttributesIdentity`, every call carries a verified attribute bundle. Read it on the backend with `msg_caller_info_data` (Rust) or `Prim.callerInfoData` (Motoko). Always verify the signer first: by itself the bundle is signed by *some* canister, and any canister could have signed an arbitrary one. Trust it only when the signer is the Internet Identity backend (`rdmx6-jaaaa-aaaaa-aaadq-cai`).
318
+
When the frontend wraps an identity with `AttributesIdentity`, every call carries a verified attribute bundle. The IC checks that the bundle is signed; it does not check *who* signed it, and any canister could have signed an arbitrary one. Trust the bundle only when the signer is the Internet Identity backend (`rdmx6-jaaaa-aaaaa-aaadq-cai`).
319
+
320
+
How that check is wired depends on the language:
321
+
322
+
- **Motoko (mo:core >= 2.5.0)**: `CallerAttributes.getAttributes<system>()` from `mo:core/CallerAttributes` returns the bundle as `?Blob` and traps when the signer is not listed in the canister's `trusted_attribute_signers` environment variable. Configure the env var in your `icp.yaml` (see below) and the trusted-signer check happens automatically.
323
+
- **Rust (ic-cdk >= 0.20.1)**: `ic_cdk::api::msg_caller_info_data() -> Vec<u8>` returns the raw bundle and `ic_cdk::api::msg_caller_info_signer() -> Option<Principal>` returns the signer. There is no CDK wrapper for the trusted-signer check yet, so check the signer explicitly before reading the data.
324
+
325
+
For Motoko, declare the trusted signer in your `icp.yaml`. The value is a comma-separated list of principal texts, so list both your local and mainnet II principals if your tests run against a locally deployed II:
If the env var is unset, `getAttributes` traps. That is the correct behavior: an unconfigured canister should not trust any attribute bundles.
319
336
320
337
The bundle is Candid-encoded as an [ICRC-3 Value](../../references/internet-identity-spec.md) `Map` with three implicit fields plus the keys you requested:
321
338
@@ -328,13 +345,11 @@ The bundle is Candid-encoded as an [ICRC-3 Value](../../references/internet-iden
328
345
<TabItem label="Motoko">
329
346
330
347
```motoko
331
-
importPrim"mo:prim";
348
+
importCallerAttributes"mo:core/CallerAttributes";
332
349
import Principal "mo:core/Principal";
333
350
import Runtime "mo:core/Runtime";
334
351
335
352
persistent actor {
336
-
let iiPrincipal =Principal.fromText("rdmx6-jaaaa-aaaaa-aaadq-cai");
337
-
338
353
type Icrc3Value = {
339
354
#Nat : Nat;
340
355
#Int : Int;
@@ -353,13 +368,10 @@ persistent actor {
353
368
null;
354
369
};
355
370
356
-
// Returns the verified attribute map, trapping if the signer is not II.
371
+
// Returns the verified attribute map. Traps when the signer is not
372
+
// listed in the canister's trusted_attribute_signers env var.
357
373
func iiAttributes() : [(Text, Icrc3Value)] {
358
-
let signer =Prim.callerInfoSigner<system>();
359
-
if (signer.size() ==0 or Principal.fromBlob(signer) != iiPrincipal) {
let #Map(entries) = value elseRuntime.trap("expected attribute map");
365
377
entries
@@ -538,7 +550,7 @@ For full details, see the [Internet Identity specification](../../references/int
538
550
- **Using `shouldFetchRootKey: true` in browser code**: pass `rootKey: canisterEnv?.IC_ROOT_KEY` from `safeGetCanisterEnv()` instead. `shouldFetchRootKey: true` fetches the root key from the replica at runtime, which lets a man-in-the-middle substitute a fake key on mainnet. For Node.js scripts targeting a local replica only, `await agent.fetchRootKey()` is acceptable: but never on mainnet.
539
551
- **Creating multiple `AuthClient` instances**: create one on page load and reuse it. Multiple instances cause race conditions with session storage.
540
552
- **Generating the attribute nonce on the frontend**: a frontend-generated nonce defeats the anti-replay guarantee. The nonce passed to `requestAttributes` must come from a backend canister call so the canister can later verify that the bundle's `implicit:nonce` matches an action it actually started.
541
-
- **Reading attribute data without verifying the signer**: `msg_caller_info_data` (`Prim.callerInfoData` in Motoko) returns whatever bundle the caller provided. The IC system checks the signature, not the identity of the signer. If you skip the `msg_caller_info_signer` check (or compare it against the wrong principal), any canister can mint its own bundle and your method will read attacker-controlled values. Verify the signer matches `rdmx6-jaaaa-aaaaa-aaadq-cai` (Internet Identity) before trusting the bundle.
553
+
- **Reading attribute data without verifying the signer**: the IC checks the signature, not the identity of the signer, so any canister can produce a valid bundle. The trusted signer for II is `rdmx6-jaaaa-aaaaa-aaadq-cai`. In Motoko, use `CallerAttributes.getAttributes<system>()` from `mo:core/CallerAttributes` and configure the `trusted_attribute_signers` env var in `icp.yaml`: the wrapper traps when an untrusted signer is detected. In Rust, there is no CDK wrapper yet, so always check `msg_caller_info_signer()` against the trusted issuer before reading `msg_caller_info_data()`.
542
554
543
555
## Next steps
544
556
@@ -550,4 +562,4 @@ For full details, see the [Internet Identity specification](../../references/int
550
562
551
563
{/* TODO: Add Unity native app integration via deep links: see portal native-apps/unity_ii_* */}
0 commit comments