Email Stops Being a Second Factor

Part of the Refresh Token Design series. The Fall-Through The verify endpoint took a six digit code and worked out what it was. If the account had an active authenticator and no emailed code was currently live, check the authenticator. If an emailed code was live, check that instead. It reads like a helpful convenience and it is the ceiling on the whole account. Anything holding a session at the lowest tier can make an emailed code live simply by asking for one. So an account with an authenticator enrolled was still, at any moment of the attacker’s choosing, an account that could be entered with a code from the inbox. ...

August 16, 2026 · 6 min · 1244 words · Sagar Nayak

The Day Two Security Features Cancelled Out

Part of the Refresh Token Design series. Two Correct Features Branching says a refresh that cannot prove its device gets a new row of its own, copying the device key from the row it came from, opening at the lowest tier. The honest case is a browser whose storage was cleared, and its road back is to prove a second factor, which rebinds the row to a fresh key. Per request signing says every request carrying a session must be signed by the device key on that session’s row. ...

August 9, 2026 · 6 min · 1261 words · Sagar Nayak

Signing Every Request, Not Just the Refresh

Part of the Refresh Token Design series. The Gap Device Binding Left Binding the refresh token to a device means a copied refresh token is worth nothing on its own. Good. It also means the refresh token is now the better protected of the two credentials, and the access token, which is sent on every single request, is still a bearer secret. Anybody who captures one has five minutes of full access and needs nothing else. ...

August 2, 2026 · 7 min · 1395 words · Sagar Nayak

Branch the Session Instead of Narrowing It

Part of the Refresh Token Design series. The Question the Last Post Left Open A refresh arrives carrying a device proof that does not verify. Two things can produce it, and the request looks identical either way. A browser that lost its key and generated a new one, which is ordinary and frequent. Somebody holding a copy of the refresh token, who cannot sign because the key never left the machine it was made on. ...

July 26, 2026 · 6 min · 1198 words · Sagar Nayak

Binding a Refresh Token to the Device

Part of the Refresh Token Design series. The Actual Defect The previous post ended on a reframing worth repeating. Reuse detection was never really about token ordering. It was trying to answer “is this a different person” by looking at “has this token been seen”, which is a bad proxy, and the false logouts are the evidence. The real defect is that a refresh token is a bearer secret. Whoever holds the bytes is the session. Copy them out of storage, a log, a crash report, a proxy, and you are indistinguishable from the person they belong to. ...

July 19, 2026 · 6 min · 1247 words · Sagar Nayak