OIDC Back-Channel Logout
Auth0 supports the OpenID Connect Back-Channel Logout 1.0 specification in all tenants with an Enterprise plan subscription.
This specification leverages the session ID (sid
) included in ID tokens and the Logout Tokens to coordinate session termination via back-channel communication. Different session IDs represent individual sessions of a user agent or device in your tenant. Logout Tokens identify the end-user and session to logout.
Back-channel communications
To use Back-Channel Logout, an application must expose a Back-Channel Logout URI, reachable from the tenant server, where the application expects to receive the requests with the Logout Token. When an application receives this request, it is compelled to clear the local session state matching the claims in the token.
Tokens in OIDC Back-Channel Logout communications
Applications cannot rely on session cookies to determine which session to terminate when communications are performed via the back-channel. Rather, the service depends on a shared session identifier (sid
) on ID and Logout Tokens.
When end-users successfully authenticate with Auth0 during login, the authorization server issues access and ID tokens. Logout tokens will be generated when a session is destroyed, such as through a logout action or session revocation. Both ID and Logout tokens contain the claims your application needs to facilitate the Back-Channel Logout workflow. To learn more about claims, read JSON Web Token Claims.
Login - During user authentication, the Auth0 tenant adds the
sid
to the ID token.Login - The application stores the received session identifier in its own session store and associates it with the application-specific session.
Logout - The IdP calls the pre-registered logout callback URL and posts the Logout Token to this endpoint. The token contains the
user_id
(sub
) and thesid
along with other parameters.Logout - The application’s backend needs to validate the Logout Token as per OIDC spec and extract the
sid
. Then the backend can use this token to find the session associated with the identifier and terminate it as necessary.
How it works
The sample use case demonstrates how Back-Channel Logout works with more than one application:
During application configuration, Application A registers a Back-Channel Logout URI with Auth0.
During application configuration, Application B registers a Back-Channel Logout URI with Auth0.
During end-user login, a user authenticates with Auth0 to access Application A.
Auth0 sends an ID token with
sid
to Application A. To learn more, read ID Token Structure.User authenticates with Auth0 to access Application B.
Auth0 sends an ID token with the same
sid
to Application B. Your application should store the session information.During logout, Application A or other entities initiate logout on the front-channel.
Auth0 terminates the Auth0 session layer via session cookie.
Auth0 calls Application A’s Back-Channel Logout URI and posts the Logout Token.
Application A validates the Logout Token and terminates the session.
Auth0 calls Application B’s Back-Channel Logout URI and posts the Logout Token.
Application B validates the Logout Token and terminates the session.
Sample token
Your application must be able to parse and validate JWTs to use as Logout Tokens with Auth0. To learn more, read Validate JSON Web Tokens.
Once your application validates and decodes your token, the contents are similar to the example below:
{
"iss": "https://artex-dev.eu.auth0.com/",
"sub": "auth0|602e93db83fa6f00749a23e6",
"aud": "TuhNLv7ulXD3RfyLlSMbOvszzwJJFPpO",
"iat": 1698160928,
"exp": 1698161048,
"jti": "44a91215-dfb4-4dfe-a1eb-fcafa911deba",
"events": {
"http://schemas.openid.net/event/backchannel-logout": {}
},
"trace_id": "81b336a94a4a5707",
"sid": "375UIp_ID5mCTClIeBEHpXfGwq51tF_L"
}
Was this helpful?
Auth0 SDKs
A full example and production code is already included under the Back-Channel Logout Example section of our express-openid-connect SDK.
Implementation examples
Session storage
The session storage example is built in Node (Express) and based on the Express OpenID Connect Web App Sample.
In your application sessions tab, expose the route you configured to receive the Logout Token. Validate the token and terminate the user session.
routes/index.js
const express = require('express');
const router = express.Router();
const { requiresAuth } = require('express-openid-connect');
// middleware to validate the logout token
const requiresValidLogoutToken = require('../middlewares/validateLogoutToken');
// helper function to delete user sessions
const deleteUserSessions = require('../utils/sessions');
// new route to receive backchannel logout tokens
// must be configured in the Application -> Sessions tab
// in the Auth0 Management Dashboard
router.post(
'/backchannel-logout',
requiresValidLogoutToken,
function (req, res, next) {
// at this point the logout token is valid, checked by requiresValidLogoutToken middleware
// you can access it from the request object: req.logoutToken
// delete user session so the user gets logged out
deleteUserSessions(
req.app.locals.sessionStore,
req.logoutToken.sub,
req.logoutToken.sid
);
res.sendStatus(200);
}
);
router.get('/', function (req, res, next) {
res.render('index', {
title: 'Auth0 Webapp sample Nodejs',
isAuthenticated: req.oidc.isAuthenticated(),
headline: process.env.APP_NAME,
backgroundColor: process.env.BACKGROUND_COLOR,
baseURL: process.env.BASE_URL,
});
});
router.get('/profile', requiresAuth(), function (req, res, next) {
res.render('profile', {
userProfile: JSON.stringify(req.oidc.user, null, 2),
title: 'Profile page',
headline: process.env.APP_NAME,
backgroundColor: process.env.BACKGROUND_COLOR,
baseURL: process.env.BASE_URL,
});
});
module.exports = router;
Was this helpful?
middlewares/validateLogoutToken.js
// This middleware validates the logout token as defined here:
// https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation
const jose = require('jose');
async function requiresValidLogoutToken(req, res, next) {
// get remote key set for token verification
const JWKS = jose.createRemoteJWKSet(
new URL(process.env.ISSUER_BASE_URL + '/.well-known/jwks.json')
);
const logoutToken = req.body.logout_token;
if (!logoutToken) {
res.status(400).send('Need logout token');
}
try {
const { payload, protectedHeader } = await jose.jwtVerify(
logoutToken,
JWKS,
{
issuer: process.env.ISSUER_BASE_URL + '/',
audience: process.env.CLIENT_ID,
typ: 'JWT',
maxTokenAge: '2 minutes',
}
);
// Verify that the Logout token contains a sub claim, a sid claim, or both
if (!payload.sub && !payload.sid) {
res
.status(400)
.send(
'Error: Logout token must contain either sub claim or sid claim, or both'
);
}
// Verify that the logout token contains an events claim
// whose value is a JSON object containing the member name http://schemas.openid.net/event/backchannel-logout
if (!payload.events['http://schemas.openid.net/event/backchannel-logout']) {
res
.status(400)
.send(
'Error: Logout token must contain events claim with correct schema'
);
}
// Verify that the Logout token does not contain a nonce claim.
if (payload.nonce) {
res
.status(400)
.send('Error: Logout token must not contain a nonce claim');
}
// attach valid logout token to request object
req.logoutToken = payload;
// token is valid, call next middleware
next();
} catch (error) {
res.status(400).send(`Error: ${error.message}`);
}
}
module.exports = requiresValidLogoutToken;
Was this helpful?
Logout token store
A common approach for token storage is to define a logout store as an alternative to the session store model. Your application(s) keeps a collection of Logout Tokens in the persistence level.
Wherever the application wants to check its authentication status, it queries the Logout Token store to find if its session is still active. The logout store flushes obsolete information regularly to keep only the necessary information.
Security considerations
Back-Channel Logout Tokens are delivered over the internet, therefore the callback endpoints receiving them must follow the best practices to ensure reliable and secure operation. The recommendations list below is not exhaustive and you must always consider the specific deployment and operational situations to adapt accordingly. In the list below, any apps that handle the Back-Channel Logout Tokens are referenced as “apps”.
Apps must have the ability to store the session ID (
sid
claim) received during user login in order to retrieve them later when receiving a Back-Channel Logout token.Apps must verify any received tokens as per JWT validation best practices.
Apps must accept tokens issued only by trusted tenants. A malicious actor can attempt to send tokens issued by other Auth0 tenants - such attempts must be rejected.
Apps must accept tokens only when they contain a
sid
value (session ID) that the app recognizes. Tokens containing an invalid session ID (being it expired or unrecognized) must be rejected.Apps must expose the callback endpoints only via TLS. Unencrypted communication channels are not allowed.
Apps are recommended to accept requests only from the published list of outbound IP addresses.
Apps are recommended to follow the general best practices in terms of monitoring, logging and rate limiting - however details of these are outside of scope of this document.
Apps are recommended to regularly clean stale or expired sessions.
Any changes in the endpoint address must be synchronised with the tenant configuration in order to ensure the Logout Tokens are always delivered to the correct Back-Channel Logout Callback URL.