Skip to content
Kenta Ishizaki edited this page Aug 14, 2026 · 3 revisions

Prompt and Max Age

OIDC Core 1.0 §3.1.2.1 defines two authorization parameters that control when the user must (re)authenticate: prompt and max_age. This page describes how the gem handles them and which configuration each value requires.

Both are handled on the authorization endpoint (GET /oauth/authorize) when the request is a valid OpenID Connect request, i.e. it includes the openid scope. Plain OAuth requests without openid ignore prompt unless you opt in via apply_prompt_to_non_oidc_requests; max_age enforcement is always OIDC-only, since it is defined by OIDC Core.

prompt

prompt is a space-delimited list. Multiple values are processed in the priority order none, consent, login, select_account; combining none with any other value is rejected with invalid_request, as required by the spec.

Value Behavior Required configuration
none No UI may be shown. Without a signed-in user the request fails with login_required; if the consent screen would be shown it fails with consent_required. When the user already has an active token for this client covering the requested scopes (a non-strict superset counts), authorization is auto-approved instead of rendering the consent form.
login Forces reauthentication of the current user by calling reauthenticate_resource_owner. reauthenticate_resource_owner
consent Re-renders the consent screen even when it would normally be skipped (e.g. skip_authorization or an existing matching token). Under Doorkeeper's api_only mode the pre-authorization is answered as JSON instead (doorkeeper-openid_connect >= 2.0; older versions answered with an empty 200, see Troubleshooting).
select_account Triggers account selection by calling select_account_for_resource_owner. select_account_for_resource_owner
create Accepted but not supported — treated as a no-op rather than an error.
anything else Rejected with invalid_request.

reauthenticate_resource_owner

The block is executed in the controller's scope and receives the resource owner and a return_to URL — a path-relative URL rebuilding the original authorization request (request path plus query string, not an absolute URL) with the just-handled prompt value removed, so that finishing the login doesn't trigger reauthentication again:

# config/initializers/doorkeeper_openid_connect.rb
Doorkeeper::OpenidConnect.configure do
  # ...
  reauthenticate_resource_owner do |resource_owner, return_to|
    store_location_for resource_owner, return_to
    sign_out resource_owner
    redirect_to new_user_session_url
  end
end

The block must render or redirect. If it completes without producing a response, the request fails with login_required — a deliberate backstop, since silently continuing would defeat the reauthentication the client asked for.

select_account_for_resource_owner

Same contract as above: controller scope, (resource_owner, return_to), and it must render or redirect — otherwise the request fails with account_selection_required (OIDC Core §3.1.2.6). Note that since v1.10.2 the block is also called when no user is signed in, with nil as the first argument, so it must handle both cases:

select_account_for_resource_owner do |resource_owner, return_to|
  store_location_for(resource_owner, return_to) if resource_owner
  redirect_to account_picker_url
end

max_age

max_age is the maximum number of seconds since the user last authenticated. When the user's authentication is older (or its time cannot be determined), the gem forces reauthentication through the same reauthenticate_resource_owner block — except under prompt=none, where showing UI is forbidden and the request fails with login_required instead.

The authentication time is resolved in this order:

  1. auth_time_from_session — receives (session, request) in the controller's scope and returns the time the current session authenticated (return nil to force reauthentication). This is the correct source when a user can hold multiple concurrent sessions.
  2. auth_time_from_resource_owner — legacy fallback receiving the resource owner. Deprecated for max_age enforcement (it returns the most recent login on any device, so a stale session can inherit a fresh login from another one — see #150); a deprecation warning is emitted once per process. It remains fully supported for populating the auth_time claim on the ID Token.

A typical setup captures the time at login and surfaces it in the config:

# app/controllers/users/sessions_controller.rb (or your login handler)
session[:auth_time] = Time.current.to_i
# config/initializers/doorkeeper_openid_connect.rb
Doorkeeper::OpenidConnect.configure do
  # ...
  auth_time_from_session do |session, _request|
    session[:auth_time]
  end
end

Details worth knowing:

  • max_age is evaluated before prompt handling — so e.g. prompt=consent with a stale authentication triggers reauthentication first, and the consent re-render happens on the follow-up request after login.
  • max_age=0 is treated as a floor of one second (clock-skew allowance), i.e. it effectively demands a just-now authentication.
  • A malformed, non-scalar max_age (e.g. max_age[]=1) is treated as absent on doorkeeper-openid_connect >= 2.0; older versions raised a 500.
  • auth_time_from_access_token only affects the auth_time claim on the ID Token; it is never used for max_age enforcement.

Error responses

Failures are reported to the client as standard authentication error responses: a redirect to the redirect_uri carrying error (login_required, consent_required, account_selection_required, or invalid_request) and the client's state, in the query or fragment as appropriate for the response type. On doorkeeper-openid_connect >= 2.0, only these protocol errors are redirected to the client — internal problems such as a missing configuration surface as a 500 instead of leaking to the client as a spurious authorization error — and the RFC 9207 iss parameter is included on error redirects when Doorkeeper (6.0+) is configured with an issuer.

See also

Clone this wiki locally