Version: v1.6

Consent Flow

OAuth2 and OpenID Connect require an authenticated End-User session for all OAuth2 / OpenID Connect flows except the client_credentials flow which does not involve End-Users.

ORY Hydra does not contain a database with End-Users but instead uses HTTP Redirection to "delegate" the login and consent flow to another app - we call this the Login & Consent App.

The following short video shows the flow from an End-User's perspective - it includes both login and consent.

info

Please read Login Flow first, as the Login Flow happens before the consent flow.

The following sequence diagram describes the different API calls and HTTP Redirects when performing the OAuth2 flow:

Redirection to the Consent Endpoint

Once the login challenge is accepted, ORY Hydra will ask the user for consent:

Exemplary Consent Screen

The UI is fully under your control, because ORY Hydra redirects the End-User's browser to the c

hydra serve all -c path/to/hydra/config.yml
# Can also be set using the environment variable:
# URLS_CONSENT=https://consent-app/consent
urls:
consent: https://consent-app/consent
note

You can implement the Login and Consent endpoints in the same code base / application. Check out our Login, Consent & Logout NodeJS Reference implementation!

ORY Hydra appends a consent_challenge query parameter to the url. The value is a ID which should later be used by the Consent Endpoint to fetch important information about the request.

https://consent-app/consent?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2

Previous Consent

A consent has four distinctive attributes:

  • The requested scope (/oauth2/auth?...&...&scope=email+profile+offline_access);
  • The OAuth2 Client (/oauth2/auth?client_id=abcd);
  • The End-User (this is the subject set in the Login Flow);
  • Whether or not a previous consent exists, the previous consent has remember: true, and the scope granted by the user (did the user accept all of email, profile, offline_access?)

There are three possible states:

  • The user has never before authorized ("consent") the OAuth2 Client before.
  • The user has authorized ("consent") the OAuth2 Client before and chose to remember the "consent".
  • The user has authorized ("consent") the OAuth2 Client before, and chose to remember the "consent", but the OAuth2 Client now also wants additional permissions ("has changed the token scope" in /oauth2/auth?scope=...).

Regardless of which of these three states we are in, the End-User's browser is always redirected to the consent endpoint. What changes is the skip value, as explained a bit later.

note

In certain scenarios (e.g. a special OAuth2 Client) you might not want to show the consent screen at all. In those cases you can choose to skip showing the UI and just accept the consent. Please keep in mind that OAuth2 is a delegation protocol and that it makes most sense for third-party access. Not showing the consent screen will break OpenID Connect Certification.

The Consent Endpoint

The Consent Endpoint (set by urls.consent) is an application written by you. You can find an exemplary NodeJS reference implementation on our GitHub.

The Consent Endpoint uses the consent_challenge value in the URL to fetch information about the consent request by making a HTTP GET request to:

http(s)://<HYDRA_ADMIN_URL>/oauth2/auth/requests/consent?consent_challenge=<challenge>

The response (see below in "Consent Challenge Response" tab) contains information about the consent request. The body contains a skip value. If the value is false, the user interface must be shown. If skip is true, you should not show the user interface but instead just accept or reject the consent request! For more details about the implementation check the "Implementing the Consent Endpoint" Guide.

The way you collect the consent information from the End-User is up to you. In most cases, you will show an HTML form similar to:

<form action="/consent" method="post">
<input type="hidden" name="csrf_token" value="...." />
<!-- Use CSRF tokens in your HTML forms! -->
<input type="checkbox" name="scope" />
</form>

Once the End-User authenticated successfully, you either accept the login challenge, or you reject (e.g. the user is not allowed to perform OAuth2 flows) the login challenge.

Accepting the Consent Flow

To accept the Consent Challenge, make a HTTP PUT request with Content-Type: application/json and a JSON payload (see Accept Consent Request HTTP API Reference)

{
// The scope granted by the user.
"grant_scope": ["openid", "offline_access"],
// The following fields are optional
// The Access Token Audience if needed. Typically equals the `requested_access_token_audience` field from
// the consent challenge.
"grant_access_token_audience": ["https://my-audience.com"],
// Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same client
// asks the same user for the same, or a subset of, scope.
"remember": true,
// RememberFor sets how long the consent authorization should be remembered for in seconds. If set to 0, the
// authorization will be remembered indefinitely.
"remember_for": 3600,
// Set the data for this consent "session"
"session": {
"access_token": {
"foo": "This field will be available when introspecting the Access Token"
},
"id_token": {
"bar": "This field will be available as a claim in the ID Token"
}
}
}

With curl this might look like the following request:

$ curl --location --request PUT 'http://127.0.0.1:4445/oauth2/auth/requests/consent/accept?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2' \
--header 'Content-Type: application/json' \
--data-raw '{
"grant_scope": ["openid", "offline_access"]
}'

The server responds with JSON

{
"redirect_to": "http://127.0.0.1:4445/oauth2/auth..."
}

which is the URL your application must the End-User's browser to.

Check the "Implementing the Login Endpoint" Guide for examples using the ORY Hydra SDK in different languages.

Rejecting the Consent Flow

To reject the Login Challenge, make a HTTP PUT request with Content-Type: application/json and a JSON payload (see Reject Consent Request HTTP API Reference)

{
// The error should follow the OAuth2 error format (e.g. `invalid_request`, `login_required`).
"error": "user_banned",
// Description of the error in a human readable format.
"error_description": "You are banned!",
// Hint to help resolve the error.
"error_hint": "Contact the site administrator.",
// Debug contains information to help resolve the problem as a developer. Usually not exposed
// to the public but only in the server logs.
"error_debug": "The user was marked banned in the database.",
// Represents the HTTP status code of the error (e.g. 401 or 403)
//
// Defaults to 400
"status_code": 403
}

With curl this might look like the following request:

$ curl --location --request PUT 'http://127.0.0.1:4445/oauth2/auth/requests/consent/reject?consent_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2' \
--header 'Content-Type: application/json' \
--data-raw '{
"error": "user_banned",
"error_debug": "The user was marked banned in the database.",
"error_description": "You are banned!",
"error_hint": "Contact the site administrator.",
"status_code": 403
}'

The server responds with JSON

{
"redirect_to": "http://127.0.0.1:4445/oauth2/auth..."
}

which is the URL your application must the End-User's browser to.

Check the "Implementing the Login Endpoint" Guide for examples using the ORY Hydra SDK in different languages.

Revoking Consent

You can revoke a user's consent either on a per application basis or for all applications.

Revoking the consent will automatically revoke all related access and refresh tokens.

warning

Do not use this endpoint to "invalidate" user sessions. Please revise your approach to and usage of OAuth2 if you use access / refresh tokens as user sessions (i.e. instead of browser cookies).

Revoking all consent sessions of a user is as easy as sending DELETE to /oauth2/auth/sessions/consent?subject={subject}.

Revoking the consent sessions of a user for a specific client is as easy as sending DELETE to /oauth2/auth/sessions/consent?subject={subject}&client={client}.

Last updated on by aeneasr