Integrate Internet Identity authentication. Covers passkey and OpenID login flows, delegation handling, and principal-per-app isolation. Use when adding login, sign-in, auth, passkeys, or Internet Identity to a frontend or canister. Do NOT use for wallet integration or ICRC signer flows — use wallet-integration instead.
Internet Identity (II) is the Internet Computer's native authentication system. Users authenticate into II-powered apps either with passkeys stored in their devices or thorugh OpenID accounts (e.g., Google, Apple, Microsoft) -- no login or passwords required. Each user gets a unique principal per app, preventing cross-app tracking.
@icp-sdk/auth (>= 5.0.0), @icp-sdk/core (>= 5.0.0)| Canister | ID | URL | Purpose |
|---|---|---|---|
| Internet Identity (backend) | rdmx6-jaaaa-aaaaa-aaadq-cai | Manages user keys and authentication logic | |
| Internet Identity (frontend) |
uqzsh-gqaaa-aaaaq-qaada-caihttps://id.ai |
| Serves the II web app; identity provider URL points here |
Using the wrong II URL for the environment. The identity provider URL must point to the frontend canister (uqzsh-gqaaa-aaaaq-qaada-cai), not the backend. Local development should use http://id.ai.localhost:8000. Mainnet must use https://id.ai (which resolves to the frontend canister). Both canister IDs are well-known and identical on mainnet and local replicas -- hardcode them rather than doing a dynamic lookup.
Setting delegation expiry too long. Maximum delegation expiry is 30 days (2_592_000_000_000_000 nanoseconds). Longer values are silently clamped, which causes confusing session behavior. Use 8 hours for normal apps, 30 days maximum for "remember me" flows.
Not handling auth callbacks. The authClient.login() call requires onSuccess and onError callbacks. Without them, login failures are silently swallowed.
Using shouldFetchRootKey or fetchRootKey() instead of the ic_env cookie. The ic_env cookie (set by the asset canister or the Vite dev server) already contains the root key as IC_ROOT_KEY. Pass it via the rootKey option to HttpAgent.create() — this works in both local and production environments without environment branching. See the icp-cli skill's references/binding-generation.md for the pattern. Never call fetchRootKey() — it fetches the root key from the replica at runtime, which lets a man-in-the-middle substitute a fake key on mainnet.
Getting 2vxsx-fae as the principal after login. That is the anonymous principal -- it means authentication silently failed. Common causes: wrong identityProvider URL, missing onSuccess callback, or not extracting the identity from authClient.getIdentity() after login.
Passing principal as string to backend. The AuthClient gives you an Identity object. Backend canister methods receive the caller principal automatically via the IC protocol -- you do not pass it as a function argument. The caller principal is available on the backend via shared(msg) { msg.caller } in Motoko or ic_cdk::api::msg_caller() in Rust. For backend access control patterns, see the canister-security skill.
Add ii: true to the local network in your icp.yaml to enable Internet Identity locally: