From bdde56ee777c7fa9bbe21ef5698a37db3adc0a35 Mon Sep 17 00:00:00 2001
From: sea-snake <104725312+sea-snake@users.noreply.github.com>
Date: Mon, 4 May 2026 15:38:21 +0000
Subject: [PATCH 1/7] docs: update Internet Identity guide for new AuthClient
API
The @icp-sdk/auth library was updated: AuthClient is now constructed
directly (not via a static factory), sign-in is promise-based, and
isAuthenticated() and getIdentity() flipped sync/async. Also moves
identityProvider, derivationOrigin, and openIdProvider onto the
constructor, and adds a one-click OpenID example.
---
.../authentication/internet-identity.mdx | 54 +++++++++++--------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/docs/guides/authentication/internet-identity.mdx b/docs/guides/authentication/internet-identity.mdx
index a3f8461..4eeefb0 100644
--- a/docs/guides/authentication/internet-identity.mdx
+++ b/docs/guides/authentication/internet-identity.mdx
@@ -79,36 +79,32 @@ function getIdentityProviderUrl() {
### Login, logout, and session check
-Create a single `AuthClient` instance on page load and reuse it for all operations:
+Create a single `AuthClient` instance on page load and reuse it for all operations. The identity provider URL is passed at construction time, not on each sign-in:
```javascript
// Create the auth client (once, on page load)
-const authClient = await AuthClient.create();
+const authClient = new AuthClient({
+ identityProvider: getIdentityProviderUrl(),
+});
// Check for existing session
-const isAuthenticated = await authClient.isAuthenticated();
-if (isAuthenticated) {
- const identity = authClient.getIdentity();
+if (authClient.isAuthenticated()) {
+ const identity = await authClient.getIdentity();
// Restore session: create agent and actor with this identity
}
// Login
async function login() {
- return new Promise((resolve, reject) => {
- authClient.login({
- identityProvider: getIdentityProviderUrl(),
+ try {
+ const identity = await authClient.signIn({
maxTimeToLive: BigInt(8) * BigInt(3_600_000_000_000), // 8 hours
- onSuccess: () => {
- const identity = authClient.getIdentity();
- console.log("Logged in as:", identity.getPrincipal().toText());
- resolve(identity);
- },
- onError: (error) => {
- console.error("Login failed:", error);
- reject(error);
- },
});
- });
+ console.log("Logged in as:", identity.getPrincipal().toText());
+ return identity;
+ } catch (error) {
+ console.error("Login failed:", error);
+ throw error;
+ }
}
// Logout
@@ -118,6 +114,21 @@ async function logout() {
}
```
+`signIn()` returns the new `Identity` directly. It rejects if the user closes the popup or authentication fails, so wrap the call in `try`/`catch` instead of relying on success/error callbacks.
+
+### One-click OpenID sign-in
+
+To skip the Internet Identity authentication-method screen and send the user straight to a specific OpenID provider, pass `openIdProvider` to the constructor. Supported values are `'google'`, `'apple'`, and `'microsoft'`:
+
+```javascript
+const authClient = new AuthClient({
+ identityProvider: getIdentityProviderUrl(),
+ openIdProvider: "google",
+});
+```
+
+The rest of the flow (`signIn`, `getIdentity`, `logout`) is unchanged.
+
### Create an authenticated agent
After login, create an `HttpAgent` using the delegation identity. The agent signs all subsequent canister calls with the user's delegated key:
@@ -291,13 +302,12 @@ To keep principals consistent across your own custom domains, configure **altern
]
```
-3. **On the alternative origin (B):** Set the `derivationOrigin` in your login call to point back to the primary origin:
+3. **On the alternative origin (B):** Set the `derivationOrigin` on the `AuthClient` constructor to point back to the primary origin:
```javascript
- authClient.login({
+ const authClient = new AuthClient({
identityProvider: "https://id.ai",
derivationOrigin: "https://xxxxx.icp0.io", // primary origin A
- onSuccess: () => { /* ... */ },
});
```
@@ -309,7 +319,7 @@ For full details, see the [Internet Identity specification](../../references/int
- **Using the wrong II URL per environment**: local development must point to `http://id.ai.localhost:8000`, mainnet to `https://id.ai`. Use the `getIdentityProviderUrl` helper (shown above) to switch based on hostname.
- **`fetch` "Illegal invocation" in bundled builds**: always pass `fetch: window.fetch.bind(window)` to `HttpAgent.create()`. Without explicit binding, bundlers (Vite, webpack) extract `fetch` from `window` and call it without the correct `this` context.
-- **Missing `onSuccess`/`onError` callbacks**: `authClient.login()` requires both. Without them, login failures are silently swallowed.
+- **Not awaiting `signIn()` or skipping the `try`/`catch`**: `authClient.signIn()` returns a promise that rejects when the user closes the popup or authentication fails. Without `await` and a `catch`, those failures are silently swallowed.
- **Delegation expiry too long**: the maximum is 30 days. Values above this are silently clamped, causing confusing session behavior. Use 8 hours for typical apps.
- **Passing principal as a string argument**: the backend reads the caller automatically from the IC protocol. Do not pass it as a function parameter.
- **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.
From 05aa244a0ca5dbe8750582bb2204d5a4b5fee58b Mon Sep 17 00:00:00 2001
From: sea-snake <104725312+sea-snake@users.noreply.github.com>
Date: Mon, 4 May 2026 15:53:03 +0000
Subject: [PATCH 2/7] docs: add identity attributes (requestAttributes /
AttributesIdentity)
---
.../authentication/internet-identity.mdx | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/docs/guides/authentication/internet-identity.mdx b/docs/guides/authentication/internet-identity.mdx
index 4eeefb0..00f9de0 100644
--- a/docs/guides/authentication/internet-identity.mdx
+++ b/docs/guides/authentication/internet-identity.mdx
@@ -149,6 +149,84 @@ async function createAuthenticatedActor(identity, canisterId, idlFactory) {
`safeGetCanisterEnv()` reads the `ic_env` cookie set by the asset canister or Vite dev server (it only works in browser contexts. For Node.js scripts or tests connecting to a **local** replica, create the agent normally and call `await agent.fetchRootKey()` explicitly after creation. Never call `fetchRootKey()` against a mainnet endpoint) on mainnet the root key is pre-trusted, and fetching it at runtime exposes a man-in-the-middle risk.
:::
+### Requesting identity attributes
+
+When a backend canister needs more than just the user's principal (for example, a verified email address), Internet Identity can return signed attributes alongside the delegation. Your backend issues a nonce scoped to a specific action; the frontend requests the attributes during sign-in; the backend verifies the bundle when the user calls the protected method.
+
+**Why a backend-issued nonce?** Tying attributes to a canister-issued nonce prevents replay: an intercepted bundle cannot be reused for a different action, on a different user, or after that action expires. The nonce must originate from the canister, not the frontend.
+
+```typescript
+import { AuthClient } from "@icp-sdk/auth/client";
+import { AttributesIdentity } from "@icp-sdk/core/identity";
+import { HttpAgent, Actor } from "@icp-sdk/core/agent";
+import { Principal } from "@icp-sdk/core/principal";
+
+async function registerWithEmail() {
+ // 1. Backend issues a nonce scoped to this registration
+ const anonymousAgent = await HttpAgent.create();
+ const backend = Actor.createActor(backendIdl, {
+ agent: anonymousAgent,
+ canisterId,
+ });
+ const nonce = await backend.registerBegin();
+
+ // 2. Run sign-in and the attribute request in parallel.
+ // The user sees a single Internet Identity interaction.
+ const signInPromise = authClient.signIn();
+ const attributesPromise = authClient.requestAttributes({
+ keys: ["email"],
+ nonce,
+ });
+
+ const identity = await signInPromise;
+ const { data, signature } = await attributesPromise;
+
+ // 3. Wrap the identity so the signed attributes travel with each call
+ const identityWithAttributes = new AttributesIdentity({
+ inner: identity,
+ attributes: { data, signature },
+ // The Internet Identity backend canister ID is the attribute signer
+ signer: { canisterId: Principal.fromText("rdmx6-jaaaa-aaaaa-aaadq-cai") },
+ });
+
+ // 4. Call the protected method. The backend verifies the nonce, origin,
+ // and timestamp, then reads the email.
+ const agent = await HttpAgent.create({ identity: identityWithAttributes });
+ const app = Actor.createActor(appIdl, { agent, canisterId });
+ await app.registerFinish();
+}
+```
+
+Each signed attribute bundle carries three implicit fields the backend should verify:
+
+- `implicit:nonce`: matches the canister-issued nonce, preventing replay across actions and users.
+- `implicit:origin`: the requesting frontend origin, so a malicious dapp cannot forward attributes to a different backend.
+- `implicit:issued_at_timestamp_ns`: issuance time, letting the canister reject stale bundles even when the nonce is still valid.
+
+Attributes can also be requested after sign-in, for example to link an email to an existing account. The pattern is the same: the backend issues a nonce for that action, the frontend calls `requestAttributes`, and the backend verifies the result.
+
+#### OpenID-scoped attributes
+
+When using one-click OpenID sign-in, attributes can be scoped to the provider. The user authenticates and shares attributes in a single step, with no extra prompt:
+
+```typescript
+import { AuthClient, scopedKeys } from "@icp-sdk/auth/client";
+
+const authClient = new AuthClient({
+ identityProvider: getIdentityProviderUrl(),
+ openIdProvider: "google",
+});
+
+const nonce = await backend.registerBegin();
+const signInPromise = authClient.signIn();
+// Requests name, email, and verified_email from the Google account
+// linked to the user's Internet Identity.
+const attributesPromise = authClient.requestAttributes({
+ keys: scopedKeys({ openIdProvider: "google" }),
+ nonce,
+});
+```
+
## Backend authentication
Your backend canister receives the caller's principal automatically through the IC protocol. You do not pass the principal as a function argument: use `msg.caller` (Motoko) or `ic_cdk::api::msg_caller()` (Rust) to read it.
@@ -324,6 +402,7 @@ For full details, see the [Internet Identity specification](../../references/int
- **Passing principal as a string argument**: the backend reads the caller automatically from the IC protocol. Do not pass it as a function parameter.
- **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.
- **Creating multiple `AuthClient` instances**: create one on page load and reuse it. Multiple instances cause race conditions with session storage.
+- **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.
## Next steps
From 59d5d2dd2aee3d1fd14ff075e2e35f58c37ba4e7 Mon Sep 17 00:00:00 2001
From: sea-snake <104725312+sea-snake@users.noreply.github.com>
Date: Mon, 4 May 2026 16:21:16 +0000
Subject: [PATCH 3/7] docs: document backend attribute reading
(msg_caller_info_data/signer)
---
.../authentication/internet-identity.mdx | 138 +++++++++++++++++-
1 file changed, 137 insertions(+), 1 deletion(-)
diff --git a/docs/guides/authentication/internet-identity.mdx b/docs/guides/authentication/internet-identity.mdx
index 00f9de0..3586078 100644
--- a/docs/guides/authentication/internet-identity.mdx
+++ b/docs/guides/authentication/internet-identity.mdx
@@ -313,6 +313,141 @@ async fn protected_async_action() -> String {
}
```
+### Read identity attributes
+
+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`).
+
+The bundle is Candid-encoded as an [ICRC-3 Value](../../reference/internet-identity-spec.md) `Map` with three implicit fields plus the keys you requested:
+
+- `implicit:nonce`: must equal a nonce your canister issued for this user and action.
+- `implicit:origin`: must equal a trusted frontend origin.
+- `implicit:issued_at_timestamp_ns`: reject if too old (a few minutes is typical).
+- Plain attribute keys (e.g., `"email"`) for default-scope attributes; OpenID-scoped keys (e.g., `"openid:https://accounts.google.com:email"`) when the frontend used `scopedKeys`.
+
+
+
+
+```motoko
+import Prim "mo:prim";
+import Principal "mo:core/Principal";
+import Runtime "mo:core/Runtime";
+
+persistent actor {
+ let iiPrincipal = Principal.fromText("rdmx6-jaaaa-aaaaa-aaadq-cai");
+
+ type Icrc3Value = {
+ #Nat : Nat;
+ #Int : Int;
+ #Blob : Blob;
+ #Text : Text;
+ #Array : [Icrc3Value];
+ #Map : [(Text, Icrc3Value)];
+ };
+
+ func lookupText(entries : [(Text, Icrc3Value)], key : Text) : ?Text {
+ for ((k, v) in entries.vals()) {
+ if (k == key) {
+ switch v { case (#Text t) { return ?t }; case _ {} };
+ };
+ };
+ null;
+ };
+
+ // Returns the verified attribute map, trapping if the signer is not II.
+ func iiAttributes() : [(Text, Icrc3Value)] {
+ let signer = Prim.callerInfoSigner();
+ if (signer.size() == 0 or Principal.fromBlob(signer) != iiPrincipal) {
+ Runtime.trap("Untrusted attribute signer");
+ };
+ let data = Prim.callerInfoData();
+ let ?value : ?Icrc3Value = from_candid (data) else Runtime.trap("invalid attribute bundle");
+ let #Map(entries) = value else Runtime.trap("expected attribute map");
+ entries
+ };
+
+ public shared ({ caller }) func registerFinish() : async Text {
+ if (Principal.isAnonymous(caller)) Runtime.trap("Anonymous caller not allowed");
+ let entries = iiAttributes();
+
+ let ?origin = lookupText(entries, "implicit:origin") else Runtime.trap("missing origin");
+ if (origin != "https://your-app.icp0.io") Runtime.trap("Wrong origin");
+
+ // Compare implicit:nonce to the nonce you minted in registerBegin (omitted for brevity)
+ // and check implicit:issued_at_timestamp_ns is within your freshness window.
+
+ let ?email = lookupText(entries, "email") else Runtime.trap("missing email");
+ "Registered " # Principal.toText(caller) # " with email " # email
+ };
+};
+```
+
+
+
+
+```rust
+use candid::{decode_one, CandidType, Deserialize, Principal};
+use ic_cdk::api::{msg_caller, msg_caller_info_data, msg_caller_info_signer};
+use ic_cdk::update;
+
+const II_PRINCIPAL: &str = "rdmx6-jaaaa-aaaaa-aaadq-cai";
+
+#[derive(CandidType, Deserialize)]
+enum Icrc3Value {
+ Nat(candid::Nat),
+ Int(candid::Int),
+ Blob(Vec),
+ Text(String),
+ Array(Vec),
+ Map(Vec<(String, Icrc3Value)>),
+}
+
+fn lookup_text<'a>(entries: &'a [(String, Icrc3Value)], key: &str) -> Option<&'a str> {
+ entries.iter().find_map(|(k, v)| match v {
+ Icrc3Value::Text(s) if k == key => Some(s.as_str()),
+ _ => None,
+ })
+}
+
+// Returns the verified attribute entries, trapping if the signer is not II.
+fn ii_attributes() -> Vec<(String, Icrc3Value)> {
+ let trusted = Principal::from_text(II_PRINCIPAL).unwrap();
+ if msg_caller_info_signer() != Some(trusted) {
+ ic_cdk::trap("Untrusted attribute signer");
+ }
+ let bundle = msg_caller_info_data();
+ let value: Icrc3Value = decode_one(&bundle).unwrap_or_else(|_| ic_cdk::trap("invalid attribute bundle"));
+ match value {
+ Icrc3Value::Map(entries) => entries,
+ _ => ic_cdk::trap("expected attribute map"),
+ }
+}
+
+#[update]
+fn register_finish() -> String {
+ let caller = msg_caller();
+ if caller == Principal::anonymous() { ic_cdk::trap("Anonymous caller not allowed"); }
+ let entries = ii_attributes();
+
+ let origin = lookup_text(&entries, "implicit:origin")
+ .unwrap_or_else(|| ic_cdk::trap("missing origin"));
+ if origin != "https://your-app.icp0.io" { ic_cdk::trap("Wrong origin"); }
+
+ // Compare implicit:nonce to the nonce you minted in register_begin (omitted for brevity)
+ // and check implicit:issued_at_timestamp_ns is within your freshness window.
+
+ let email = lookup_text(&entries, "email")
+ .unwrap_or_else(|| ic_cdk::trap("missing email"));
+ format!("Registered {} with email {}", caller, email)
+}
+```
+
+
+
+
+:::tip[Storing the nonce]
+Mint the nonce in your `registerBegin` (or equivalent) method and persist it in stable memory keyed by the user's principal and the action name. Mark it consumed in `registerFinish` so a bundle cannot be replayed. Use a short freshness window so abandoned attempts age out.
+:::
+
## Local development
Start the local network and deploy. With `ii: true` in your `icp.yaml`, icp-cli deploys a local Internet Identity canister automatically:
@@ -403,6 +538,7 @@ For full details, see the [Internet Identity specification](../../references/int
- **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.
- **Creating multiple `AuthClient` instances**: create one on page load and reuse it. Multiple instances cause race conditions with session storage.
- **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.
+- **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.
## Next steps
@@ -414,4 +550,4 @@ For full details, see the [Internet Identity specification](../../references/int
{/* TODO: Add Unity native app integration via deep links: see portal native-apps/unity_ii_* */}
-{/* 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 */}
+{/* 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-auth (README.md, src/client/auth-client.ts); dfinity/cdk-rs (ic-cdk/src/api.rs msg_caller_info_data/signer); caffeinelabs/motoko (src/prelude/prim.mo callerInfoData/Signer, test/run-drun/caller-info/caller-info.mo) */}
From 037f3ec96335c920a770c714af0945da2bdbae27 Mon Sep 17 00:00:00 2001
From: sea-snake <104725312+sea-snake@users.noreply.github.com>
Date: Mon, 4 May 2026 16:50:37 +0000
Subject: [PATCH 4/7] docs: use sign-in terminology in prose, keep logout for
the API call
---
.../authentication/internet-identity.mdx | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/docs/guides/authentication/internet-identity.mdx b/docs/guides/authentication/internet-identity.mdx
index 3586078..0de2b9e 100644
--- a/docs/guides/authentication/internet-identity.mdx
+++ b/docs/guides/authentication/internet-identity.mdx
@@ -1,6 +1,6 @@
---
title: "Internet Identity"
-description: "Integrate passkey-based authentication with Internet Identity for frontend login, backend caller verification, and session management"
+description: "Integrate passkey-based authentication with Internet Identity for frontend sign-in, backend caller verification, and session management"
sidebar:
order: 1
---
@@ -9,7 +9,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
Internet Identity (II) is the Internet Computer's native authentication system. Users sign in with passkeys or OpenID accounts (Google, Apple, Microsoft) instead of passwords. Each user receives a unique principal per frontend origin, preventing cross-app tracking.
-This guide covers setting up II authentication end-to-end: configuring your project, adding login to your frontend, and verifying callers in your backend.
+This guide covers setting up II authentication end-to-end: configuring your project, adding sign-in to your frontend, and verifying callers in your backend.
## How it works
@@ -46,7 +46,7 @@ npm install @icp-sdk/auth @icp-sdk/core
## Frontend integration
-The `AuthClient` from `@icp-sdk/auth` handles the full login flow: opening the II popup, receiving the delegation, and managing session persistence.
+The `AuthClient` from `@icp-sdk/auth` handles the full sign-in flow: opening the II popup, receiving the delegation, and managing session persistence.
### Environment detection
@@ -77,7 +77,7 @@ function getIdentityProviderUrl() {
}
```
-### Login, logout, and session check
+### Sign in, log out, and session check
Create a single `AuthClient` instance on page load and reuse it for all operations. The identity provider URL is passed at construction time, not on each sign-in:
@@ -93,21 +93,21 @@ if (authClient.isAuthenticated()) {
// Restore session: create agent and actor with this identity
}
-// Login
-async function login() {
+// Sign in
+async function signIn() {
try {
const identity = await authClient.signIn({
maxTimeToLive: BigInt(8) * BigInt(3_600_000_000_000), // 8 hours
});
- console.log("Logged in as:", identity.getPrincipal().toText());
+ console.log("Signed in as:", identity.getPrincipal().toText());
return identity;
} catch (error) {
- console.error("Login failed:", error);
+ console.error("Sign-in failed:", error);
throw error;
}
}
-// Logout
+// Log out
async function logout() {
await authClient.logout();
// Reset UI state or reload
@@ -131,7 +131,7 @@ The rest of the flow (`signIn`, `getIdentity`, `logout`) is unchanged.
### Create an authenticated agent
-After login, create an `HttpAgent` using the delegation identity. The agent signs all subsequent canister calls with the user's delegated key:
+After sign-in, create an `HttpAgent` using the delegation identity. The agent signs all subsequent canister calls with the user's delegated key:
```javascript
async function createAuthenticatedActor(identity, canisterId, idlFactory) {
From 4550c0ffd18d3cbc5acdcb1b887dbdfe2408e181 Mon Sep 17 00:00:00 2001
From: sea-snake <104725312+sea-snake@users.noreply.github.com>
Date: Mon, 4 May 2026 17:10:24 +0000
Subject: [PATCH 5/7] docs: rename logout to signOut to match @icp-sdk/auth API
rename
---
docs/guides/authentication/internet-identity.mdx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/guides/authentication/internet-identity.mdx b/docs/guides/authentication/internet-identity.mdx
index 0de2b9e..e2eb2d3 100644
--- a/docs/guides/authentication/internet-identity.mdx
+++ b/docs/guides/authentication/internet-identity.mdx
@@ -77,7 +77,7 @@ function getIdentityProviderUrl() {
}
```
-### Sign in, log out, and session check
+### Sign in, sign out, and session check
Create a single `AuthClient` instance on page load and reuse it for all operations. The identity provider URL is passed at construction time, not on each sign-in:
@@ -107,9 +107,9 @@ async function signIn() {
}
}
-// Log out
-async function logout() {
- await authClient.logout();
+// Sign out
+async function signOut() {
+ await authClient.signOut();
// Reset UI state or reload
}
```
@@ -127,7 +127,7 @@ const authClient = new AuthClient({
});
```
-The rest of the flow (`signIn`, `getIdentity`, `logout`) is unchanged.
+The rest of the flow (`signIn`, `getIdentity`, `signOut`) is unchanged.
### Create an authenticated agent
From 371bf0122606925b20c481cd09d6421932a8cdc5 Mon Sep 17 00:00:00 2001
From: Marco Walz
Date: Tue, 5 May 2026 00:43:11 +0200
Subject: [PATCH 6/7] chore: bump icp-js-sdk-docs to v7.0.0 auth, fix broken
link and upstream comment
- Bumps .sources/icp-js-sdk-docs to 17e4346 (auth v7.0.0)
- Fixes broken ../../reference/ link to ../../references/ (internet-identity-spec.md)
- Updates Upstream comment to reference icp-js-sdk-docs submodule instead of upstream source repo
---
.sources/icp-js-sdk-docs | 2 +-
docs/guides/authentication/internet-identity.mdx | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.sources/icp-js-sdk-docs b/.sources/icp-js-sdk-docs
index 603272e..17e4346 160000
--- a/.sources/icp-js-sdk-docs
+++ b/.sources/icp-js-sdk-docs
@@ -1 +1 @@
-Subproject commit 603272ea3bc9374c845c2fd9892f8a7895216839
+Subproject commit 17e4346a456bf4f8a2f526c192fe0d6461158bff
diff --git a/docs/guides/authentication/internet-identity.mdx b/docs/guides/authentication/internet-identity.mdx
index e2eb2d3..7bfa2ab 100644
--- a/docs/guides/authentication/internet-identity.mdx
+++ b/docs/guides/authentication/internet-identity.mdx
@@ -317,7 +317,7 @@ async fn protected_async_action() -> String {
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`).
-The bundle is Candid-encoded as an [ICRC-3 Value](../../reference/internet-identity-spec.md) `Map` with three implicit fields plus the keys you requested:
+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:
- `implicit:nonce`: must equal a nonce your canister issued for this user and action.
- `implicit:origin`: must equal a trusted frontend origin.
@@ -550,4 +550,4 @@ For full details, see the [Internet Identity specification](../../references/int
{/* TODO: Add Unity native app integration via deep links: see portal native-apps/unity_ii_* */}
-{/* 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-auth (README.md, src/client/auth-client.ts); dfinity/cdk-rs (ic-cdk/src/api.rs msg_caller_info_data/signer); caffeinelabs/motoko (src/prelude/prim.mo callerInfoData/Signer, test/run-drun/caller-info/caller-info.mo) */}
+{/* 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) */}
From 9ecb8bf516eed092d90ab9416cdc6302e6daefc7 Mon Sep 17 00:00:00 2001
From: Marco Walz
Date: Tue, 5 May 2026 00:56:17 +0200
Subject: [PATCH 7/7] chore: bump cdk-rs to ic-cdk v0.20.1
(msg_caller_info_data/signer)
Adds msg_caller_info_data and msg_caller_info_signer to ic_cdk::api,
which are required by the Rust backend example in the Internet Identity
guide (identity attributes section).
No other user-facing API changes between 0.19.0 and 0.20.1.
---
.sources/VERSIONS | 2 +-
.sources/cdk-rs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.sources/VERSIONS b/.sources/VERSIONS
index 1dabdb7..1ea551e 100644
--- a/.sources/VERSIONS
+++ b/.sources/VERSIONS
@@ -52,6 +52,6 @@ ic-pub-key v1.0.1
icp-cli v0.2.3 caeac37
motoko v1.6.0 b818df6
motoko-core v2.4.0 cd37dbf
-cdk-rs ic-cdk v0.19.0 / ic-cdk-timers v1.0.0 / ic-cdk-executor v2.0.0 80d8234
+cdk-rs ic-cdk v0.20.1 / ic-cdk-timers v1.0.0 / ic-cdk-executor v2.0.0 317f55c
candid 2025-12-18 # candid v0.10.20, didc v0.5.4 2e4a2cf
response-verification v3.1.0 18c5a37
diff --git a/.sources/cdk-rs b/.sources/cdk-rs
index 80d8234..317f55c 160000
--- a/.sources/cdk-rs
+++ b/.sources/cdk-rs
@@ -1 +1 @@
-Subproject commit 80d82348b7de1f10117aead50ad26669da7e2b79
+Subproject commit 317f55c08e8f4384552d04c97f91e122b84abf1a