ASP.NET Core

How to Revoke a JWT Before It Expires in ASP.NET Core

ASP.NET Core validates a JWT by its signature and expiry, so a stolen token keeps working after a password change. The article walks through two fixes: short-lived access tokens paired with revocable refresh tokens, then a token_version check for immediate revocation. Every .NET developer running JWT-based APIs should know where the gap is and what each fix costs.

Milan Jovanović [email protected] (Milan Jovanović)
Published
Reading time
9 min read
agent run #10
agent run #10
Go to original milanjovanovic.tech

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • Keep access tokens short (the author uses 5 minutes) and store refresh tokens as hashes in a table.
  • Enforce single use of refresh tokens with a UsedAt check; if a used token comes back, delete every token in that login's family.
  • Give each login a FamilyId and put it in the access token as a sid claim so single-device logout stays separate.
  • For immediate revocation, store token_version on the user and call context.Fail in OnTokenValidated when it differs.
  • The version check adds a database lookup to every authenticated request; measure it and consider a cache with the database as source of truth.

A default JWT setup in ASP.NET Core checks the signature, issuer, audience and expiry on each request, but never reads the database. That is why a password change leaves previously issued tokens valid. The author starts with the standard design: a short-lived access token plus a refresh token stored as a hash, where reusing a refresh token is treated as theft and every token from that login is revoked.

For immediate revocation, the second option copies a token_version value from the user row into the token and compares them in OnTokenValidated on every request. In the author's k6 test, that lookup added about 0.65 ms per request with one user and cut throughput sharply under 64 users, while a Redis cache reduced the overhead. Checking a session id as well lets a single device's logout take effect immediately without signing out everything.

Real-world use

  • A multi-device SaaS API that must end all sessions when a user changes their password.
  • An internal admin system that has to cut off a deactivated user's access tokens immediately.
  • A mobile and web API that needs single-device logout using refresh token families and a sid claim.

Why it matters

A default JWT setup keeps accepting a token after a password change, so you need refresh token revocation and, if required, a per-request version check to close the gap. Knowing the delay and performance cost of each option lets you choose the right balance for your auth design.

This summary was generated by the agent from the original post on Milan Jovanović. The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message