Access and refresh tokens: rotation and reuse detection
Every refresh hands out a new refresh token; a used one coming back ends the whole sign-in.
Predict first
Before you read: predict
ExamHall signs a candidate in with a 10-minute access token and a refresh token, and her laptop refreshes every 10 minutes through the 3-hour exam. During ECON-201 a helper copies the laptop's refresh token to his phone at 09:00 and refreshes from the phone every 10 minutes too, sometimes just before the laptop, sometimes just after. Both devices reach ExamHall through the campus Wi-Fi's one public address. ExamHall wants two things: from the laptop's first refresh on, the phone gets no new access token, whichever device went first; and a candidate whose token nobody copied refreshes all exam without signing in again.
Which token-endpoint design does both?
The 30-second version
Sign-in gives two tokens. The API checks the access token by itself, so keep it short-lived. The refresh token gets new ones. Rotation swaps it on every use; a used one coming back means two holders, so the server revokes the whole sign-in. Two tabs refreshing at once look the same: refresh single-flight, or allow seconds of grace.
Every refresh hands out a new refresh token; a used one coming back ends the whole sign-in.
How it works
Step 1 of 13
In the kitchen
- today's wristband, dated and stamped; the ride operator at each gate checks only the date and the stamp → the access token, checked by the API alone (steps 1–2)
Analogy: A theme park's daily wristbands and its season-pass vouchers (amusement parks)
A season-pass holder rides on today's wristband and swaps a voucher at guest services each morning for the next.
| At the theme park | With tokens |
|---|---|
| today's wristband, dated and stamped; the ride operator at each gate checks only the date and the stamp | the access token, checked by the API alone (steps 1–2) |
| a wristband taken from a guest at noon still gets on rides until the park closes | a copied access token working until exp (step 3) |
| at guest services, the holder hands in her voucher and gets today's wristband and a new numbered voucher | a refresh, with rotation (steps 4–5) |
| guest services' book lists every voucher of one season pass in order | the family (step 6) |
| a voucher already swapped comes back: the desk cancels every voucher of that pass and asks the holder to return with photo ID | reuse revoking the family (steps 7–8) |
| two of the family at the desk's two windows at once, with one voucher | two tabs colliding (step 11) |
In real terms: the API checks the wristband; the token endpoint swaps vouchers and keeps the book.
Breaks down when: a ride operator radios guest services. An API that asks the authorization server about each token learns of a revocation at once (step 2 describes the usual JWT case).
Examples, run for real
4.1 ExamHall EXH-3760: rotation without the alarm
ExamHall's token endpoint returned a new refresh token on each refresh and refused a used one. During ECON-201 a helper copied a candidate's refresh token to his phone, which refreshed first at 09:10. MR !4060 makes a used token revoke its sign-in, and flags the attempt for proctor review:
4.1 before MR !4060 (a new refresh token each time, a used one refused): 09:10 phone -> 200 · laptop -> 400 (reuse), the candidate signed out · access tokens to the phone after that, to 12:00: 17
4.1 after MR !4060 (a used one also revokes the sign-in): 09:10 phone -> 200 · laptop -> 400 (reuse), sign-in revoked · phone at 09:20 -> 400 (revoked) · access tokens to the phone after the laptop's refresh: 0 · sign-ins flagged for proctor review: 1
Before the MR, the candidate was signed out and the phone kept her session: the endpoint refused the second presenter, whoever it was (step 9). After it, the laptop's refused refresh also revoked the phone's live token (steps 7–8). The candidate signs in again; a proctor reviews the flag.
4.2 Media streaming: two tabs, one stored token
A streaming service's web player keeps one refresh token in shared storage, and the service revokes a sign-in on reuse. Two tabs of the player need a refresh:
4.2 two tabs, one stored refresh token, both refreshing at 20:10:00: answers 200 and 400 (reuse) · sign-in revoked: yes · tabs signed in a minute later: 0 of 2
4.2 the second tab 1 s later, after the first stored its new token: answers 200 and 200 · sign-in revoked: no · tabs signed in a minute later: 2 of 2
The two tabs did nothing wrong: their second request was a reuse in the server's eyes (step 11). Refreshing one after the other kept both signed in, which is what a single-flight refresh guarantees (step 12).
Try it
Answer first, then check. Nothing is saved.
Question 1 of 6
ExamHall rotates refresh tokens and detects reuse; its token endpoint runs this handler for each refresh. claimIfUnused is one conditional write: it marks the row used only if it is still unused, and returns whether it did. reuseDetected is ExamHall's handling of a used token that comes back, and it answers invalid_grant. A candidate's two exam tabs present the same refresh token in the same instant. A tab that is refused sends its refresh once more with the token it holds.
Which tab then holds a live refresh token?
const row = await db.findByHash(sha256(token));if (!row || row.revoked) return invalidGrant();const won = await db.claimIfUnused(row.id);if (!won) return reuseDetected(row);return ok(await issuePair(row.signInId));Question 2 of 6
A streaming service's TV app and phone app each have their own sign-in. An MR changes three things in its sign-in service.
Which change should the review block?
Question 3 of 6
At 10:00 ExamHall revokes a candidate's sign-in after a used refresh token comes back. A helper's phone still holds an access token ExamHall issued at 09:56 with a 10-minute lifetime. The exam API checks each access token locally with jose's jwtVerify and ExamHall's public key. The phone calls the API at 10:04 and again at 10:08.
What does the API answer?
Question 4 of 6
In a design round you proposed rotating ExamHall's refresh tokens and revoking a sign-in's tokens when a used one comes back. The interviewer follows up: "A candidate has the exam open in two tabs that share one stored refresh token. Both refresh at the same instant, and she is signed out. Tell me what you change." You give one answer, and the interviewer's next question is about a copied refresh token presented an hour later.
Which answer survives that question?
Question 5 of 6
A streaming service rotates refresh tokens, revokes a sign-in's refresh tokens when a used one comes back, and keeps no grace window. At 20:00 a viewer's TV refreshes; the service answers, but the answer is lost on the way, so 10 seconds later the TV sends the same request again. The viewer's phone has its own sign-in.
What happens next?
Question 6 of 6
A streaming service rotates refresh tokens, revokes a sign-in's refresh tokens when a used one comes back, and keeps a 10-second grace window after a refresh token is redeemed. A viewer's web player has two tabs: tab A redeems the stored refresh token R7 at 21:00:00 and gets R8. Tab B, which read R7 a moment earlier, presents R7 at 21:00:04. At 21:00:30 a copy of R7 is presented from a device the viewer does not own.
What happens?
Go deeper
Pro tip
- When you propose rotating refresh tokens in a design round, add revoking the family on reuse in the same sentence, then raise the two-tab case before the interviewer does. When: the session or token part of any auth design. Status: SELF (this lesson's practice target; not run).
Source
Quotes are checked verbatim by quote-check.mjs. Valid as of 2026-10-13.
- (steps 5–6); "one of them will present an invalidated refresh token, which will inform the authorization server of the breach" (step 7); "The authorization server cannot determine which party submitted the invalid refresh token, but it will revoke the active refresh token." and "This stops the attack at the cost of forcing the legitimate client to obtain a fresh authorization grant." (step 8); "Authorization servers MAY revoke refresh tokens automatically in case of a security event" and "Refresh tokens SHOULD expire if the client has been inactive for some time" (step 13); "Sender-constrained refresh tokens".
- (step 1); "The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token."; "the authorization server could employ refresh token rotation in which a new refresh token is issued with every access token refresh response"; "The authorization server MUST ensure that refresh tokens cannot be generated, modified, or guessed to produce valid refresh tokens by unauthorized parties.".
The grace window and the single-flight refresh (step 12) are not in either RFC: they are executed here as designs, and the pack makes no claim that a standard names them.
Related lessons
Checked against the tools and docs as of 2026-10-13.