Honour OIDC prompt=login / select_account / max_age in /oauth/login #4

Open
opened 2026-06-29 08:07:56 +00:00 by franz · 0 comments
Owner

Split out from #2 (the consent-screen "use a different account" affordance and the Phase 1 account chooser shipped in PR #3; this is the remaining optional thread).

/oauth/login (src/oauth/login.rs) accepts the existing Kratos session whenever one is present and only forces re-auth for an acr_values=aal2 step-up. It does not honour the OIDC prompt and max_age parameters, so a relying party cannot force re-authentication or account selection.

Goal

Honour, at the Hydra login phase:

  • prompt=login - force a fresh authentication even when a Kratos session exists.
  • max_age=N - force re-auth when the session's authenticated_at is older than N seconds.
  • prompt=select_account - force account selection (in practice, the logout + fresh login path the chooser already uses).

Why it was deferred (design constraints)

  • The Hydra SDK's OIDC context (OAuth2ConsentRequestOpenIdConnectContext) does not expose prompt or max_age; they have to be parsed out of request_url (the same pattern as the existing organization_id parsing).
  • skip alone can't drive this: skip=false is also the normal first-time SSO case, so forcing re-auth on skip=false would break SSO. The prompt/max_age values must be read explicitly.
  • Loop-breaking is the hard part. Forcing re-auth on prompt=login and bouncing to /login returns to the same login_challenge with prompt=login still in request_url, which re-forces indefinitely. max_age=N (N>0) breaks the loop naturally because authenticated_at updates after a refresh login (state-based, like the AAL2 step-up); prompt=login (and max_age=0) need a one-shot marker carried on Forseti's own return_to to break it.
  • prompt=select_account is accepted but ignored by Hydra (ory/hydra#1943), so Forseti must drive it itself (the chooser's logout + login path).
  • max_age=0 has a Hydra skip bug (ory/hydra#3034); parsing request_url ourselves sidesteps it.

Notes

/login already supports refresh=true (Kratos privileged re-auth), which is the mechanism to force credential re-entry; the carve-out in auth/login.rs already prevents the bounce-back when refresh=true.

Split out from #2 (the consent-screen "use a different account" affordance and the Phase 1 account chooser shipped in PR #3; this is the remaining optional thread). `/oauth/login` (`src/oauth/login.rs`) accepts the existing Kratos session whenever one is present and only forces re-auth for an `acr_values=aal2` step-up. It does not honour the OIDC `prompt` and `max_age` parameters, so a relying party cannot force re-authentication or account selection. ### Goal Honour, at the Hydra login phase: - `prompt=login` - force a fresh authentication even when a Kratos session exists. - `max_age=N` - force re-auth when the session's `authenticated_at` is older than N seconds. - `prompt=select_account` - force account selection (in practice, the logout + fresh login path the chooser already uses). ### Why it was deferred (design constraints) - The Hydra SDK's OIDC context (`OAuth2ConsentRequestOpenIdConnectContext`) does not expose `prompt` or `max_age`; they have to be parsed out of `request_url` (the same pattern as the existing `organization_id` parsing). - `skip` alone can't drive this: `skip=false` is also the normal first-time SSO case, so forcing re-auth on `skip=false` would break SSO. The `prompt`/`max_age` values must be read explicitly. - Loop-breaking is the hard part. Forcing re-auth on `prompt=login` and bouncing to `/login` returns to the same `login_challenge` with `prompt=login` still in `request_url`, which re-forces indefinitely. `max_age=N` (N>0) breaks the loop naturally because `authenticated_at` updates after a refresh login (state-based, like the AAL2 step-up); `prompt=login` (and `max_age=0`) need a one-shot marker carried on Forseti's own `return_to` to break it. - `prompt=select_account` is accepted but ignored by Hydra (ory/hydra#1943), so Forseti must drive it itself (the chooser's logout + login path). - `max_age=0` has a Hydra skip bug (ory/hydra#3034); parsing `request_url` ourselves sidesteps it. ### Notes `/login` already supports `refresh=true` (Kratos privileged re-auth), which is the mechanism to force credential re-entry; the carve-out in `auth/login.rs` already prevents the bounce-back when `refresh=true`.
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#4
No description provided.