Multi-account chooser (Tier 1, Phase 1): consent-surface remember + switch #3

Merged
franz merged 15 commits from feat/multi-account-chooser-phase1 into master 2026-06-29 06:54:30 +00:00
Owner

Multi-account chooser (Tier 1) — Phase 1

A "remembered accounts on this device" chooser, building on the issue #2 "Use a different account" affordance. Phase 1 covers the consent surface; the login-screen prefill (Phase 2) and the persistent page-chrome widget (Phase 3) are deliberately out of scope here.

Design

One live Kratos session at a time (Kratos is single-session-per-cookie), so switching is logout + a fresh login, not concurrent sessions. The remembered list is a signed forseti_known_accounts cookie holding identity UUIDs only; email and display name are resolved server-side and never written to a cookie or URL. The opt-in is captured server-side at consent (where the verified subject is already known), not via a login-screen checkbox, so there is no pre-auth flag that could persist the wrong identity.

What's included

  • forseti_known_accounts signed cookie (MRU, deduped, capped at 5) and a dedicated TTL config key.
  • Shared ory::kratos::tear_down_session helper; the OAuth logout and the issue #2 consent switch now share it.
  • with_prompt_login preserves a client's existing prompt (only forcing login) and carries the target identity as login_hint.
  • POST /accounts/switch with a post-teardown verification gate: if the session is still live after teardown it aborts to /error rather than redirecting (which would re-grant the old account). Clears the active-org pin only on success.
  • POST /accounts/forget (one or all).
  • Consent screen: lists other remembered accounts as a "Switch account" chooser (current subject excluded) and a "Remember this account on this device" opt-in; the consent-surface switch restarts the same OAuth flow so the downstream app still completes.

Testing

  • Unit tests for the cookie codec, MRU/forget logic, identity resolution, and the prompt/login_hint handling.
  • Rust integration tests against the live stack (CSRF guards, switch teardown + redirect + active-org clear, consent remember opt-in). These caught a real bug: the switch form field was optional and silently dropped login_hint — now a required identity_id.
  • A Playwright E2E (tests/e2e/.../m-account-chooser.spec.ts): remember opt-in sets the cookie, the chooser lists the other account, and switching restarts the flow to a fresh login.

Privacy

Consent is the lawful basis for the opt-in. The cookie holds only opaque identity UUIDs (no readable PII), client-side, with a TTL and an explicit forget path.

Relates to #2.

## Multi-account chooser (Tier 1) — Phase 1 A "remembered accounts on this device" chooser, building on the issue #2 "Use a different account" affordance. Phase 1 covers the consent surface; the login-screen prefill (Phase 2) and the persistent page-chrome widget (Phase 3) are deliberately out of scope here. ### Design One live Kratos session at a time (Kratos is single-session-per-cookie), so switching is logout + a fresh login, not concurrent sessions. The remembered list is a signed `forseti_known_accounts` cookie holding identity UUIDs only; email and display name are resolved server-side and never written to a cookie or URL. The opt-in is captured server-side at consent (where the verified subject is already known), not via a login-screen checkbox, so there is no pre-auth flag that could persist the wrong identity. ### What's included - `forseti_known_accounts` signed cookie (MRU, deduped, capped at 5) and a dedicated TTL config key. - Shared `ory::kratos::tear_down_session` helper; the OAuth logout and the issue #2 consent switch now share it. - `with_prompt_login` preserves a client's existing `prompt` (only forcing `login`) and carries the target identity as `login_hint`. - `POST /accounts/switch` with a post-teardown verification gate: if the session is still live after teardown it aborts to `/error` rather than redirecting (which would re-grant the old account). Clears the active-org pin only on success. - `POST /accounts/forget` (one or all). - Consent screen: lists other remembered accounts as a "Switch account" chooser (current subject excluded) and a "Remember this account on this device" opt-in; the consent-surface switch restarts the same OAuth flow so the downstream app still completes. ### Testing - Unit tests for the cookie codec, MRU/forget logic, identity resolution, and the `prompt`/`login_hint` handling. - Rust integration tests against the live stack (CSRF guards, switch teardown + redirect + active-org clear, consent remember opt-in). These caught a real bug: the switch form field was optional and silently dropped `login_hint` — now a required `identity_id`. - A Playwright E2E (`tests/e2e/.../m-account-chooser.spec.ts`): remember opt-in sets the cookie, the chooser lists the other account, and switching restarts the flow to a fresh login. ### Privacy Consent is the lawful basis for the opt-in. The cookie holds only opaque identity UUIDs (no readable PII), client-side, with a TTL and an explicit forget path. Relates to #2.
franz merged commit 045d1379b0 into master 2026-06-29 06:54:30 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
franz/forseti!3
No description provided.