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
{{ message }}
Repository navigation
docs: split the Internet Identity guide and add Enterprise SSO - #340
The Authentication section becomes a set of focused pages, and gains a guide for SSO administrators.
Getting started (guides/authentication/internet-identity.mdx, URL unchanged): what Internet Identity is, install, sign in and sign out, rendering on the sign-in status, calling the backend as the user, rejecting anonymous callers, and local development.
App metadata (new): publishing /.well-known/ii-app-metadata and serving it with CORS. The validation rules link to the Internet Identity specification instead of being copied.
Identity attributes (new): the available keys, openid: and sso: scoped keys, requesting attributes with the sign-in, and verifying them in Motoko (mo:identity-attributes) and Rust.
One-click sign-in (new): openIdProvider and ssoDomain, and checking an organization domain the user typed with getSsoStatus(), subscribe(), and refreshSsoStatus().
Enterprise SSO (new): the administrator's guide to registering an OIDC client, publishing /.well-known/ii-openid-configuration, gating access per app, the limits that reject the whole file, and when a change applies.
Shared sessions across subdomains (new): one derivation origin with ii-alternative-origins, then sharing one sign-in across sibling subdomains, including /.well-known/ii-auth-callbacks.
Verifiable credentials is hidden from the sidebar; the page stays published.
Structural decisions
The section is flat and ordered by sidebar.order; sidebar.mjs is unchanged.
These pages become the guides for Internet Identity integration. The @icp-sdk/auth docs on js.icp.build keep installation, a quick start, and the API reference, and link here for guides (the shared-sessions and one-click pages there move here).
Links into moved sections (#alternative-origins, #frontend-integration, #create-an-authenticated-agent) point at their new pages.
Internet Identity can authenticate an organization's staff against its
own OpenID provider, but nothing documented how to set that up. The new
page is written for the administrator of the identity provider: register
a client, publish the discovery file, and optionally govern access per
application.
Also adds the application developer's side to the Internet Identity
page: the `ssoDomain` option, validating a user-typed domain with
`isValidSsoDomain`, and SSO-scoped attributes. The two pages link to
each other.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds administrator-facing documentation for configuring Internet Identity single sign-on (SSO) via an organization’s OpenID Connect provider, and updates the existing Internet Identity guide with the corresponding application-side integration details. Also adjusts sidebar ordering so the new guide sits next to the existing authentication docs.
Changes:
Added a new SSO guide covering OIDC client setup, the /.well-known/ii-openid-configuration discovery file, per-app access control, and troubleshooting.
Updated the Internet Identity guide with ssoDomain usage, isValidSsoDomain, and SSO-scoped attribute key examples.
Moved “Verifiable credentials” down one slot in the Authentication sidebar order.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
The docs assert a precise timing guarantee ("never resolves in under 750 ms"). That kind of implementation detail is brittle and can become incorrect across SDK versions. Consider describing the behavior without hard-coding a minimum duration.
Call `controller.abort()` when the input changes. An aborted check rejects instead of returning `false`, so a superseded check is never read as an invalid domain. The check also never resolves in under 750 ms, which keeps a partially typed domain from flashing an error on every keystroke.
docs/guides/authentication/single-sign-on.md:22
The navigation path "Create App Integration → OIDC → Web Application" is Okta-specific UI wording, but the guide is written as if it applies to any IdP. Consider making the instruction generic and optionally calling out Okta as an example so Entra/other IdPs aren’t misled.
This sentence says nothing has to be registered on either side, but the SSO flow still requires the organization to register an OIDC client in its IdP. Reword to avoid contradicting the new SSO admin guide and to clarify that it’s the application that does not need a client registration with the org’s IdP.
To send the user to their organization's own OpenID provider instead, pass `ssoDomain` with the organization's domain. Internet Identity resolves the provider from a configuration file the organization publishes on that domain, so nothing has to be registered on either side:
This snippet creates a new AuthClient, but only imports isValidSsoDomain. Readers copying the snippet will hit a missing import for AuthClient; either include it here or remove the import line entirely.
import { isValidSsoDomain } from "@icp-sdk/auth/client";
The three steps are now the top-level sections, so the page outline is
the flow and the duplicate list in the intro is gone. Per-app access
keeps its steps together with the gate and the hashed-key recipe, and
the full file moves to the end as reference.
The hashed-key section now says where to run the snippet and what to do
with what it prints.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Names the audience rather than the mechanism, so a company scanning the
sidebar can see the page is about connecting their own provider. The file
is renamed to match the title, and the code fence gets a language tag.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sea-snake
changed the title
docs: add single sign-on guide for SSO administrators
docs: add Enterprise SSO guide for SSO administrators
Aug 4, 2026
Drops the Next steps link to it and restores its sidebar order, so the
page is untouched by this branch. Enterprise SSO still sorts ahead of it
within the Authentication group.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It appeared only in the Entra note and then unannounced in the complete
file. It exists because a per-app client can change the sub a provider
issues for the same person, so it is introduced in the per-app step, and
the complete file now has a table for the fields that step adds.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both were trailing sentences in step 3, so the default-deny switch read
as a footnote and the subject claim did not state its precondition. Each
is now a subsection: what happens to unlisted apps and which default to
pick, and the pairwise-sub case that only arises once an app has its own
client.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both sat in one aside, where a reader skimming the steps would miss them.
Assignment required now sits in the assignment step it modifies, since
missing it leaves an app open to the whole tenant, and the oid claim sits
with the subject-claim section. No aside remains.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gives admins a way to say how long a sign-in stays valid before staff
authenticate again. Named after the OIDC max_age parameter, in seconds,
and documented as a cap on whatever lifetime an application asks for.
The field is pending implementation in Internet Identity: the section
carries a comment saying so, and it must not be published before the
canister supports it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The block appeared twice, split by a blank line, and the lead-in still
referred to step 3 after the session field was added in step 2. One block
now, in field order, with no blank lines inside it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The field defaults to 28800 rather than being unset, so leaving it out
caps a sign-in at eight hours instead of deferring to whatever the
application asks for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Top of stack, stacked on #4189.
# Motivation
The sign-in screen and the relying party both key re-authentication off
delegation expiry. Without this, an organization could set an eight-hour
session and a dapp holding a 30-day delegation would carry on working
long past it, with only attribute verification failing.
# Changes
- The sign-in flow carries the organization's session length from the
discovery result onto the authenticated session, for both the
fresh-sign-in and last-used paths.
- The delegation handler bounds the `maxTimeToLive` it requests for the
account delegation by that value, via `cappedMaxTimeToLive`. An app
asking for 30 days on an eight-hour domain gets eight hours; a shorter
request is left alone.
This is a **UX cap, not a security boundary**, and it is applied in the
frontend deliberately: a delegation identifies the identity rather than
the SSO session, so any other access method on the anchor can mint a
fresh one regardless. The organization's deadline is enforced by the
expiry inside the certified attribute bundle (#4189).
# Tests
- New unit tests for the clamp: non-SSO sessions pass through, a longer
request is capped, a shorter request is untouched, and the
organization's value applies when nothing was requested.
- `tsc --project tsconfig.all.json`: clean. `eslint`: clean. Frontend
unit tests pass.
Administrator-facing documentation is in
dfinity/developer-docs#340.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verified against .sources/internetidentity at release-2026-08-07 (c78d1b99), the pin main picked up in #341.
Must fix
Sidebar order collision: the new page sets order: 2, and docs/guides/authentication/verifiable-credentials.md already uses order: 2. The PR description claims VC moves to order: 3, but the diff never touches it. Bump VC to 3.
Config changes are not immediate, and the page never says so: discovery entries cache for one hour (FRESH_FOR_SECONDS, sso.rs:72), with another hour of stale-if-error fallback (STALE_FOR_SECONDS, sso.rs:75). On a page whose central promise is gating access app by app, an admin will assume that removing an app_clients entry revokes access at once. It can take up to an hour, or two if a refetch fails. This belongs in step 3.
A gated app cannot be a user's first sign-in: resolve_gated_ii_client_sub returns None (surfacing as NoSuchAnchor) unless an identity was already established through the org's II client on that domain. sso_gating.rs:163 says it outright: "sign in normally first." A new employee sent straight to a gated app fails, with no explanation in this guide.
Out-of-bounds values reject the whole file, not just the field: more than 100 app_clients entries (MAX_APP_CLIENTS, sso.rs:48, rejected rather than truncated, deliberately), a session_max_age_seconds of 0 or above 2592000, or a file over 64 KiB (DISCOVERY_MAX_RESPONSE_BYTES, sso.rs:94) all make validate_ii_config fail, which takes SSO down for the entire domain. The guide presents the 30-day figure as a ceiling, which reads as "it clamps." It does not. One sentence per limit, plus the 100-entry cap, which is not mentioned at all.
PR description does not match the PR: it names the file single-sign-on.md (it is enterprise-sso.md), claims a Verifiable credentials reorder that is absent, and states that internet-identity is not among the .sources/ submodules when .sources/internetidentity exists and is pinned. It also carries an AI attribution line, which the repo rules ban.
Suggestions
Say what a domain may look like: validate_discovery_domain (sso.rs:378) requires a bare authority that round-trips exactly through the URL parser (is_bare_authority, sso.rs:400), so https://acme.com, acme.com/, and Acme.com are all rejected. The guide says "enter acme.com" without signalling that this is a constraint rather than a formatting choice.
app_clients keys are exact origins: matching is byte-equality against the app's origin, so a trailing slash, a path, or a scheme mismatch silently falls through to the org-wide client. The failure is invisible, so it deserves a line.
Explain why the CORS header is needed: Access-Control-Allow-Origin: * matters only for the client-side pre-check. The canister fetches the file via HTTPS outcalls and is unaffected. As written, an admin may believe SSO breaks without it.
Caution on changing stable_identifier_claim later: it changes how identities key onto anchors, so flipping it on a live domain is a migration, not a config tweak. Worth confirming the intended behaviour before wording it.
Anchor the cross-link: Next steps points at internet-identity.md bare. internet-identity.md#one-click-sso-sign-in lands on the half that answers "how do applications send users into this flow."
Numbering vs the intro: "Setup is two steps" followed by headings 1., 2., 3. reads slightly against itself. "Two required steps, plus an optional third" would remove the stumble.
Verified
Neither file is on a sync path. sync-ii-spec.yml writes only docs/references/internet-identity-spec.md, docs/references/verifiable-credentials-spec.md, and public/references/internet-identity.did.
Field names, defaults, and semantics against sso.rs and sso_gating.rs: client_id, openid_configuration, name (falls back to the domain), app_clients, gate_all_apps, stable_identifier_claim (default sub), session_max_age_seconds (default 8h via DEFAULT_SESSION_MAX_AGE_NS, ceiling delegation::MAX_EXPIRATION_PERIOD_NS = 30 days).
The salted-hash recipe: sha256(origin || salt) with the salt hashed as its hex string, keyed as <hex>:<hex>, cleartext and hashed keys mixable in one file. The shell snippet produces exactly what AppClientKey::parse accepts.
OIDC client settings: response_type=code id_token with response_mode=form_post, no access token, redirect https://id.ai/callback, default scopes openid/profile/email.
sso:<domain>:<key> attribute keys, and that verified_email is deliberately unavailable under sso: (attributes.rs:468), matching the page's reasoning.
session_max_age_seconds is live on mainnet: proposal 143403 executed 2026-08-10, module f6c0abac... = release-2026-08-07.
Style rules: no em-dashes, no dfx, no mo:base, no absolute internal links, .md extensions, complete frontmatter, ## Next steps present. All three links resolve.
One release-state note
The internet-identity.mdx additions (ssoDomain, isValidSsoDomain, scopedKeys({ ssoDomain })) come from dfinity/icp-js-auth#141, which is still open. The latest published @icp-sdk/auth is 8.0.3 and exposes none of them: scopedKeys accepts only openIdProvider. The canister half of this PR is fully live, so that section is the only part gated on an external release.
The Internet Identity page covered sign-in, attributes, one-click sign-in,
alternative origins, shared sessions, and app metadata in one place. It now
covers getting started, and each other topic has a page of its own in the
Authentication section: App metadata, Identity attributes, One-click
sign-in, and Shared sessions across subdomains. Links into the moved
sections point at their new pages.
Documents the limits that make Internet Identity reject the whole discovery
file, how long a cached copy is used before a change applies, and how
revoking access in the IdP differs from editing app_clients. Drops the CORS
requirement, since Internet Identity fetches the file itself, and spells out
the domain and app_clients key formats.
The page stays published at its URL; it is no longer listed under
Authentication.
sea-snake
changed the title
docs: add Enterprise SSO guide for SSO administrators
docs: split the Internet Identity guide and add Enterprise SSO
Oct 7, 2026
Read the caller in getProfile, pass the root key to both attribute agents,
bound and expire the Rust nonce set, handle signIn() rejections on the SSO
Continue button, build one client per page in shared sessions, and make the
OIDC client step provider-neutral.
This creates an AuthClient solely to read the status and then loses the instance. The guide states that each client installs listeners and schedules delegation refreshes, so every page that is not redirected leaks those resources for the lifetime of the page; use the page's existing client or dispose this temporary client when no redirect is needed.
On the item Copilot lists under "previously missed" (shared-sessions.md, a client created only to read the status): fixed in fdc5052. The page-load snippet now creates the page's own authClient, and the open-page snippet subscribes on that same client.
The Internet Identity specification only normalizes the icp0.io gateway to ic0.app; it does not make icp.net automatically equivalent. Treating all three as sharing a principal can cause users to skip alternative-origin configuration and receive different principals when moving between icp.net and ic0.app. Clarify the gateway exception and direct readers to configure alternative origins for other gateway origins.
getProfile takes a principal again, in 3035554: it is a public profile lookup, so reading another user's profile is what it is for. The Rust tab now uses the identity-attributes crate with the same lookup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Authentication section becomes a set of focused pages, and gains a guide for SSO administrators.
guides/authentication/internet-identity.mdx, URL unchanged): what Internet Identity is, install, sign in and sign out, rendering on the sign-in status, calling the backend as the user, rejecting anonymous callers, and local development./.well-known/ii-app-metadataand serving it with CORS. The validation rules link to the Internet Identity specification instead of being copied.openid:andsso:scoped keys, requesting attributes with the sign-in, and verifying them in Motoko (mo:identity-attributes) and Rust.openIdProviderandssoDomain, and checking an organization domain the user typed withgetSsoStatus(),subscribe(), andrefreshSsoStatus()./.well-known/ii-openid-configuration, gating access per app, the limits that reject the whole file, and when a change applies.ii-alternative-origins, then sharing one sign-in across sibling subdomains, including/.well-known/ii-auth-callbacks.Structural decisions
sidebar.order;sidebar.mjsis unchanged.@icp-sdk/authdocs on js.icp.build keep installation, a quick start, and the API reference, and link here for guides (the shared-sessions and one-click pages there move here).#alternative-origins,#frontend-integration,#create-an-authenticated-agent) point at their new pages.Describes
This documents the behavior of these changes:
getSsoStatus()andrefreshSsoStatus(), and Internet Identity resolving an organization domain without a browser fetch (soii-openid-configurationneeds no CORS header).Verified
npm run buildpasses (223 pages); the Authentication sidebar renders in the intended order and without Verifiable credentials.node scripts/validate.jspasses on every changed file; every internal link and anchor resolves.wasm32-unknown-unknownagainstic-cdk0.20.3 andic-cdk-management-canister.@icp-sdk/authsources, II limits and caching against the II sources, andmo:identity-attributesagainst its README.