feat: say a session ended, and name the identity provider as a pair - #191
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Correct expiry subscription behavior, sign-out guarantees, and SupersededError disposal coverage.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a v9 upgrade guide for @icp-sdk/auth, covering storage-backed sessions, API changes, lifecycle behavior, and migration details.
Changes:
- Documents client lifecycle, status APIs, and session storage changes.
- Covers session limits, provider configuration, refresh behavior, and errors.
- Summarizes shared sessions, SSO, principals, and agent options.
File summaries
| File | Summary |
|---|---|
docs/src/content/docs/upgrading/v9.md |
New v9 migration and API behavior guide. |
Review details
Suppressed comments (4)
docs/src/content/docs/upgrading/v9.md:65
maxTimeToIdleis enforced by the provider, and a refresh that discovers that idle expiry invokes the client'sSessionGoneErrorpath, which removes the stored state. Subscribers therefore observesigned-out;expiredis derived from the stored expiration (maxTimeToLive) and does not distinguish an idle timeout. As written, both the prose and replacement callback recommend a state that will not be produced.
An idle-out then arrives like any other change, as an `expired` status. Whatever `onIdle` did for you belongs there:
docs/src/content/docs/upgrading/v9.md:91
- This is not unconditional:
signOut()only revokes the provider session when this origin has a stored session credential, and it propagates a revoke failure after clearing local state. In particular, an origin currently insigned-in-elsewherehas nothing to revoke, so claiming that the session cannot be resumed can be false. Please qualify this as a successful revoke and mention the failure case.
Signing out ends the session at the canister. Every tab and every sibling subdomain that shared it is signed out, and it cannot be resumed — a new sign-in is a new session.
docs/src/content/docs/upgrading/v9.md:93
SupersededErroris also thrown whendispose()is called whilesignIn()is in flight (AuthClient.#assertCurrent), not only when a later sign-in or sign-out takes over. Because this guide recommends disposing clients on unmount, omitting that cause can leave a normal lifecycle rejection uncaught; please include disposal in the description.
Four errors are worth naming in that flow: `SessionGoneError` when the session has ended or been revoked, `InteractionRequiredError` when a silent request needs a real ceremony after all, `SessionNotHeldError` when a sign-in exists for the domain but this origin holds no credential for it, and `SupersededError` when a later sign-in or sign-out took over from the one you were waiting on.
docs/src/content/docs/upgrading/v9.md:91
- This overstates the revocation guarantee.
signOut()removes the shared state and revokes the provider session, but app delegations already issued to other tabs or sibling subdomains can remain usable until their short expiry; subsequent refreshes and mints are rejected. The existing shared-sessions guide documents this window, so distinguish status convergence from immediate invalidation here.
Signing out ends the session at the canister. Every tab and every sibling subdomain that shared it is signed out, and it cannot be resumed — a new sign-in is a new session.
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pair configures one deployment, so half of it rendered the ceremony at one provider and minted against another. Both fields are required where the option is given, and omitting it still means mainnet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A session the identity provider refuses is published as expired, naming the account it ended for, so every tab and sibling can say so. Signing out removes the record, which is what reads as signed out. The cookie's lifetime no longer tracks the session's, or the browser would drop the record at the moment it became worth reading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
marc0olo
approved these changes
Sep 15, 2026
MRmarioruci
approved these changes
Sep 15, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Two things, since the second came out of reviewing the first.
identityProvideris a pairThe option was
{ authorizeUrl?, canisterId? }— object optional, and both fields optional inside it. That let half a deployment be named: a customauthorizeUrlwith nocanisterIdrenders the ceremony at your provider and mints against mainnet, which is exactly the misconfiguration theTypeErrorfor bare URLs was added to catch. The two values configure one deployment, so they are now required together:identityProvider?: { - authorizeUrl?: string | URL; - canisterId?: Principal | string; + authorizeUrl: string | URL; + canisterId: Principal | string; };Omitting the option still means mainnet Internet Identity, so an app on mainnet configures nothing. A half-named object is a type error, and for a caller without types it throws a
TypeErrorthat says so rather than theCannot convert undefined or null to objectit produced before.9.0.0 is tagged but not published (
lateston npm is 8.0.3), so this tightens an API nobody can be depending on yet.docs/quick-start.mdneeded the same fix: it still passedidentityProvideras a string, which neither typechecks nor survives the constructor, and it declared the canister id only forAttributesIdentity. It now names both halves and varies only the URL by network.Upgrading guide
Adds
docs/src/content/docs/upgrading/v9.mdbeside the v4 and v8 guides. Prose rather than a checklist, ordered so that what stops a v8 build compiling comes first:AuthClientis no longer a singleton to thread through an appgetStatus()andsubscribe(), whatexpiredandsigned-in-elsewhereare for, and that other tabs arrive the same waystorage→credentialStorage, the newstateStorageandnamespace, and whykeyTypewent with themIdleManagerremoved, with the session's ownmaxTimeToIdle/maxTimeToLivein its place — including that v8's quiet 8-hour cap is now the provider's 30 daysidentityProvideras a pair, plus theidentityandtargetsoptions that are gonedisableBrowserActivity, sign-out ending the session everywhere, and the four errors worth catchinggetPrincipal()andagentOptionsEvery API name, default and removal in it was checked against the code rather than recalled.
Verification
typecheck,test(356 passing, one new),codestyle:checkandbuild+publintall pass. The new test was falsified twice: it first passed with the guard removed, becausePrincipal.from(undefined)throws aTypeErrorof its own, so it now asserts the message; with that, removing the guard fails it. Reverting the two fields to optional makestscreport both@ts-expect-errordirectives as unused, so the type-level half of the check is load-bearing too.The two guides the upgrading page points at are named rather than linked, since no page under
docs/links to another and the published site serves them under a versioned base path.🤖 Generated with Claude Code