Manage Logout & Sessions

Learn how to end OAuth 2 and OpenID Connect sessions properly. This lesson covers logout flows, token revocation, and session management best practices.

Focus: handle logout and session management

Sponsored

You’ve spent weeks perfecting your login flow — redirects, token exchanges, user info claims — but what happens when the user clicks Log out? If you skip session teardown, you leave the door open for leaked access, stale permissions, and users who are "logged out" on your app but still authenticated at the provider. In this lesson, you’ll master handle logout and session management — the forgotten half of the OAuth 2 · OpenID Connect story — and learn to end sessions cleanly across your app, the authorization server, and even the user’s browser.

The problem this lesson solves

Imagine a user logs out of your application but still holds a valid access token. They can keep calling your API until that token expires — minutes, hours, or even days later. Worse, if they don’t know the logout was incomplete, they might leave a shared computer thinking they’re safe. Poor logout implementations lead to:

  • Stale sessions: The user appears logged out in your UI but their session persists on the server.
  • Token abuse: Revoked tokens that are still accepted by resource servers.
  • Fragmented identity: Logging out of your app doesn’t log them out of the provider or other relying parties.

Without a structured approach, logout becomes a security liability and a user-experience failure. This lesson gives you a mental model for treating logout as a first-class citizen — not an afterthought.

Core concept / mental model

Think of an OAuth 2 / OIDC session as a chain of three links:

  1. Application session — the local cookie or storage that keeps the user logged into your frontend.
  2. Token session — the access and refresh tokens held by your app or API.
  3. Provider session — the authorization server’s own session with the user (SSO session).

When the user logs out, you must break all three links — not just the first one.

Here’s the mental model: imagine a hotel keycard. Logging out of your app is like throwing away the keycard in your room’s trash can. The receptionist (provider) still has a record that you’re a guest, and the door lock (resource server) still accepts the old key until the battery dies. To truly check out, you need to return to the front desk, revoke the card in the system, and have the lock updated to reject it immediately.

In OIDC, this maps to: - Local logout: Clear your app session and delete local tokens. - Token revocation: Tell the authorization server to invalidate tokens immediately. - Single Logout (SLO): Notify the provider so it can log the user out of all relying parties.

How it works step by step

The standard flow for a secure logout combines these actions in sequence:

  1. User initiates logout — in your app, clicking "Log out" calls your backend endpoint (e.g., POST /logout).
  2. Clear local session — your server deletes the secure cookie or session ID; your frontend clears local storage.
  3. Revoke access token — call the provider’s revocation endpoint with the access token (or a client assertion) to invalidate it immediately.
  4. Revoke refresh token (if present) — so the provider can’t issue new access tokens later.
  5. Redirect to provider’s end_session endpoint — with an id_token_hint (the ID token you received at login) to trigger the provider-side logout.
  6. Provider clears its own session and calls back — if Single Logout is configured, the provider logs the user out of other relying parties and returns to your post_logout_redirect_uri.

Each step is cause-and-effect: failing to revoke tokens means they remain valid until natural expiry; failing to call the end_session endpoint means the provider session survives — so the user might auto-login again on next visit.

Hands-on walkthrough

Let’s see this in practice with a simple Flask app using the requests-oauthlib library (or you can use any OIDC client library). We’ll implement a logout endpoint that revokes tokens and redirects to the provider.

Step 1: Set up the app skeleton

# app.py
from flask import Flask, session, redirect, url_for
from requests_oauthlib import OAuth2Session
import os

app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY", "dev-key")

# OAuth2 client configuration (from provider)
CLIENT_ID = os.environ["CLIENT_ID"]
CLIENT_SECRET = os.environ["CLIENT_SECRET"]
AUTHORIZATION_BASE_URL = "https://provider.example.com/authorize"
TOKEN_URL = "https://provider.example.com/token"
REVOCATION_URL = "https://provider.example.com/revoke"
END_SESSION_URL = "https://provider.example.com/logout"
REDIRECT_URI = "http://localhost:5000/callback"
POST_LOGOUT_URI = "http://localhost:5000/"

def token_saver(token):
    session["oauth_token"] = token

@app.route("/login")
def login():
    oauth = OAuth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI)
    auth_url, state = oauth.authorization_url(AUTHORIZATION_BASE_URL)
    session["oauth_state"] = state
    return redirect(auth_url)

Step 2: Implement the logout endpoint

Now add the heart of this lesson — a logout endpoint that clears local session and revokes the token:

@app.route("/logout")
def logout():
    # 1. Get the token from the session
    token = session.pop("oauth_token", None)
    if token:
        # 2. Revoke the access token (and optionally refresh token)
        oauth = OAuth2Session(CLIENT_ID, token=token)
        oauth.revoke_token(token["access_token"], client_id=CLIENT_ID, client_secret=CLIENT_SECRET, token_type_hint="access_token", revoke_url=REVOCATION_URL)
        if "refresh_token" in token:
            oauth.revoke_token(token["refresh_token"], client_id=CLIENT_ID, client_secret=CLIENT_SECRET, token_type_hint="refresh_token", revoke_url=REVOCATION_URL)
    # 3. Clear local session (cookies, etc.)
    session.clear()
    # 4. Redirect to provider's end_session endpoint with id_token_hint
    if "id_token" in token:
        return redirect(f"{END_SESSION_URL}?id_token_hint={token['id_token']}&post_logout_redirect_uri={POST_LOGOUT_URI}")
    # If no id_token, just redirect home
    return redirect(url_for("home"))

Expected output: After calling /logout, the user is redirected to the provider's logout page, then back to your site's home page. The local session is empty, and the provider has invalidated the tokens.

Step 3: Test with a local provider (Keycloak example)

If you’re using Keycloak as your provider, the end_session endpoint is:

# Using curl to test token revocation
curl -X POST "https://auth.example.com/realms/myrealm/protocol/openid-connect/revoke" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "token=eyJhbGciOi..." \
  -d "token_type_hint=access_token"

After revocation, an attempt to use the old token returns 401 Unauthorized from the resource server.

Compare options / when to choose what

Different logout strategies have different trade-offs. Here’s a quick reference:

Strategy Description Best when Trade-offs
Local logout only Clear your app session; don’t revoke tokens Low-risk apps, short-lived tokens Tokens remain valid until expiry; user stays logged in at provider
Token revocation Invalidate access/refresh tokens at the provider API access must cease immediately Requires provider support; adds network call
OIDC end_session Provider-level logout with single logout (SLO) Enterprise apps with SSO Requires id_token_hint; redirect round-trip; SLO not always supported
Full SLO with backchannel Provider notifies all relying parties via backchannel High-security environments Complex; requires provider and client support for backchannel logout

Choose local logout + revocation for most web apps; add end_session for SSO ecosystems. Reserve backchannel SLO for sensitive scenarios.

Troubleshooting & edge cases

Here are common pitfalls and fixes:

  • Token revocation fails with 400: The provider may not support revocation. Check your provider’s docs; if unsupported, rely on end_session redirect and rely on token expiry.
  • id_token_hint missing: If you don’t store the ID token from login, you can’t call end_session. Always persist the ID token in the session (or at least until logout).
  • post_logout_redirect_uri not allowed: Add this URI to your client’s allowed redirect URIs in the provider’s admin panel.
  • User auto-logs-in again: This means the provider session wasn’t cleared. Ensure you redirect to end_session endpoint, not just clear your local session.
  • Session fixation: Always rotate session IDs after logout — call session.clear() and also regenerate the session ID if your framework allows.

Pro tip: Always store the ID token and the refresh token in a server-side session, not in localStorage, to avoid XSS token theft. Revoke the refresh token first — if it’s compromised, that alone is a security hole.

What you learned & what's next

You now understand the three-tier model of logout — local app session, token session, and provider session — and how to break all three cleanly. You’ve seen hands-on code for token revocation and end_session redirects, compared strategic options, and troubleshot common failure modes. This meaningfully addresses the objectives: you can explain the core idea behind handle logout and session management and complete a practical exercise for it.

Next lesson in this track will build on this foundation — likely covering token refresh and rotating credentials — where you’ll apply session management principles to keep long-lived access alive without sacrificing security. Practice the exercise below first, then continue the path.

Practice recap

Enhance the Flask app from this lesson: add a /check endpoint that calls a protected API with the stored access token. After logging out, call /check again and confirm you receive a 401. Also implement a “log out everywhere” button that revokes the refresh token before redirecting to the provider. This will solidify your understanding of token revocation and session termination.

Common mistakes

  • Only clearing the local session and never revoking tokens — access tokens remain valid until expiry, leaving a security hole.
  • Forgetting to store the ID token, so you can’t provide id_token_hint to the end_session endpoint — provider session stays alive.
  • Saving tokens in localStorage, exposed to XSS; if you must store client-side, use secure cookies and implement CSRF protections.
  • Not regenerating the session ID after logout, allowing session fixation attacks — always clear and rotate the session.
  • Assuming the provider supports SLO — many don’t; verify end_session behavior and test manually.

Variations

  1. Use backchannel logout (e.g., OpenID Connect Back-Channel Logout) for server-to-server notification when the provider ends a session.
  2. Implement Front-Channel Logout if your provider requires multiple iframes to log out multiple RPs simultaneously.
  3. For SPAs, rely on token revocation plus clearing localStorage and redirecting to end_session — but avoid storing refresh tokens in the browser.

Real-world use cases

  • Enterprise SSO: an employee logs out of your internal web app and expects all company apps using the same provider to log out (SLO).
  • Mobile API access: a user revokes access from a device — your backend immediately revokes the refresh token so it can’t mint new access tokens.
  • Shared kiosk: a customer logs out of a public terminal — your app must clear local session and revoke tokens to prevent the next user from accessing their data.

Key takeaways

  • Logout must address three sessions: application, token, and provider — otherwise the user stays authenticated elsewhere.
  • Always revoke access and refresh tokens via the provider’s revocation endpoint to invalidate them immediately.
  • Store the ID token in your session to enable the end_session redirect with id_token_hint for provider-side logout.
  • Compare strategies based on your security needs: local logout, revocation, end_session, or full SLO.
  • Rotate and clear session IDs server-side to prevent fixation; use secure storage for tokens.
  • Regularly test logout flows manually — a broken logout is a silent security hole.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.