Skip to main content
CodeOath
← All posts

Auth & Security63 min total · 19 parts

OAuth 2.0 and JWT Explained: What Really Happens When You Click "Sign In"

Part 14 of 19 · ~2 min

Refresh Token Rotation and Revocation

Because it can go on minting fresh access tokens indefinitely and sticks around for a long time, a refresh token's theft is a genuinely serious compromise — and that's precisely what ends up sitting in a developer's OS keychain once tasklight login finishes. Steal that laptop and you don't need Priya's password, or the developer's either — you're holding a credential that just keeps working.

Refresh token rotation is Kestrel's answer to that exposure. Nothing about a refresh token gets to just sit there being reused forever — the instant one is spent to fetch a new access token, Kestrel retires it for good and issues a fresh replacement in the same breath. A refresh token, in other words, is good for exactly one redemption, ever. Here's why that single change matters so much:

Normal CLI use:
  tasklight login uses refresh_A --> gets access_token + refresh_B (refresh_A now dead)
  next refresh cycle uses refresh_B --> gets access_token + refresh_C (refresh_B now dead)

Theft detected:
  A stolen laptop still has refresh_B saved (already used, and already dead)
  The thief tries to use refresh_B --> Kestrel recognizes it as an already-consumed token
  --> treats that as reuse of a dead credential, a strong signal of theft
  --> revokes the entire token family --> the real developer has to run `tasklight login` again

Notice what actually gave the thief away: not the theft itself — a copied credential leaves no trace at the moment it's copied — but the act of trying to spend a token that had already been spent once. A stolen refresh token sitting unused in someone's possession is, cryptographically, indistinguishable from a legitimate one. It's the second attempt to use the same one-time value that rotation turns into an alarm, which is the whole reason the mechanism works even when nobody knew the laptop was gone yet.

Step back from rotation specifically and there's a broader lesson about revocation hiding in it. Say an admin suspends Priya's account this second, or someone reports the stolen laptop an hour after it actually went missing. None of that touches any access token already floating around out there — an access token doesn't check in with anyone once it's issued, it just keeps being cryptographically true until its own clock runs out, oblivious to whatever's happened to the account behind it since. Genuinely instant revocation would mean checking every single access token against a server-side list on every request, which erases the one thing stateless JWTs were bought for in the first place — no database round trip to verify one. Tasklight takes the other option instead: keep access tokens short enough that a fifteen-minute blind spot is a cost worth paying, and put the actual kill switch on the refresh token, where a database check happens rarely rather than on every request. Revoke that, and whatever access token is still out there simply runs out on its own, with nothing left alive to replace it.