“What if refresh token is stolen?”
Assume worst case:
- Attacker somehow gets the refresh token
- They try to use it to mint new access tokens
OAuth is designed assuming this can happen.
1️⃣ Refresh Token Rotation (MOST IMPORTANT)
What rotation means
Refresh token is single-use
Each time it is used:
- Old refresh token → invalid
- New refresh token → issued
Visual flow
Login →
RefreshToken = RT1
RT1 used →
Auth Server returns:
AccessToken = AT2
RefreshToken = RT2
RT1 is now DEAD ❌
Attacker scenario
Case A: Attacker uses stolen RT first
Attacker → uses RT1 → gets RT2
User later uses RT1 → ❌ INVALID
Auth server detects:
“This refresh token was already used → TOKEN THEFT”
Then:
- Revokes entire token family
- Forces re-login
Case B: User uses RT first
User → RT1 → RT2
Attacker → tries RT1 → ❌ INVALID
Attacker is blocked immediately.
Result
✔ Only one party wins ✔ Theft is detected ✔ Damage is minimal
This is why rotation is powerful.
2️⃣ Revocation (Kill switch 🔴)
What revocation means
Auth server can explicitly invalidate tokens
Triggers:
- User logs out
- Password change
- Suspicious activity
- Admin action
Flow
Auth Server:
revoke(refresh_token_id)
After revocation:
- Access tokens → rejected
- Refresh tokens → rejected
Attacker holding token: ❌ Completely useless
Real example
- You click “Logout from all devices”
- All refresh tokens die instantly
3️⃣ Audience Binding (Token is “for someone”)
What it means
Token is issued for a specific client or API
Token contains:
aud = instagram-backend
If attacker tries:
Use token with another API
Result: ❌ Token rejected
Why this matters
Even if attacker steals token:
-
They can’t use it in:
- Other apps
- Other APIs
- Other environments
Token is context-locked.
4️⃣ PKCE (Protects token exchange)
PKCE is often misunderstood.
PKCE protects:
- Authorization code theft
- Refresh token issuance
Without PKCE (bad)
Attacker steals auth code
→ exchanges it for tokens
With PKCE (good)
Client proves:
“I am the same app that started the login”
Using:
- code_verifier
- code_challenge
If attacker tries:
Auth Server:
❌ Missing code_verifier
No tokens issued → no refresh token → no access token.
🧠 Very important clarification
PKCE:
- ❌ Does NOT protect stolen refresh token directly
- ✅ Prevents attacker from GETTING refresh token in first place
5️⃣ Putting it all together (THIS IS THE KEY)
What OAuth assumes
Tokens can be stolen
What OAuth guarantees
Stolen tokens cause minimal, detectable damage
Combined defense layers
| Protection | Stops |
|---|---|
| Rotation | Reuse of stolen refresh token |
| Revocation | Kill stolen tokens |
| Audience binding | Cross-app misuse |
| PKCE | Token issuance theft |
🧩 Final attacker story (complete)
Attacker steals refresh token
↓
Tries to use it
↓
Rotation detects reuse
↓
Auth server revokes token family
↓
User is forced to re-login
↓
Attack stops