Logout flow
Ory OAuth2 & OpenID Connect implements OpenID Connect RP-Initiated Logout 1.0 and 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). 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.
A logout request can provide a id_token_hint
and may optionally define state
and post_logout_redirect_uri
. Because of that
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 "doesn't 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 OAuth2 & OpenID Connect is running at. Do not mix up IP (for example127.0.0.1
,192.168.1.1
) addresses and FQDNs (for examplelocalhost
,google.com
).****
: Thepost_logout_redirect
defaults to the configuration value ofurls.post_logout_redirect
. If 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 URL query contains an ID Token issued by Ory OAuth2 & OpenID Connect as theid_token_hint
(/oauth2/sessions/logout?id_token_hint=...
) then:- 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 MAY contain key
- 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 OAuth2 & OpenID Connect 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 doesn't remove any Access/Refresh Tokens.
Logout provider example (Node.js pseudo-code)
Following step 1 from the flow above, the user-agent is redirected to the logout provider (for example
https://my-logout-provider/logout?challenge=...
). Next, the logout provider fetches information about the logout request:
// This is node-js pseudo code and won't work if you copy it 1:1
challenge = req.url.query.logout_challenge
fetch("https://hydra/oauth2/auth/requests/logout?" + querystring.stringify({ logout_challenge: challenge }))
.then(function (response) {
return response.json()
})
.then(function (response) {
// ...
})
The server response is a JSON object with the following keys:
{
// The user for whom the logout was request.
"subject": "user-id",
// The login session ID that was requested to log out.
"sid": "abc..",
// The original request URL.
"request_url": "https://hydra/oauth2/sessions/logout?id_token_hint=...",
// True if the request includes an id_token_hint. False otherwise.
"rp_initiated": true | false
}
Next, the logout provider should decide if the end-user should perform a UI action such as confirming the logout request. It's
STRONGLY RECOMMENDED to request logout confirmation from the end-user when rp_initiated
is set to false
as this signifies that
the end-user requested the logout without any further query parameters. In this situation neither Ory OAuth2 & OpenID Connect nor
the logout provider can implicitly verify whether the end-user willingly requested the logout URL or if they were tricked into
clicking a manipulated link in a vulnerable application. On the other hand, when rp_initiated
is set to true
it's also
RECOMMENDED to request logout confirmation from the end-user, however, based on your Relying Parties (the OAuth2 Clients) this
step could most likely safely be skipped.
When the logout provider decides to accept the logout request, the flow is completed as follows:
fetch("https://hydra/oauth2/auth/requests/logout/accept?" + querystring.stringify({ logout_challenge: challenge }), {
method: "PUT",
})
.then(function (response) {
return response.json()
})
.then(function (response) {
// The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next.
res.redirect(response.redirect_to)
})
You can also reject a logout request (for example if the user chose to not log out):
fetch("https://hydra/oauth2/auth/requests/logout/reject?" + querystring.stringify({ logout_challenge: challenge }), {
method: "PUT",
}).then(function (response) {
// Now you can do whatever you want - redirect the user back to your home page or whatever comes to mind.
})
If the logout request was granted and the user agent redirected back to Ory OAuth2 & OpenID Connect, 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 OAuth2 & OpenID Connect always appends query parameters values iss
and sid
to the Front-Channel Logout URI, for example:
https://rp.example.org/frontchannel_logout
?iss=https://server.example.com
&sid=08a5019c-17e1-4977-8f42-65a12843ea02
Each OpenID Connect ID Token is issued with a sid
claim that will match the sid
value from the Front-Channel Logout URI.
Ory OAuth2 & OpenID Connect will automatically execute the required HTTP Redirects to make this work. No extra work is required.
OpenID Connect Back-Channel Logout
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's 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.
{
"iss": "https://server.example.com",
"aud": "s6BhdRkqt3",
"iat": 1471566154,
"jti": "bWJq",
"sid": "08a5019c-17e1-4977-8f42-65a12843ea02",
"events": {
"http://schemas.openid.net/event/backchannel-logout": {}
}
}
An exemplary Back-Channel Logout Request looks as follows:
POST /backchannel_logout HTTP/1.1
Host: rp.example.org
Content-Type: application/x-www-form-urlencoded
logout_token=eyJhbGci ... .eyJpc3Mi ... .T3BlbklE ...
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're 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 doesn't contain a nonce Claim.
- Optionally verify that another Logout Token with the same jti value hasn't been recently received.
The endpoint then returns a HTTP 200 OK response. Cache-Control headers should be set to:
Cache-Control: no-cache, no-store
Pragma: no-cache
Because the OpenID Connect Back-Channel Logout Flow isn't executed using the user-agent (such as Browser) but from Ory OAuth2 & OpenID directly, the session cookie of the end-user won't be available to the OAuth 2.0 Client and the session has to be invalidated by some other means (for example by blacklisting the session ID).