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 OAuth2 & OpenID Connect 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.
This guide uses URLs from the 5 Minute Tutorial:
- Ory OAuth2 & OpenID Connect Public Endpoint: http://127.0.0.1:4444
- Ory OAuth2 & OpenID Connect Admin Endpoint: http://127.0.0.1:4445
When translating this guide into your own environment, make sure to use the correct endpoint for your interactions.
- UI
- HTML
- JavaScript
<a href="https://<hydra-public>/oauth2/auth?client_id=...&response_type=...&scope=...">
Login in with Ory OAuth2 & OpenID Connect</a
>
// ...
window.location.href = "https://public.hydra.url/oauth2/auth?client_id=...&response_type=...&scope=..."
Redirection to the login endpoint
The next task for Ory OAuth2 & OpenID Connect is to know the user of the request. To achieve that, Ory OAuth2 & OpenID Connect
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 OAuth2 & OpenID Connect redirects the browser to the "Login Endpoint" established in your config:
# Can also be set using the environment variable:
# URLS_LOGIN=https://login-app/login
urls:
login: https://login-app/login
Ory OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 belogin_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 OAuth2 & OpenID Connect to force the End-User to log in using the Login HTML Form in the Login Endpoint.prompt=consent
instructs Ory OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect, see [#].
max_age
(optional) specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated by Ory OAuth2 & OpenID Connect. 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 OAuth2 & OpenID Connect 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, typicallylogin_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.
- UI
- curl
- Login Challenge Response
curl "http://127.0.0.1:4445/oauth2/auth/requests/login?login_challenge=7bb518c4eec2454dbb289f5fdb4c0ee2"
Check the "Implementing the Login Endpoint" Guide for examples using the Ory OAuth2 & OpenID Connect SDK in different languages.
{
"challenge": "7bb518c4eec2454dbb289f5fdb4c0ee2",
"requested_scope": ["openid", "offline"],
"requested_access_token_audience": null,
"skip": false,
"subject": "",
"oidc_context": {},
"client": {
"client_id": "auth-code-client",
"client_name": "",
"redirect_uris": ["http://127.0.0.1:5555/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code", "id_token"],
"scope": "openid offline",
"audience": null,
"owner": "",
"policy_uri": "",
"allowed_cors_origins": null,
"tos_uri": "",
"client_uri": "",
"logo_uri": "",
"contacts": null,
"client_secret_expires_at": 0,
"subject_type": "public",
"token_endpoint_auth_method": "client_secret_basic",
"userinfo_signed_response_alg": "none",
"created_at": "2020-07-08T12:31:47Z",
"updated_at": "2020-07-08T12:31:47Z"
},
"request_url": "http://127.0.0.1:4444/oauth2/auth?audience=&client_id=auth-code-client&max_age=0&nonce=hognfveoohhddoralbeygsjg&prompt=&redirect_uri=http%3A%2F%2F127.0.0.1%3A5555%2Fcallback&response_type=code&scope=openid+offline&state=imnweycejbfpyrmnahgqzcmm",
"session_id": "d3c98aa6-67ae-478b-bc30-f7887b58f630"
}
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)
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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect 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 OAuth2 & OpenID Connect SDK in different languages.
Redirection to the consent endpoint
Please head over to Consent Flow!
Revoking Ory OAuth2 & OpenID Connect login sessions
Revoking a login session will remove all of the user's cookies at Ory OAuth2 & OpenID Connect 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.
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.