KAHIBARO
Discord Login Register

13.13. Logout

Why Logout Matters

Logout looks very simple to the user, but it is a critical security feature.

A user clicks “Logout,” and you must make sure that:

If logout is weak or incomplete, attackers can reuse old cookies or tokens and access accounts.

Rule: Logout is not “just hide the UI.”
You must invalidate authentication credentials (session, token, etc.) on the server side.

In this chapter you will see how logout works with sessions, tokens, and common backend patterns.


Logout in Session-Based Authentication

In session-based systems, the browser stores a session cookie like:

http
Cookie: session_id=abc123

The server stores session data for abc123 in memory, Redis, or a database.

To log out, you must do two things:

  1. Remove or invalidate the server-side session.
  2. Remove the cookie in the browser.

Server-Side Session Invalidation

For a typical web framework, logout might:

python
def logout(request):
    session_id = request.cookies.get("session_id")
    if session_id:
        delete_session_from_store(session_id)  # remove from DB / Redis
    response = redirect_to_login()
    response.delete_cookie("session_id")      # tell browser to drop cookie
    return response

The key part is delete_session_from_store. After this, even if the cookie is stolen or not deleted yet, the server will not find a valid session.

Common mistakes:

If your session store is Redis, logout might be as simple as:

python
redis_client.delete(f"session:{session_id}")

or in SQL:

sql
DELETE FROM sessions WHERE id = 'abc123';

Rule: With sessions, logout must destroy or invalidate the server-side session record, not just rely on the browser.

Clearing the Session Cookie

The backend tells the browser to remove the cookie by setting it again with an expiration in the past:

http
Set-Cookie: session_id=deleted; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Secure

Frameworks usually provide helpers like:

python
response.delete_cookie("session_id")

Important details:

Logout Flow Example (Sessions)

  1. User clicks “Logout” link that sends POST /logout with cookie session_id=abc123.
  2. Backend handler:
    • Reads session id from cookie.
    • Deletes session from store.
    • Returns response that clears cookie and redirects to /login.
  3. Any future request with session_id=abc123 will fail, since the session is gone.

Logout in Token-Based Authentication

In token-based systems (for example JWTs) the browser or client stores an access token, sometimes a refresh token too.

A typical Authorization header:

http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Basic idea

So how do you “logout” if the server does not store tokens?

You have two jobs:

  1. On the client, remove the token.
  2. On the server, optionally prevent reuse of some tokens (especially refresh tokens).

Client-Side Token Removal

For a single-page application:

js
function logout() {
  localStorage.removeItem('access_token');
  localStorage.removeItem('refresh_token');
  window.location.href = '/login';
}

Or, if you used HttpOnly cookies for tokens, the backend must send Set-Cookie headers that clear those cookies, just like with session cookies.

Example response on logout:

http
Set-Cookie: access_token=; HttpOnly; Secure; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: refresh_token=; HttpOnly; Secure; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT

Server-Side Token Revocation

If tokens are short-lived (for example 5 minutes) and only access tokens exist, many systems do not store them. Logout is basically:

However, when you use refresh tokens, you typically store them in a database. Logout must revoke or delete them.

Example data:

user_idrefresh_token_idtoken_valueexpires_at
519f21...2026-12-01 12:00:00

On logout:

python
def logout(current_user, refresh_token_id):
    db.execute(
        "DELETE FROM refresh_tokens WHERE id = %s AND user_id = %s",
        (refresh_token_id, current_user.id)
    )
    response = Response(status_code=204)
    response.delete_cookie("refresh_token")
    response.delete_cookie("access_token")
    return response

Now that refresh token can never be used to get a new access token.

Rule: In token-based systems with refresh tokens, logout must revoke or delete the refresh token on the server.


Logout Endpoints and HTTP Methods

A logout is not a simple “view” of data, it changes authentication state. Because of that, you should not use GET /logout with no protection.

A safer design:

Example REST design:

ActionMethodPathDescription
LoginPOST/auth/loginCreate session / token
LogoutPOST/auth/logoutDestroy current session
Logout allPOST/auth/logout-allInvalidate all sessions

Example FastAPI style logout handler (session-based):

python
from fastapi import APIRouter, Response, Depends
router = APIRouter()
@router.post("/logout")
def logout(response: Response, current_user=Depends(get_current_user)):
    # remove user sessions from store, example:
    delete_sessions_for_user(current_user.id)
    response.delete_cookie("session_id")
    return {"detail": "Logged out"}

You typically want CSRF protection for logout if it is cookie-based, since a POST request can be triggered from another site.


Logout from All Devices

Sometimes the user clicks “Logout from all devices” or “Log out everywhere.” This is especially important for security after password change.

The implementations differ for sessions vs tokens.

Sessions: Invalidate All User Sessions

Your session store may link sessions to users:

session_iduser_idcreated_at
abc123422026-01-01 10:00:00
def456422026-01-02 13:00:00

To logout from all devices:

sql
DELETE FROM sessions WHERE user_id = 42;

Every device that tries to use an old cookie will fail.

Tokens: Invalidate All Refresh Tokens

If you store refresh tokens:

iduser_idtoken_valueexpires_at
142aaaa...2026-02-01 00:00:00
242bbbb...2026-02-15 00:00:00

Logout all devices:

sql
DELETE FROM refresh_tokens WHERE user_id = 42;

Optionally, for access tokens, many systems rely on short lifetimes and do not store them. After logout-all:

Handling Logout in Single-Page Applications (SPAs)

In SPAs, the frontend often controls navigation and state. Backend still controls security.

A typical SPA logout flow:

  1. User clicks “Logout” button.
  2. Frontend sends POST /auth/logout to backend with the current credentials.
  3. Backend:
    • Deletes session or revokes refresh token.
    • Clears cookies if used.
    • Returns 204.
  4. Frontend:
    • Clears any local storage tokens.
    • Clears any user state in memory (like React context or Vuex).
    • Redirects to /login or a public page.

Example (React-ish pseudocode):

js
async function handleLogout() {
  try {
    await api.post('/auth/logout'); // backend invalidates
  } catch (e) {
    // ignore errors, still clear local state
  }
  localStorage.removeItem('access_token');
  localStorage.removeItem('refresh_token');
  setUser(null);
  navigate('/login');
}

Rule: Frontend must clear its own copy of authentication data, but real logout security depends on what the backend does.


Security Considerations and Common Pitfalls

Logout is part of your security design. Done poorly, it gives a false feeling of safety.

Using GET /logout Without Protection

If logout is triggered by a simple GET /logout, any site can embed an image:

html
<img src="https://your-app.com/logout" />

This would log users out without their intention. While this is not as bad as logging them in or changing data, it can be annoying and may break flows.

A better design:

Not Invalidating Server-Side State

Common mistake:

If sessions or refresh tokens remain valid, an attacker who already has a copy can keep using them.

Always:

Not Handling Multiple Devices

Users may log in from:

Profile actions like “Change password” or “Report stolen phone” should often:

This means your data model must support finding all active sessions or tokens for a user.

Long-Lived Tokens Without Revocation

If access tokens live for many days and you do not have any revocation list, logout becomes weak. An attacker who steals the token can keep using it until it expires.

Safer approach:

Implementing Token Blacklists

If you must be able to immediately revoke access tokens before they expire, you can keep a blacklist or a “version” strategy.

Blacklist example

This adds overhead, but allows instant logout.

Version (token version) example

Any old tokens with the previous version are automatically invalid.

Example table:

user_idtoken_version
1005

On logout-all:

sql
UPDATE users SET token_version = token_version + 1 WHERE id = 100;

Token payload:

json
{
  "sub": 100,
  "token_version": 5,
  "exp": 1735707600
}

If token_version in DB becomes 6, all old tokens with token_version: 5 are invalid.


User Experience Around Logout

While security is the main goal, UX also matters.

Good practices:

Example response:

json
{
  "detail": "Logged out successfully"
}

Even if the session did not exist, returning success is usually fine. Do not leak information about whether the user was logged in or not.


Summary

You have seen how logout works for different authentication styles:

With these patterns, you can implement logout that is both user friendly and secure.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!