Imagine you’re creating a web app that everyone loves. It’s growing fast—people are signing up left and right. But soon, trouble sneaks in. A sneaky hacker grabs a refresh token and gets inside. Users grumble because logging in on their phone kicks them off their computer. And your database? It’s huffing and puffing, trying to keep up with millions of users.

You realize your basic JWT login system is like a shaky lock on the door. You need something tougher, safer, and ready to handle the crowd. Here’s the story of how you fixed it.


The Plan: A Smarter Way to Log In

You come up with a new idea to keep things safe and smooth:

  • Access Tokens: Quick little keys that last 15 minutes for using the app.
  • Refresh Tokens: Longer-lasting keys, good for 7 days, to get new access tokens.
  • Session Tracking: A way to watch each device separately.
  • Hashed Refresh Tokens: A trick to make stolen tokens useless.

Let’s see how this plays out.


Step 1: How It All Works

It starts with Alice, who wants to use your app. She logs in, and the server hands her three things: an accessToken, a refreshToken, and a sessionId—like a special ticket for her phone. The system writes her info down in a safe spot, like a secret notebook.

After 15 minutes, her access token runs out. No problem! Her phone shows the refresh token, and the server checks the notebook. If it matches, she gets a new access token and a fresh refresh token. The old one gets tossed out.

But what if a hacker tries to use a stolen refresh token? The server spots the fake—it doesn’t match the notebook. Boom! The session gets shut down, and the hacker’s out of luck.


Step 2: Building It (Simple Code)

You make this happen with some handy tools. Here’s how it looks in easy words:

The Notebook for Sessions

You set up a place to store info:

Session { id (UUID) userId (INDEX) refreshTokenHash deviceInfo (optional) lastActive (TIMESTAMP) } // compound index [userId, id]

Starting the Login

When Alice logs in, you make her tokens:

function login(user): accessToken = makeJWT({ userId, role, permissions }, lasts=15min) refreshToken = makeJWT({ userId }, lasts=7d) sessionId = makeUniqueCode() saveSession(sessionId, userId, hash(refreshToken)) return { accessToken, refreshToken, sessionId }

Getting New Tokens

When her token runs out, here’s what happens:

function refreshTokens(userId, sessionId, refreshToken): session = findSession(userId, sessionId) if no session → say "Nope, not allowed" if hash(refreshToken) doesn’t match session.refreshTokenHash: deleteSession(userId, sessionId) say "Nope, not allowed" newAccessToken = makeJWT({ userId, role, permissions }, lasts=15min) newRefreshToken = makeJWT({ userId }, lasts=7d) updateSession(sessionId, scramble(newRefreshToken)) return { newAccessToken, newRefreshToken, sessionId }

Step 3: Keeping Trouble Out

Your system’s got some clever tricks to stay safe:

  • Hashed Refresh Tokens: Stolen tokens don’t work.
  • New Tokens Each Time: Old ones can’t sneak back in.
  • Spotting Fakes: If something’s fishy, the session’s gone.
  • Short Access Tokens: Leaks don’t last long.
  • Works on All Devices: Each gadget gets its own login.

Summary of the JWT Authentication Flow

This login system is built to be fast and secure, even if a hacker steals a refresh token. Here’s how it works:

  1. User Login: Alice logs in with credentials. The server generates an accessToken (expires in 15 minutes), a refreshToken (expires in 7 days), and a sessionId. The session info is stored in the database with a refreshTokenHash.
  2. Using Access Token: Alice uses the accessToken to call APIs. After 15 minutes, it expires.
  3. Token Refresh: Alice’s device sends the refreshToken and sessionId to the server to get a newAccessToken. The server checks the refreshToken against the refreshTokenHash in the database:
    • If it matches, it issues a newAccessToken and a newRefreshToken, updating the refreshTokenHash (token rotation).
    • If it doesn’t match (fake/old token), it deletes the session and throws "Unauthorized".
  4. Hacker Attack: If a hacker tries a stolen refresh token, the server detects the mismatch with refreshTokenHash, deletes the session, and blocks the threat.
  5. Outcome: The system supports multi-device logins (each device has its own session), reduces database load, and stays secure with short-lived access tokens.

This flow keeps the app fast, light, and hack-proof!

A System That Rocks

Now, your app’s login system is solid. A stolen token? No big deal—it’s useless. Users switching devices? They’re happy as can be. Millions of users? Your database keeps humming along.

You’ve built something awesome safe, simple, and ready for anything. What do you think? Got any cool ideas to add? Let’s talk!