The JWT Revocation Gap, Timed (Diagram)

You click log out. In one app the credential stops working before the page finishes reloading. In the other it keeps working for another fifteen minutes, and there is nothing you can do about it from the server.

A sequence diagram comparing logout in two apps. The server-session app deletes the session row, so a stolen cookie sent one second later gets a 401. The stateless JWT app deletes nothing, so a stolen token sent one second later still returns 200 OK and keeps working until the expiry baked into the token.
One logout, two outcomes, and a window measured in minutes.Full explanationMermaid source

How long does the gap last?

Exactly as long as the exp claim inside the token, because that is the only thing that can end it. Fifteen minutes is a common access-token setting. Longer values show up regularly in generated auth code, and the gap is however long that value says.

Does a refresh token fix this?

It shortens the window, it does not close it. Revoking the refresh token stops new access tokens being issued, capping the damage at the current token's remaining life. That is a genuine improvement and the reason short access-token expiry matters, but the token already in play still works.

What actually revokes a JWT then?

A blocklist your server checks on every request. That means a lookup per request, which is server-side state, which is the thing stateless tokens were picked to avoid. You end up paying the cost of sessions and the complexity of tokens together.

So should I just use server sessions?

For a normal browser-based web app, usually yes. Sessions revoke instantly, and the lookup you were avoiding comes back the moment you add a blocklist anyway. JWTs earn their keep between services, or where the party doing the checking genuinely cannot reach your database.

::

The source

---
title: "Logging out does not log out a JWT"
---
sequenceDiagram
    autonumber
    participant U as You, clicking<br/>log out
    participant T as Someone with a<br/>stolen copy
    participant S as App using<br/>server sessions
    participant J as App using<br/>stateless JWTs

    U->>S: log out
    S-->>U: the session row is deleted
    U->>J: log out
    J-->>U: nothing to delete.<br/>your browser drops its copy

    rect rgb(254, 242, 242)
        T->>S: same cookie, one second later
        S-->>T: 401. no row left to look up
        T->>J: same token, one second later
        J-->>T: 200 OK. still signed in
    end

    Note over U,J: one logout, two outcomes. the JWT keeps working<br/>until the expiry baked into it, often fifteen minutes.

Check How Your Tokens Are Configured

CheckYourVibe reads what your deployed app actually ships, including token lifetimes long enough to make a logout meaningless.

diagrams

The JWT Revocation Gap, Timed (Diagram)