Skip to content

Commit c9a6f59

Browse files
authored
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)
1 parent d831777 commit c9a6f59

1 file changed

Lines changed: 24 additions & 12 deletions

File tree

docs/guides/authentication/internet-identity.mdx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,24 @@ async fn protected_async_action() -> String {
315315
316316
### Read identity attributes
317317
318-
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:
326+
327+
```yaml
328+
canisters:
329+
- name: backend
330+
settings:
331+
environment_variables:
332+
trusted_attribute_signers: "rdmx6-jaaaa-aaaaa-aaadq-cai"
333+
```
334+
335+
If the env var is unset, `getAttributes` traps. That is the correct behavior: an unconfigured canister should not trust any attribute bundles.
319336
320337
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:
321338
@@ -328,13 +345,11 @@ The bundle is Candid-encoded as an [ICRC-3 Value](../../references/internet-iden
328345
<TabItem label="Motoko">
329346
330347
```motoko
331-
import Prim "mo:prim";
348+
import CallerAttributes "mo:core/CallerAttributes";
332349
import Principal "mo:core/Principal";
333350
import Runtime "mo:core/Runtime";
334351

335352
persistent actor {
336-
let iiPrincipal = Principal.fromText("rdmx6-jaaaa-aaaaa-aaadq-cai");
337-
338353
type Icrc3Value = {
339354
#Nat : Nat;
340355
#Int : Int;
@@ -353,13 +368,10 @@ persistent actor {
353368
null;
354369
};
355370

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.
357373
func iiAttributes() : [(Text, Icrc3Value)] {
358-
let signer = Prim.callerInfoSigner<system>();
359-
if (signer.size() == 0 or Principal.fromBlob(signer) != iiPrincipal) {
360-
Runtime.trap("Untrusted attribute signer");
361-
};
362-
let data = Prim.callerInfoData<system>();
374+
let ?data = CallerAttributes.getAttributes<system>() else Runtime.trap("no trusted attributes");
363375
let ?value : ?Icrc3Value = from_candid (data) else Runtime.trap("invalid attribute bundle");
364376
let #Map(entries) = value else Runtime.trap("expected attribute map");
365377
entries
@@ -538,7 +550,7 @@ For full details, see the [Internet Identity specification](../../references/int
538550
- **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.
539551
- **Creating multiple `AuthClient` instances**: create one on page load and reuse it. Multiple instances cause race conditions with session storage.
540552
- **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()`.
542554

543555
## Next steps
544556

@@ -550,4 +562,4 @@ For full details, see the [Internet Identity specification](../../references/int
550562

551563
{/* TODO: Add Unity native app integration via deep links: see portal native-apps/unity_ii_* */}
552564

553-
{/* Upstream: informed by dfinity/portal (docs/building-apps/authentication/overview.mdx, docs/building-apps/authentication/integrate-internet-identity.mdx, docs/building-apps/authentication/alternative-origins.mdx); dfinity/icskills (skills/internet-identity/SKILL.md); dfinity/icp-js-sdk-docs (public/auth/latest.zip api/client/ — AuthClient, scopedKeys, SignedAttributes, AuthClientCreateOptions; public/core/latest.zip libs/identity/api.md — AttributesIdentity); dfinity/cdk-rs (ic-cdk/src/api.rs); caffeinelabs/motoko (src/prelude/prim.mo callerInfoData/Signer, test/run-drun/caller-info/caller-info.mo) */}
565+
{/* Upstream: informed by dfinity/portal (docs/building-apps/authentication/overview.mdx, docs/building-apps/authentication/integrate-internet-identity.mdx, docs/building-apps/authentication/alternative-origins.mdx); dfinity/icskills (skills/internet-identity/SKILL.md); dfinity/icp-js-sdk-docs (public/auth/latest.zip api/client/ — AuthClient, scopedKeys, SignedAttributes, AuthClientCreateOptions; public/core/latest.zip libs/identity/api.md — AttributesIdentity); dfinity/cdk-rs (ic-cdk/src/api.rs); caffeinelabs/motoko-core (src/CallerAttributes.mo getAttributes wrapper); caffeinelabs/motoko (src/prelude/prim.mo callerInfoData/Signer, test/run-drun/caller-info/caller-info.mo); dfinity/icp-cli (docs/reference/canister-settings.md#environment_variables) */}

0 commit comments

Comments
 (0)