Skip to main content

Login 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 doesn't contain a database with End-Users but instead uses HTTP Redirection to "delegate" the login 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.

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

Initiating the OAuth 2.0 / OpenID Connect flow

The OAuth2 2.0 / OpenID Connect Flow is initiated by pointing the End-User's browser to the /oauth2/auth endpoint. Depending on which flow ("Authorize Code Flow", "Implicit Flow", ...) you want to use some of the query parameters (for example /oauth2/auth?response_type=code, /oauth2/auth?response_type=token, ...) might change but the overall initiation works always by sending the browser to that URL.

note

This guide uses URLs from the 5 Minute Tutorial:

When translating this guide into your own environment, make sure to use the correct endpoint for your interactions.

OAuth 2.0 Client

Redirection to the login endpoint

The next task for Ory Hydra is to know the user of the request. To achieve that, Ory Hydra checks if a session cookie is set containing information about a previously successful login. Additionally, OpenID Connect parameters id_token_hint, prompt, and max_age are evaluated and processed. Depending on their values and the login state, the user might need to re-authenticate or the flow will fail.

To authenticate the user (this happens regardless of whether a session exists for the user or not), Ory Hydra redirects the browser to the "Login Endpoint" established in your config:

"hydra
# Can also be set using the environment variable:
# URLS_LOGIN=https://login-app/login
urls:
login: https://login-app/login

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

https://login-app/login?login_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2

Login sessions, prompt, max_age, id_token_hint

Ory Hydra keeps track of user sessions. It does so by issuing a cookie which contains the user ID. On subsequent OAuth2 / OpenID Connect flows, the session will be checked and the Login Endpoint will be instructed to, for example, show the Login HTML Form or skip the Login HTML Form.

Ory Hydra supports the following OpenID Connect query parameters:

  • prompt (optional). Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
    • prompt=none instructs Ory Hydra to not display the login or consent user interface pages. An error is returned if an End-User isn't already authenticated or the Client doesn't have pre-configured consent for the requested Claims or doesn't fulfill other conditions for processing the request. The error code will typically be login_required, interaction_required, or another code. This can be used as a method to check for existing authentication and/or consent.
    • prompt=login instructs Ory Hydra to force the End-User to log in using the Login HTML Form in the Login Endpoint.
    • prompt=consent instructs Ory Hydra to force the End-User to re-authorize (give consent) the OAuth2 Client using the Consent HTML Form in the Consent Endpoint.
    • prompt=select_account is not supported in Ory Hydra, see [#].
  • max_age (optional) specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated by Ory Hydra. If the elapsed time is greater than this value, the Login HTML Form must be shown and the End-User must re-authenticate.
  • id_token_hint (optional) - ID Token previously issued by Ory Hydra being passed as a hint about the End-User's current or past authenticated session with the Client. If the End-User identified by the ID Token is logged in or is logged in by the request, then the Authorization Server returns a positive response; otherwise, it returns an error, typically login_required. It does not matter if the ID Token is expired or not.

To specify these parameters add them to the OAuth2 Auth Endpoint URL Query:

https://<hydra-public>/oauth2/auth?prompt=login&max_age=60&id_token_hint=...'

The login endpoint

The Login Endpoint (set by urls.login) is an application written by you. You can find an exemplary Node.js reference implementation on our GitHub.

The Login Endpoint uses the login_challenge value in the URL to fetch information about the login request by making a HTTP GET request to:

http(s)://<HYDRA_ADMIN_URL>/oauth2/auth/requests/login?login_challenge=<challenge>

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

OAuth 2.0 Login UI Screen

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

<form action="/login" method="post">
<input type="hidden" name="csrf_token" value="...." />
<!-- Use CSRF tokens in your HTML forms! -->
<input type="email" name="login_email" placeholder="Please enter your email address to log in" />
<input type="password" name="login_password" />
<input type="checkbox" name="remember" value="Remember me on this device" />
<input type="submit" value="Log in" />
</form>

Once the End-User authenticated successfully, you either accept the login challenge, or you reject (for example the user isn't allowed to perform OAuth2 flows) the login challenge.

Accepting the login flow

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

danger

The subject must be an immutable user ID, it should never be an email address, a username, or something else that may change over time.

{
// Subject is the user ID of the end-user that authenticated.
"subject": "string", // required!

// All values below are optional!

// Remember, if set to true, tells Ory Hydra to remember this user by telling the user agent (browser) to store
// a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she won't be asked to log in again.
"remember": true,

// RememberFor sets how long the authentication should be remembered for in seconds. If set to 0,
// the authorization will be remembered for the duration of the browser session (using a session cookie).
"remember_for": 0,

// ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication.
"acr": "string",

// Context is an optional object which can hold arbitrary data. The data will be made available when fetching the
// consent request under the "context" field. This is useful in scenarios where login and consent endpoints share
// data.
"context": {
// "foo": "bar"
},

// ForceSubjectIdentifier forces the "pairwise" user ID of the end-user that authenticated. The "pairwise"
// user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg]
// of the OpenID Connect specification. It allows you to set an obfuscated subject ("user") identifier that's unique
// to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token.
// It doesn't change the sub claim in the OAuth 2.0 Introspection. Per default, Ory Hydra handles this value with its own algorithm.
// In case you want to set this yourself you can use this field. Please note that setting this field has no effect if pairwise isn't
// configured in Ory Hydra or the OAuth 2.0 Client doesn't expect a pairwise identifier (set via subject_type key in the client's configuration).
// Please also be aware that Ory Hydra is unable to properly compute this value during authentication. This implies that
// you have to compute this value on every authentication process (probably depending on the client ID or some other unique value).
// If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.
"force_subject_identifier": "string"
}

With curl this might look like the following request:

curl --location --request PUT 'http://127.0.0.1:4445/oauth2/auth/requests/login/accept?login_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2' \
--header 'Content-Type: application/json' \
--data-raw '{
"subject": "the-user-id-that-just-logged-in",
"remember": true,
"remember_for": 3600
}'

The server responds with JSON

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

which is the URL your application must redirect 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 login flow

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

{
// The error should follow the OAuth2 error format (for example `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 (for example 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/login/reject?login_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.

Please head over to Consent Flow!

Revoking Ory Hydra login sessions

Revoking a login session will remove all of the user's cookies at Ory Hydra and will require the user to re-authenticate when performing the next OAuth 2.0 Authorize Code Flow. Be aware that this option will remove all cookies from all devices.

info

Revoking a login session doesn't invalidate any Access, Refresh, or ID Tokens! If you log out of GitHub, you won't be logged out of CircleCI/TravisCI.

Revoking the login sessions of a user is as easy as sending DELETE to /oauth2/auth/sessions/login?subject={subject} (see full API documentation).

This endpoint isn't compatible with OpenID Connect Front-/Backchannel logout and doesn't revoke any tokens.