ASVS 5.0 V10.4.9 (Level 2) requires that an authorized user can revoke refresh tokens and reference access tokens from the authorization server's own user interface. The goal is to limit damage from malicious clients and stolen tokens.
A reference access token is an opaque string that the resource server looks up or introspects. Self-contained JWT access tokens are different, and the requirement names reference tokens for that reason: you can revoke them instantly.
Why it matters
If a user notices an unfamiliar app in their account or loses a laptop, they need a way to cut access that does not depend on a support ticket. Without one, the only recourse is waiting for expiry.
How to implement it
Build an "Authorized applications" or "Active sessions" page, behind a fresh login or step up check, that lists each grant with the app name, scopes, creation date and last use. Each row gets a Revoke button.
@app.post("/account/grants/<grant_id>/revoke")
@login_required
@csrf_protect
def revoke_grant_view(grant_id):
grant = db.get_grant(grant_id)
if grant is None or grant.user_id != current_user.id:
abort(404)
db.execute("UPDATE refresh_tokens SET revoked_at = now() WHERE grant_id = %s", (grant.id,))
db.execute("UPDATE access_tokens SET revoked_at = now() WHERE grant_id = %s", (grant.id,))
audit.log("grant_revoked", user=current_user.id, grant=grant.id)
return redirect("/account/grants")
Check ownership on the server, as shown. Also make sure resource servers check revocation, through introspection or a short cache, so revocation takes effect within seconds.
Showing the right information
Each row should help the user decide. Show the application name, whether it is verified, what it can do in plain words, when it was authorized, when it was last used, and the approximate location or device of the last use if you collect it. A user can spot a stolen token when the last used location is a country they have never visited.
Propagation to resource servers
Revocation on the authorization server only helps if resource servers notice. For reference tokens, the resource server calls the introspection endpoint, so revocation shows up at the next call. If you cache introspection results to save load, keep the cache time to a few seconds, and document it. For self-contained tokens, keep their lifetime short and add a revocation list check for high risk operations.
Revocation endpoint for clients
Also expose the standard token revocation endpoint for clients, so an app can revoke its own tokens at sign out. It must authenticate confidential clients (see V10.4.10) and respond with 200 even when the token is unknown, so it does not reveal which tokens exist.
How to test it
Sign in with a test client, open the grants page and revoke. Then call an API with the old access token and try the refresh token. Both should fail. Log in as a second user and try to revoke the first user's grant by ID. Expect 404.
Audit and notification
Write an audit record for every revocation, including who did it and from where. Consider emailing the user when a grant is revoked or a new one is created. A message the user did not expect is an early warning of account misuse.
Common mistakes
- Revoking the refresh token but leaving access tokens alive until they expire with no cache limit.
- Missing ownership checks, which makes grant IDs a path to revoking other users' access.
- No CSRF protection on the revoke action.
Quick check
Why are reference tokens easier to revoke than JWTs?
The server holds the state, so deleting or flagging it takes effect at the next lookup. A signed JWT stays valid until it expires unless you add a deny list.