Allow switching account on the OAuth consent screen #2

Closed
opened 2026-06-24 18:32:37 +00:00 by franz · 3 comments
Owner

When a user arrives at the OAuth/SSO consent screen with an active session, the consent template (templates/consent.html) shows "Signed in as " but offers no way to switch accounts. The only actions are Allow and Deny scopes.

If the wrong account is signed in, the user has to manually log out elsewhere and start the flow again. There is no "Not you? / Use a different account" affordance.

Current behaviour

  • templates/consent.html displays the signed-in email and the scope checkboxes, plus Allow/Deny buttons only.
  • src/oauth/login.rs accepts the existing Kratos session directly when one is present; it only forces re-auth for acr_values=aal2 step-up.
  • OIDC prompt=login / prompt=select_account and max_age are not handled.

Proposed

  • Add a "Use a different account" link on the consent screen that lets the user re-authenticate as someone else (log out of the current Kratos session and restart the OAuth flow, returning to the same consent challenge).
  • Optionally honour the OIDC prompt=login / select_account and max_age parameters to drive the same path.
When a user arrives at the OAuth/SSO consent screen with an active session, the consent template (`templates/consent.html`) shows "Signed in as <email>" but offers no way to switch accounts. The only actions are Allow and Deny scopes. If the wrong account is signed in, the user has to manually log out elsewhere and start the flow again. There is no "Not you? / Use a different account" affordance. ### Current behaviour - `templates/consent.html` displays the signed-in email and the scope checkboxes, plus Allow/Deny buttons only. - `src/oauth/login.rs` accepts the existing Kratos session directly when one is present; it only forces re-auth for `acr_values=aal2` step-up. - OIDC `prompt=login` / `prompt=select_account` and `max_age` are not handled. ### Proposed - Add a "Use a different account" link on the consent screen that lets the user re-authenticate as someone else (log out of the current Kratos session and restart the OAuth flow, returning to the same consent challenge). - Optionally honour the OIDC `prompt=login` / `select_account` and `max_age` parameters to drive the same path.
Author
Owner

Related fix on the account-switching path: the login-screen sign-out form (the AAL2 escape hatch shown when a step-up is required but no second factor is enrolled) was missing its CSRF token, so submitting it returned a 403. Fixed by adding the _csrf hidden field. That's the login-screen sign-out, a different surface from this issue's consent-screen "use a different account" affordance, which stays open.

Related fix on the account-switching path: the login-screen sign-out form (the AAL2 escape hatch shown when a step-up is required but no second factor is enrolled) was missing its CSRF token, so submitting it returned a 403. Fixed by adding the `_csrf` hidden field. That's the login-screen sign-out, a different surface from this issue's consent-screen "use a different account" affordance, which stays open.
Author
Owner

Added the 'Use a different account' affordance on the consent screen.

It posts (with CSRF) back to POST /oauth/consent with decision=switch_account. The handler tears down the Kratos session (best-effort, same path as /oauth/logout), emits an oauth.account.switch audit row, and restarts the OAuth flow at the original auth URL with prompt=login. With the Kratos session gone, /oauth/login bounces to /login so the user can authenticate as someone else, then flows back to consent. No new routes; reuses POST /oauth/consent.

Scoped to the affordance only. The optional prompt=login / select_account / max_age honouring in oauth_login is not done: the Hydra SDK's OIDC context does not expose prompt or max_age (they would need parsing out of request_url), and making oauth_login honour prompt=login needs careful loop-breaking to avoid a re-auth loop. Leaving this issue open to track that follow-up, or it can be split into its own issue.

Added the 'Use a different account' affordance on the consent screen. It posts (with CSRF) back to POST /oauth/consent with decision=switch_account. The handler tears down the Kratos session (best-effort, same path as /oauth/logout), emits an oauth.account.switch audit row, and restarts the OAuth flow at the original auth URL with prompt=login. With the Kratos session gone, /oauth/login bounces to /login so the user can authenticate as someone else, then flows back to consent. No new routes; reuses POST /oauth/consent. Scoped to the affordance only. The optional prompt=login / select_account / max_age honouring in oauth_login is not done: the Hydra SDK's OIDC context does not expose prompt or max_age (they would need parsing out of request_url), and making oauth_login honour prompt=login needs careful loop-breaking to avoid a re-auth loop. Leaving this issue open to track that follow-up, or it can be split into its own issue.
Author
Owner

Closing: the consent-screen account switching this issue asked for shipped in PR #3 (#3) — the 'Use a different account' affordance plus a Tier 1 'remembered accounts on this device' chooser (server-side remember opt-in, switch, forget), merged to master.

The one optional thread that remains, honouring OIDC prompt=login / select_account / max_age in /oauth/login, is split out into #4.

Closing: the consent-screen account switching this issue asked for shipped in PR #3 (https://git.gofranz.com/franz/forseti/pulls/3) — the 'Use a different account' affordance plus a Tier 1 'remembered accounts on this device' chooser (server-side remember opt-in, switch, forget), merged to master. The one optional thread that remains, honouring OIDC prompt=login / select_account / max_age in /oauth/login, is split out into #4.
franz closed this issue 2026-06-29 08:08:08 +00:00
Sign in to join this conversation.
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#2
No description provided.