Logout

Background

Previously we discussed the initial Token Renewal solution for our SPA.  Next we will provide a brief overview of OpenID Connect Logout options.

Logout User Behaviour

Most commonly, users simply close the app to finish interacting with an app, rather than explicitly signing out. A big reason why many companies want to support logout functionality in an OAuth app is for test purposes. It can be useful to sign in to your UI as multiple test accounts with different settings or access rights:

OpenID Connect Logout Standards

OpenID Connect logout supports multiple standards related to single logout. However, some of these rely on techniques such as sending authorization server cookies from iframes, and may no longer work.

This blog will use only RP Initiated Logout, which works reliably, and this behaviour will be implemented for all apps. For single page applications, multi-tab logout will also be implemented.

RP initiated logout runs a top level redirect to end the user’s authenticated session. This expires cookies in the authorization server. If an external identity provider is used, cookies are also expired in that system.

RP Initiated Logout Request Messages

This involves an HTTP redirect to the End Session Endpoint, and the request message commonly uses the following query parameters:

Field Description
id_token_hint The ID token received during login or one that was updated via token renewal
post_logout_redirect_uri An unsecured location to return to in the app once logout completes
state An optional field to allow the response to be correlated to the request

Some providers allow a client_id field to be provided instead of the id_token_hint parameter. There are a couple of security trade offs here:

Field Description
id_token_hint This allows the Authorization Server to trust the caller, since a JWT is being provided, but there is a risk that the request could reveal personal data
client_id When a client ID is sent, there is no proof of who the caller is and only a subset of RP Initiated features may be allowed

Allowed Post Logout Redirect URIs

Some providers do not allow post logout redirect URIs to include a hash location so you may instead need to redirect to a small logout HTML page, then manage this redirect within the app. The next SPA code example will follow this approach:

<!DOCTYPE html>
<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <base href='/spa/' />
        <title>OAuth Demo App</title>    
    </head>
    <body>
        <script src='dist/loggedout.js'></script>
    </body>
</html>

Post Logout Location

When logout has completed, our code samples will return the user to a Post Logout Landing Page within the app, then present the user with an option to sign in again:

Multi Tab Logout in Web UIs

It uses to be possible to implement logout for SPAs using OpenID Connect Session Management. In this flow, an iframe on each tab polled the authorization server, sending it the SSO session cookie, to check whether there had been an external logout, such as on another browser tab.

This no longer works reliably however, since some browsers, such as Safari with default settings, may treat the SSO session cookie as being third party, and therefore drop it during this flow.

Yet similar behaviour for a web app can be achieved by storing a boolean setting in local storage and using the Browser Storage API to watch for changes across tabs. This blog’s code samples will use this approach.

Benign Logout Errors

Some authorization servers may return logout error responses if you attempt to logout when the session is already expired. This blog’s samples will ignore logout errors. by just removing tokens then navigating to the post logout location.

Controlling Logout

An SSO cookie in a user agent is a pointer to authorization server state, and can represent sessions for multiple clients and users. When a logout is triggered for one application, it may impact other apps, and you may not always be able to achieve your desired usability.

There may be other logout annoyances on particular platforms. An example is on iOS, where an ASWebAuthenticationSession window is used for RP Initiated Logout. This always indicates a Sign In operation, and it is not possible to update the text to indicate that a Sign Out is taking place:

When you run into this type of problem, a better choice can be for the app to avoid using RP Initiated Logout. Instead, the app can simply remove its tokens and move the user to a logged out page.

By default though, the next login will then perform an automatic login with no user prompts. To prevent this you can control behaviour using OpenID Connect request parameters. In particular the ‘prompt=login‘ parameter can force a new login when the app no longer has any tokens.

Access Token Validity After Logout

When a user signs out, an OAuth secured app that uses refresh tokens can optionally make a revocation call to the Authorization Server, to ensure that the refresh token can no longer be used.

Sometimes developers also expect all access tokens to be immediately rejected by APIs, to enable the same behaviour as in older cookie based web apps. For this to work, APIs would need to call the authorization server on every API request, leading to performance problems.

Instead, access tokens are bearer tokens and remain usable until they expire. The simplest option is to implement the following behaviour, which is the approach followed by all of this blog’s apps:

  • Client applications discard all tokens upon logout
  • Access tokens are short lived so that they expire soon after logout 

Where Are We?

We have explained how this blog’s logout behaviour will work, which will involve only simple and reliable code. RP Initiated and multi-tab logout will be implemented in the next SPA code sample, in a few posts’ time.

Next Steps