October 20, 2026
Why API access can survive logout
Token revocation, refresh requests, and the point where a web or mobile session actually ends.
By Mehmet Sinan TATAR
6 min read
You sign out of an application. Your profile picture disappears and the sign-in screen opens. From the user's perspective, the job is done. Now imagine an authorized test in which an access token obtained before logout still retrieves account data from the API.
Here, the API is the interface the app uses to request server data, and the token is the credential accompanying that request. No token has been stolen in this example. We are following what happens to a credential from our own test account after logout.
The first reaction might be that logout is broken. Before deciding that, it helps to separate what actually ended. Was the local copy deleted? Was the server session closed? Or was only the ability to obtain new tokens removed? Those three outcomes can look identical on screen while producing different API behavior.
Returning to sign in does not notify the server
Deleting a browser's session cookie or removing a token from mobile storage clears that client's copy. It does not, by itself, stop the server from accepting a copy obtained earlier. OWASP therefore calls for server-side session invalidation during logout. Session management
If our app only clears local data, returning to the sign-in screen is expected. The API accepting the old token shows that the server still recognizes its authority. The two components may be operating without sharing the logout decision.
That distinction also matters for "sign out this device" versus "sign out everywhere." Closing the phone's session is not the same product decision as closing the laptop's session for the same account. Without an explicit target, the expected test result is unclear too.
Access tokens and refresh tokens do different jobs
An access token is presented to a protected API. A refresh token is sent to the authorization server to obtain another access token, not to the data API. Systems do not have to issue both. OAuth token roles
Suppose our application uses the pair. The access token is short-lived, and the app uses its refresh token to replace it when necessary. This is the path that keeps the session usable without asking for a password each time.
Assume the access token is a JWT, a format that can carry identity and timing claims. Its exp claim identifies the end of its acceptance period, with any small clock-skew allowance considered separately. That claim is not a logout notification. JWT expiration
Validation also involves the accepted algorithm, expected issuer, and intended recipient, not just the signature. The recipient check distinguishes a token issued for another service. Passing these checks does not establish that a later logout decision has been consulted. JWT validation practices
The twelve minutes left after logout
Consider a token issued at 10:00 with expiration set to 10:15. The user signs out at 10:03. Logout revokes the refresh token. The API validates the existing JWT's signature, expected claims, and timing, but does not check whether its session has since closed. In that design, the same access token may still work at 10:04. A replacement cannot be issued, but the existing token has not expired. These times belong to the example; fifteen minutes is not a recommended lifetime.
The OAuth revocation standard explicitly notes that revoking a refresh token does not immediately invalidate existing access tokens when the server does not support access-token revocation. RFC 7009
What changes the assessment is the application's security promise. If the feature means "cut off this device now," the remaining interval matters. If residual access for a short period is an accepted design choice, its duration and permitted operations need to be explicit. In account recovery or a lost-device flow, simply closing the screen is a particularly weak stopping point.
The API needs to receive the revocation decision
One option is for the API to ask the authorization server about the token's current state. OAuth introspection can report whether a token is active. Caching that answer can leave the API relying on an older "active" decision; RFC 7662 discusses this security and performance trade-off. Token introspection
If our API caches an active response for sixty seconds, logout may not affect the next request unless that cached answer is invalidated. Sixty seconds is another example, not a recommendation. This makes the design question more specific than whether revocation is supported: how long can it take before a revoked session is rejected?
Another approach associates the token with a server-side session record. When session S-42 closes in our example, the API can reject access by reading that record's current state. Writing "closed" into a database is not enough on its own; the services accepting requests must use it. What happens when the state service is unavailable belongs to the same design.
Firebase makes the distinction concrete. Revoking a user's refresh tokens through the Admin SDK and checking an incoming ID token for revocation are separate operations. Its Node.js example enables the latter with verifyIdToken(idToken, true). The ID token here belongs to Firebase's session model; it should not be treated as interchangeable with every OAuth access token. Firebase session management
A 200 response from a revocation request does not prove that every access path has closed either. RFC 7009 also specifies 200 for a request to revoke an invalid token. The useful check is whether the old authority still reaches the protected resource. Revocation response
When refresh and logout happen together
Now imagine the mobile app starts refreshing tokens in the background. Before the response arrives, the user signs out. A second later, the refresh response arrives and the app stores the new tokens. Despite clearing local data, the old session appears to return.
This is a sequencing problem with two separate responsibilities in our example. The client can associate the refresh operation with the session that started it. If that session has closed, the late response is not stored. This fixes the client's state; it does not automatically revoke authority already issued by the server.
On the server, refresh and closure need a consistent ordering against the same session state. In the proposed S-42 design, a closed session cannot issue new tokens. A token issued just before closure remains associated with that session rather than becoming independent authority that escapes the API's current-state check.
Refresh token rotation is a separate protection that issues a replacement on each refresh and tracks its relationship to the previous token. RFC 9700 requires refresh tokens for public clients, which cannot keep an embedded shared client secret confidential, to be cryptographically bound to a client instance or use rotation. Current OAuth security guidance
Application code running in a browser and mobile apps distributed to users' devices are examples of this client type. That classification does not mean user tokens can be stored without protection. An application's shared secret and a user's session credentials are different things. OAuth client types
Rotation alone does not resolve the logout race. In our design, the important property is that every replacement remains associated with the same session that can be closed.
When the phone is offline or a connection stays open
A user can sign out while the phone is offline. The app can clear its own data, but a revocation request that never reached the server cannot be treated as completed. The design needs to preserve that distinction: local sign-out happened; server-side access termination has not been confirmed. Keeping a token indefinitely just to retry revocation would introduce another security decision.
At the other extreme, a connection may already be open. WebSocket provides ongoing messaging between client and server. Authentication when the connection opens is not an automatic guarantee for its remaining lifetime. OWASP recommends closing the relevant connections when a session ends and checking authorization for individual actions. WebSocket session management
In our example, logout is incomplete if HTTP requests are rejected while the same session's live notification channel continues receiving protected data. Conversely, a document downloaded before logout remaining visible on the phone does not, by itself, show that the server accepted a new request. Retained data and continuing access are different findings.
What I would distinguish in a logout test
An authorized environment and a test account containing harmless data may be enough to examine this flow. Starting with two independent sessions also reveals whether signing out one device unintentionally closes the other session.
The useful distinctions are:
- Protected data is accessible before logout. What happens afterward with the old access token from that session?
- Can the old refresh token obtain fresh access? Failure here does not replace the first check.
- Can a refresh response pending during logout restore the closed session on the client?
- Do the same session's open connections continue receiving data?
- Do "this device" and "everywhere" apply their promised scope across separate sessions?
A status code alone does not describe the result. Which session was used, when did the server accept logout, how long did protected test data remain available, and did a fresh sign-in happen silently in between? Those observations separate a bounded, expected interval from a missing revocation mechanism. Raw token values do not need to appear in reports or logs.
The last place I would look in a logout flow is not the sign-in screen but the next protected request. Once we can explain what makes that request fail, the promise made to the user becomes something we can actually test.