Skip to content

OAuth2 Consent Screen

The consent screen allows users to review and approve the scopes an OAuth2 client is requesting before an authorization code is issued. Consent is per-client and per-user – once granted, it is remembered and not re-prompted unless scopes change.

sequenceDiagram
    actor User
    participant Browser
    participant IdP as Autentico

    User->>Browser: Complete login (password / passkey / MFA)
    Browser->>IdP: POST /oauth2/login
    IdP->>IdP: Authentication successful
    IdP->>Browser: 302 -> /oauth2/consent?client_id=X&scope=Y&...
    Browser->>IdP: GET /oauth2/consent (render consent page)
    IdP->>Browser: Show requested scopes + Allow / Deny buttons
    User->>Browser: Click "Allow"
    Browser->>IdP: POST /oauth2/consent (action=allow)
    IdP->>IdP: Store consent in user_consents table
    IdP->>Browser: 302 -> redirect_uri?code=AUTH_CODE

If the user clicks Deny, Autentico redirects back to the client with error=access_denied per RFC 6749 Section 4.1.2.1.

Consent is controlled by the consent_required field on the client. When consent_required is true, users must approve scopes before an authorization code is issued.

Set it when creating or updating a client:

Terminal window
curl -X POST https://auth.example.com/admin/api/clients \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"client_id": "my-app",
"client_name": "My Application",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scopes": "openid profile email offline_access",
"client_type": "public",
"token_endpoint_auth_method": "none",
"consent_required": true
}'

When consent_required is false or not set, consent is skipped after authentication unless the request includes prompt=consent. That prompt requires an explicit decision even for a first-party client.

The consent screen displays human-readable descriptions for standard scopes:

Scope Description shown to user
openid Verify your identity
profile View your profile information
email View your email address
address View your address
phone View your phone number
offline_access Stay signed in between sessions

Custom scopes are displayed by their raw scope name.

When a user approves a consent request, the decision is stored in the user_consents table with the following fields:

Column Description
id Unique consent record ID
user_id The user who granted consent
client_id The client that received consent
scopes Space-separated list of approved scopes
granted_at Timestamp of when consent was granted

A unique constraint on (user_id, client_id) ensures only one consent record exists per user-client pair. When a user re-approves, the existing record is updated with the new scopes and timestamp.

Autentico skips the consent screen if all of these conditions are true:

  1. The user has a stored consent record for the client
  2. All requested scopes are covered by the previously granted scopes
  3. The prompt parameter does not include consent

Re-consent is triggered when:

  • New scopes are requested – if the client requests a scope not covered by the stored consent, the user is re-prompted
  • prompt=consent – per OIDC Core Section 3.1.2.1, the prompt=consent parameter forces re-prompting even if consent was previously granted
  • No prior consent exists – the user has never approved scopes for this client

Consent URLs are internal continuations issued after successful login or eligible SSO. A signed consent_flow binds the exact authorization parameters to the browser session for ten minutes. An old session cookie and arbitrary GET parameters cannot create a new consent request. Applications should begin at /oauth2/authorize.

Opening and submitting consent recheck current client status, redirect URI, code flow, scopes and PKCE, along with active user status, local MFA, the current client idle timeout, global session lifetime and any positive max_age. An expired login or continuation requires restarting authorization.

Fresh login can continue to consent with SSO disabled or prompt=login / max_age=0. An SSO continuation cannot substitute for fresh login. prompt=none never opens consent: it returns login_required when authentication is needed, or consent_required when an eligible session needs approval.

The form’s HMAC-SHA256 consent_sig covers every authorization field, including prompt, max_age, continuation and user ID. Submission requires an explicit Allow or Deny action. Changing parameters or browser sessions invalidates the continuation.

Token auth_time reflects actual browser authentication, including after waiting on consent or using SSO. Code exchange and token refreshes preserve that time; issuing a token does not authenticate the user again.