Implementing Login, Consent & Logout UI
Let's build a simple consent app that can be used as part of the Hydra's Login and consent workflow.
#
OAuth 2.0 Authorize Code FlowBefore anything happens, the OAuth 2.0 Authorize Code Flow is initiated by an
OAuth 2.0 Client. This usually works by generating a URL in the form of
https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...
.
Then, the OAuth 2.0 Client points the end user's user agent to that URL.
Next, the user agent (browser) opens that URL.
#
User LoginAs the user agent hits the URL, ORY Hydra checks if a session cookie is set
containing information about a previously successful login. Additionally,
parameters such as id_token_hint
, prompt
, and max_age
are evaluated and
processed.
Next, the user will be redirect to the Login Provider which was set using the
OAUTH2_LOGIN_URL
environment variable. For example, the user is redirected to
https://login-provider/login?login_challenge=1234
if
OAUTH2_LOGIN_URL=https://login-provider/login
. This redirection happens
always and regardless of whether the user has a valid login session or if the
user needs to authenticate.
The service which handles requests to https://login-provider/login
must first
fetch information on the authentication request using a REST API call. Please be
aware that for reasons of brevity, the following code snippets are pseudo-code.
For a fully working example, check out our reference
User Login & Consent Provider implementation.
The endpoint handler at /login
must not remember previous sessions. This
task is solved by ORY Hydra. If the REST API call tells you to show the login
ui, you must show it. If the REST API tells you to not show the login ui,
you must not show it. Again, do not implement any type of session here.
The server response is a JSON object with the following keys:
For a full documentation on all available keys, please head over to the API documentation (make sure to select the right API version).
Depending of whether or not skip
is true, you will prompt the user to log in
by showing him/her a username/password form, or by using some other proof of
identity.
If skip
is true, you should not show a user interface but accept the login
request directly by making a REST call. You can use this step to update some
internal count of how often a user logged in, or do some other custom business
logic. But again, do not show the user interface.
To accept the login request, do something along the lines of:
You may also choose to deny the login request. This is possible regardless of
the skip
value.
#
User ConsentNow that we know who the user is, we must ask the user if he/she wants to grant the requested permissions to the OAuth 2.0 Client. To do so, we check if the user has previously granted that exact OAuth 2.0 Client the requested permissions. If the user has never granted any permissions to the client, or the client requires new permissions not previously granted, the user must visually confirm the request.
This works very similar to the User Login Flow. First, the user will be redirect
to the Consent Provider which was set using the OAUTH2_CONSENT_PROVIDER
environment variable. For example, the user is redirected to
https://consent-provider/consent?consent_challenge=1234
if
OAUTH2_CONSENT_PROVIDER=https://consent-provider/consent
. This redirection
happens always and regardless of whether the user has a valid login session or
if the user needs to authorize the application or not.
The service which handles requests to https://consent-provider/consent
must
first fetch information on the consent request using a REST API call. Please be
aware that for reasons of brevity, the following code snippets are pseudo-code.
For a fully working example, check out our reference
User Login, Logout & Consent Provider implementation.
The server response is a JSON object with the following keys:
If skip is true, you should not show any user interface to the user. Instead, you should accept (or deny) the consent request. Typically, you will accept the request unless you have a very good reason to deny it (e.g. the OAuth 2.0 Client is banned).
If skip is false and you show the consent screen, you should use the
requested_scope
array to display a list of permissions which the user must
grant (e.g. using a checkbox). Some people choose to always skip this step if
the OAuth 2.0 Client is a first-party client - meaning that the client is used
by you or your developers in an internal application.
Assuming the user accepts the consent request, the code looks very familiar to the User Login Flow.
You may also choose to deny the consent request. This is possible regardless of
the skip
value.
Once the user agent is redirected back, the OAuth 2.0 flow will be finalized.
#
User LogoutORY Hydra supports OpenID Connect Front-Channel Logout 1.0 and OpenID Connect Back-Channel Logout 1.0 flows.
A logout request may be initiated by the OpenID Provider (OP - you) or by the Relying Party (RP - the OAuth2 Client):
- The OP-initiated flow does not need an
id_token_hint
, and it may neither define astate
nor apost_logout_redirect_uri
. - The RP-initiated flow needs an
id_token_hint
and may optionally definestate
andpost_logout_redirect_uri
.
Both requests follow the same pattern as user login and user consent. Before the logout is completed, the user is redirected to the Logout UI (similar to Login UI and Consent UI) to confirm the logout request.
There are several possible pathways for executing this flow, explained in the following diagram:
Legend:
*
: This is a query parameter, for example/oauth2/sessions/logout?id_token_hint=...
**
Here, an "active session" implies that there has been at least one login request completed withremember: true
for that user. If that's not the case, the system "does not know" what to do (because there has never been a session issued that was remembered - hence it's not possible to forget it).***
: Here, the "valid session cookies" implies that the browser has a valid authentication cookie when calling/oauth2/sessions/logout
. If you have problems at this step, check if there is a cookieoauth2_authentication_session
for the domain ORY Hydra is running at. Do not mix up IP (e.g.127.0.0.1
,192.168.1.1
) addresses and FQDNs (e.g.localhost
,google.com
).****
: Thepost_logout_redirect
defaults to the configuration value ofurls.post_logout_redirect
. If it's an RP-initiated flow and apost_logout_redirect_uri
was set and that URL is in the array of the OAuth2 Client'surls.post_logout_redirect
, the browser will be redirected there instead.
#
Logout Flow- A user-agent (browser) requests the logout endpoint
(
/oauth2/sessions/logout
). If the request is done on behalf of a RP:- The URL query MUST contain an ID Token issued by ORY Hydra as the
id_token_hint
:/oauth2/sessions/logout?id_token_hint=...
- The URL query MAY contain key
post_logout_redirect_uri
indicating where the user agent should be redirected after the logout completed successfully. Each OAuth 2.0 Client can whitelist a list of URIs that can be used as the value using thepost_logout_redirect_uris
metadata field:/oauth2/sessions/logout?id_token_hint=...&post_logout_redirect_uri=https://i-must-be-whitelisted/
- If
post_logout_redirect_uri
is set, the URL query SHOULD contain astate
value. On successful redirection, this state value will be appended to thepost_logout_redirect_uri
. The functionality is equal to thestate
parameter when performing OAuth2 flows.
- The URL query MUST contain an ID Token issued by ORY Hydra as the
- The user-agent is redirected to the logout provider URL (configuration item
urls.logout
) and contains a challenge:https://my-logout-provider/logout?challenge=...
- The logout provider uses the
challenge
query parameter to fetch metadata about the request. The logout provider may choose to show a UI where the user has to accept the logout request. Alternatively, the logout provider MAY choose to silently accept the logout request. - To accept the logout request, the logout provider makes a
PUT
call to/oauth2/auth/requests/logout/accept?challenge=...
. No request body is required. - The response contains a
redirect_to
value where the logout provider redirects the user back to. - ORY Hydra performs OpenID Connect Front- and Back-Channel logout.
- The user agent is being redirected to a specified redirect URL. This may
either be the default redirect URL set by
urls.post_logout_redirect
or to the value specified by query parameterpost_logout_redirect_uri
.
This endpoint does not remove any Access/Refresh Tokens.
#
Logout Provider Example (NodeJS Pseudo-code)Following step 1 from the flow above, the user-agent is redirected to the logout
provider (e.g. https://my-logout-provider/logout?challenge=...
). Next, the
logout provider fetches information about the logout request:
The server response is a JSON object with the following keys:
Next, the logout provider should decide if the end-user should perform a UI
action such as confirming the logout request. It is RECOMMENDED to request
logout confirmation from the end-user when rp_initiated
is set to true.
When the logout provider decides to accept the logout request, the flow is completed as follows:
You can also reject a logout request (e.g. if the user chose to not log out):
If the logout request was granted and the user agent redirected back to ORY Hydra, all OpenID Connect Front-/Back-channel logout flows (if set) will be performed and the user will be redirect back to his/her final destination.
OpenID Connect Front-Channel Logout 1.0#
In summary
(read the spec)
this feature allows an OAuth 2.0 Client to register fields
frontchannel_logout_uri
and frontchannel_logout_session_required
.
If frontchannel_logout_uri
is set to a valid URL (the host, port, path must
all match those of one of the Redirect URIs), ORY Hydra will redirect the
user-agent (typically browser) to that URL after a logout occurred. This allows
the OAuth 2.0 Client Application to log out the end-user in its own system as
well - for example by deleting a Cookie or otherwise invalidating the user
session.
ORY Hydra always appends query parameters values iss
and sid
to the
Front-Channel Logout URI, for example:
Each OpenID Connect ID Token is issued with a sid
claim that will match the
sid
value from the Front-Channel Logout URI.
ORY Hydra will automatically execute the required HTTP Redirects to make this work. No extra work is required.
OpenID Connect Back-Channel Logout 1.0#
In summary
(read the spec)
this feature allows an OAuth 2.0 Client to register fields
backchannel_logout_uri
and backchannel_logout_session_required
.
If backchannel_logout_uri
is set to a valid URL, a HTTP Post request with
Content-Type application/x-www-form-urlencoded
and a logout_token
will be
made to that URL when a end-user logs out. The logout_token
is a JWT signed
with the same key that is used to sign OpenID Connect ID Tokens. You should thus
validate the logout_token
using the ID Token Public Key (can be fetched from
/.well-known/jwks.json
). The logout_token
contains the following claims:
iss
- Issuer Identifier, as specified in Section 2 of [OpenID.Core].aud
- Audience(s), as specified in Section 2 of [OpenID.Core].iat
- Issued at time, as specified in Section 2 of [OpenID.Core].jti
- Unique identifier for the token, as specified in Section 9 of [OpenID.Core].events
- Claim whose value is a JSON object containing the member name http://schemas.openid.net/event/backchannel-logout. This declares that the JWT is a Logout Token. The corresponding member value MUST be a JSON object and SHOULD be the empty JSON object {}.sid
- Session ID - String identifier for a Session. This represents a Session of a User Agent or device for a logged-in End-User at an RP. Different sid values are used to identify distinct sessions at an OP. The sid value need only be unique in the context of a particular issuer. Its contents are opaque to the RP. Its syntax is the same as an OAuth 2.0 Client Identifier.
An exemplary Back-Channel Logout Request looks as follows:
The Logout Token must be validated as follows:
- Validate the Logout Token signature in the same way that an ID Token signature is validated, with the following refinements.
- Validate the iss, aud, and iat Claims in the same way they are validated in ID Tokens.
- Verify that the Logout Token contains a sid Claim.
- Verify that the Logout Token contains an events Claim whose value is JSON object containing the member name http://schemas.openid.net/event/backchannel-logout.
- Verify that the Logout Token does not contain a nonce Claim.
- Optionally verify that another Logout Token with the same jti value has not been recently received.
The endpoint then returns a HTTP 200 OK response. Cache-Control headers should be set to:
Because the OpenID Connect Back-Channel Logout Flow is not executed using the user-agent (e.g. Browser) but from ORY Hydra directly, the session cookie of the end-user will not be available to the OAuth 2.0 Client and the session has to be invalidated by some other means (e.g. by blacklisting the session ID).
#
Revoking consent and login sessions#
LoginYou can revoke 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.
Revoking the login sessions of a user is as easy as sending DELETE
to/oauth2/auth/sessions/login?subject={subject}
.
This endpoint is not compatible with OpenID Connect Front-/Backchannel logout and does not revoke any tokens.
#
ConsentYou 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.
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}
.
#
OAuth 2.0#
OAuth 2.0 ScopeThe scope of an OAuth 2.0 scope defines the permission the token was granted by the end-user. For example, a specific token might be allowed to access public pictures, but not private ones. The granted permissions are established during the consent screen.
Additionally, ORY Hydra has pre-defined OAuth 2.0 Scope values:
offline_access
: Include this scope if you wish to receive a refresh tokenopenid
: Include this scope if you wish to perform an OpenID Connect request.
When performing an OAuth 2.0 Flow where the end-user is involved (e.g. Implicit
or Authorize Code), the granted OAuth 2.0 Scope must be set when accepting the
consent using the grant_scope
key.
A OAuth 2.0 Scope is not a permission:
- A permission allows an actor to perform a certain action in a system: Bob is allowed to delete his own photos.
- OAuth 2.0 Scope implies that an end-user granted certain privileges to a client: Bob allowed the OAuth 2.0 Client to delete all users.
The OAuth 2.0 Scope can be granted without the end-user actually having the right permissions. In the examples above, Bob granted an OAuth 2.0 Client the permission ("scope") to delete all users in his name. However, since Bob is not an administrator, that permission ("access control") is not actually granted to Bob. Therefore any request by the OAuth 2.0 Client that tries to delete users on behalf of Bob should fail.
#
OAuth 2.0 Access Token AudienceThe Audience of an Access Token refers to the Resource Servers that this token is intended for. The audience usually refers to one or more URLs such as
https://api.mydomain.com/blog/posts
https://api.mydomain.com/users
but may also refer to a subset of resources:
https://api.mydomain.com/tenants/foo/users
When performing an OAuth 2.0 Flow where the end-user is involved (e.g. Implicit
or Authorize Code), the granted audience must be set when accepting the consent
using the grant_access_token_audience
key. In most cases, it is ok to grant
the audience without user-interaction.
#
OAuth 2.0 Refresh TokensOAuth 2.0 Refresh Tokens are issued only when an Authorize Code Flow
(response_type=code
) or an OpenID Connect Hybrid Flow with an Authorize Code
Response Type (response_type=code+...
) is executed. OAuth 2.0 Refresh Tokens
are not returned for Implicit or Client Credentials grants:
- Capable of issuing an OAuth 2.0 Refresh Token:
- https://ory-hydra.example/oauth2/auth?response_type=code&...
- https://ory-hydra.example/oauth2/auth?response_type=code+token&...
- https://ory-hydra.example/oauth2/auth?response_type=code+token+id_token&...
- https://ory-hydra.example/oauth2/auth?response_type=code+id_token&...
- Will not issue an OAuth 2.0 Refresh Token
- https://ory-hydra.example/oauth2/auth?response_type=token&...
- https://ory-hydra.example/oauth2/auth?response_type=token+id_token&...
- https://ory-hydra.example/oauth2/token?grant_type=client_redentials&...
Additionally, each OAuth 2.0 Client that wants to request an OAuth 2.0 Refresh
Token must be allowed to request scope offline_access
. When performing an
OAuth 2.0 Authorize Code Flow, the offline_access
value must be included in
the requested OAuth 2.0 Scope:
When accepting the consent request, offline_access
must be in the list of
grant_scope
:
Refresh Token Lifespan can be set using configuration key ttl.refresh_token
.
If set to -1, Refresh Tokens never expire.
#
OAuth 2.0 Token IntrospectionOAuth2 Token Introspection is an IETF standard. It defines a method for a protected resource to query an OAuth 2.0 authorization server to determine the active state of an OAuth 2.0 token and to determine meta-information about this token. OAuth 2.0 deployments can use this method to convey information about the authorization context of the token from the authorization server to the protected resource.
You can find more details on this endpoint in the
ORY Hydra API Docs. You can also use the CLI command
hydra token introspect <token>
.
#
OAuth 2.0 ClientsYou can manage OAuth 2.0 clients using the cli or the HTTP REST API:
- CLI:
hydra help clients
- REST: Read the API Docs
#
ExamplesThis section provides a few examples to get you started with the most-used OAuth 2.0 Clients:
#
Authorize Code Flow with Refresh TokenThe following command creates an OAuth 2.0 Client capable of executing the Authorize Code Flow, requesting ID and Refresh Tokens and performing the OAuth 2.0 Refresh Grant:
The OAuth 2.0 Client will be allowed to use values http://my-app.com/callback
and http://my-other-app.com/callback
as redirect_url
.
It is expected that the OAuth 2.0 Client sends its credentials using HTTP Basic Authorization.
If you wish to send credentials in the POST Body, add the following flag to the command above:
The same can be achieved by setting
"token_endpoint_auth_method": "client_secret_post"
in the the request body of
POST /clients
and PUT /clients/<id>
.
#
Client Credentials FlowA client only capable of performing the Client Credentials Flow can be created as follows:
#
OpenID Connect#
UserinfoThe /userinfo
endpoint returns information on a user given an access token.
Since ORY Hydra is agnostic to any end-user data, the /userinfo
endpoint
returns only minimal information per default:
Any information set to the key session.id_token
during accepting the consent
request will also be included here.
By making the /userinfo
call with a token issued by this consent request, one
would receive:
You should only include data that has been authorized by the end-user through an
OAuth 2.0 Scope. If an OAuth 2.0 Client, for example, requests the phone
scope
and the end-user authorizes that scope, the phone number should be added to
session.id_token
.
Be aware that the
/userinfo
endpoint is public. Its contents are thus as publicly visible as those of ID Tokens. It is therefore imperative to not expose sensitive information without end-user consent.