We already know from Part 1 what ID, Access and Refresh tokens are and how they differ.
But how does it actually work in practice?
How is it possible that a user can refresh the page, open a new browser tab, or switch back after a minute — and still remain logged in?
And what exactly is PKCE?
👉 Part 1 (recap) here:
https://dev.to/sylwia-lask/auth-explained-part-1-id-vs-access-vs-refresh-tokens-what-they-actually-do-and-why-2l1
TL;DR for goldfish (read this first)
Access Token = short-lived ticket (kept only in memory)
Refresh Token = long-lived “key” (kept safely in HttpOnly cookie)
When you reload or open a new tab → app loses the ticket…
but the cookie quietly gives it a new one
That’s why you stay logged in ✔️
The Redirect Dance 💃
When your frontend loads for the first time, it doesn’t know who the user is.
It must “go ask” a trusted Identity Provider.
Frontend → IdP → user logs in → redirect back → tokens obtained ✅
| Step | What happens | Why |
|---|---|---|
| 1 | Frontend redirects to IdP with a code_challenge
|
prepares PKCE |
| 2 | User authenticates | AuthN |
| 3 | IdP redirects back with an authorization_code
|
temporary proof |
| 4 | Frontend exchanges it + verifier | receives tokens |
After that:
| Token | Where | Purpose |
|---|---|---|
| Access | in memory | API calls |
| ID | in memory | UI only |
| Refresh | HttpOnly cookie | session continuity |
PKCE — why it exists 🔐
The authorization code is visible in the URL → can be stolen.
PKCE makes that theft useless.
| PKCE piece | Role | Quick analogy |
|---|---|---|
| code_challenge | public | the lock 🔒 |
| code_verifier | secret | the key 🔑 |
IdP: “You have the code? Cool. But do you have the verifier that matches it?”
A deeper analogy: PKCE = SIM card + PIN 📱
| PKCE piece | SIM analogy |
|---|---|
| code_verifier | the PIN (secret) |
| code_challenge | stored PIN hash |
| authorization_code | the SIM card |
Just like in real life:
- Steal the SIM → you still can’t use it
- You prove ownership only by entering the correct PIN
- The SIM alone isn’t enough — PIN binds it to the real owner
PKCE = “prove this code really belongs to you”.
Where the Refresh Token actually comes from 🍪
The frontend never manually stores it.
The IdP sets it as a secure HttpOnly cookie during the token exchange:
Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Lax; Path=/token/refresh;
JS can’t read this cookie.
XSS can’t steal it.
It’s browser-managed, not app-managed.
What happens when the access token expires? ⏳
- API returns
401 Unauthorized - Frontend calls
/token/refreshwithcredentials: "include" - Browser silently attaches the cookie
- New Access Token is returned
- User stays logged in → smooth UX
This is what feels like “automatic session”.
✅ What happens if the user refreshes the page / opens a new tab / logs out somewhere else?
This was one of the top questions from Part 1 — so here’s the mental model:
| Scenario | Result | Why |
|---|---|---|
| Page refresh | still logged in ✅ | browser still has refresh cookie |
| New browser tab | still logged in ✅ | cookies are shared across tabs |
| Hard reload / loss of JS | still logged in ✅ | refresh recreates access token |
| Access token in memory lost | expected ✅ | memory-only on purpose |
| Logout in another tab | logs out everywhere ✅ | Refresh cookie/session invalidated |
| Cookie deleted | logged out ❌ | no more way to mint new tokens |
Refresh cookie = “long-lived proof-of-session”
Access token = “short-lived working ticket”
Reload ≠ logout.
Refresh Token Rotation ♻️
Every refresh → new RT replaces the previous one.
If someone ever stole a refresh token:
- the very next refresh exposes the mismatch
- IdP detects it
- session can be revoked
It’s a “break-on-use” wristband 🎟️ — one use and done.
A tiny peek at BFF (Backend for Frontend) 🛡️
All of this (PKCE + RT cookie) is still happening in the browser.
BFF goes one level safer:
- tokens live only on your backend
- browser never touches them
- no refresh cookie at all
- every request = proxied through your backend
This is becoming the modern default for high-security apps.
What’s next — Part 3 🚀
✅ JWT vs BFF — which model is actually safer in the real world?
Short and to the point:
- what BFF really changes
- why some teams are migrating away from browser-JWT
- when BFF is worth it (and when it’s overkill)
What about you — what was the first thing about auth that finally clicked for you (or totally confused you)? 😄
💡 Liked this one? I’m writing more about growth, mindset and the not-so-obvious parts of software on my Substack → sylwialask.substack.com
Top comments (0)