Phase 3: Authentication & Security

JWT access & refresh tokens with expiration & rotation

Intermediate ~2 min read
Think of it this way A friendly analogy. Read this if the technical version feels dense. Show Hide

Imagine your favorite library has a super special secret section with all the coolest new books and games, but only certain members are allowed in. To keep everything safe, the library is very careful about who gets in and for how long. They don't want someone to sneak in, grab a bunch of stuff, and disappear!

So, when you first arrive and show them you're a member, they give you a little Access Slip. This slip is like a golden ticket, but it's only good for a short time, maybe just 15 minutes. Every time you want to pick up a book or play a game in that special section, you show your Access Slip. Because it expires so fast, if you drop it and someone else finds it, they can only use it for a tiny window of time before it becomes useless. This keeps most things safe and limits any potential problems!

But waiting outside and getting a new Access Slip every 15 minutes would be super annoying, right? That's where your actual Library Card comes in! This card is like your super important, long-lasting ID. You keep it really safe, maybe in a special pocket. When your little Access Slip expires, you just quietly show your Library Card to the main desk, and they instantly give you a brand new Access Slip, good for another 15 minutes. You don't have to prove who you are all over again, and you only show your Library Card at the main desk, not every time you pick up a book. This makes sure you can keep enjoying the special section without constant interruptions.

Now, to make it even safer, sometimes when you use your Library Card to get a new Access Slip, the librarian doesn't just give you a new slip; they also give you a brand new Library Card and make your old one instantly useless. It's like swapping out an old key for a fresh one after you've used it once. This is called "rotation," and it's a super smart trick! If someone ever managed to copy your old Library Card, it wouldn't work anymore after you've rotated it. So, when you're building websites and apps that need to keep people's information super safe, using these Access Slips and rotating Library Cards means you can make sure only the right people get in, and even if there's a problem, any damage is tiny and temporary.

To build robust and secure backend authentication systems using JWTs, developers often employ a dual-token strategy: an access token and a refresh token. The access token is your primary key to accessing protected resources. It's short-lived (e.g., 15 minutes) and is sent with every API request after the initial login. Its short lifespan is a crucial security measure; if an access token is intercepted, an attacker has a very limited window to misuse it, thereby minimizing potential damage.

Since re-authenticating the user every 15 minutes would be a terrible user experience, we introduce the refresh token. This token is long-lived (e.g., 7 days, 30 days) and is stored securely on the client side, typically in an HTTP-only cookie or secure storage. Its sole purpose is to obtain a new access token when the current one expires, without requiring the user to log in again. The refresh token is only sent to a specific refresh endpoint, not with every regular API request, further reducing its exposure.

Enhancing security even further is the concept of refresh token rotation. When a client uses a refresh token to request a new access token, the server not only issues a new access token but also a brand new refresh token, simultaneously invalidating the old one. This makes refresh tokens effectively "one-time use." If an attacker intercepts a refresh token, using it will render the legitimate client's token invalid, immediately alerting the system to potential compromise and making the stolen token useless for subsequent renewals. This proactive invalidation significantly mitigates the risk associated with long-lived refresh tokens.

Key Takeaways

  • Access tokens are short-lived and grant access to resources.
  • Refresh tokens are long-lived and grant new access tokens.
  • Expiration limits the window of opportunity for stolen tokens.
  • Refresh token rotation invalidates old tokens upon use, enhancing security.
  • Always store refresh tokens securely (e.g., HTTP-only cookies).

Code Example

python
import jwt
import datetime

# In production, use a strong, environment-variable key!
SECRET = "your_super_secret_key_here"

def create_access_token(user_id: str) -> str:
    payload = {
        "user_id": user_id,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15) # Short-lived
    }
    return jwt.encode(payload, SECRET, algorithm="HS256")

def create_refresh_token(user_id: str) -> str:
    payload = {
        "user_id": user_id,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(days=7) # Long-lived
        # For rotation, you'd add a unique ID (JTI) here and store it server-side
    }
    return jwt.encode(payload, SECRET, algorithm="HS256")

How this code works

This code provides the core logic for generating JWT (JSON Web Token) access and refresh tokens, a common pattern for authentication in backend applications. It utilizes the jwt library for token operations and datetime for managing token lifetimes. A SECRET key is defined, which is vital for securely signing and verifying tokens; its importance is highlighted for production environments. The primary goal is to produce two types of tokens with different validities to balance security and user experience.

The create_access_token function constructs a payload dictionary with the user_id and an exp (expiration) timestamp. This token is designed to be short-lived, expiring after just 15 minutes, using datetime.timedelta. A subtle but important detail for beginners is the use of datetime.utcnow(): exp claims should always be in UTC to avoid timezone issues. The create_refresh_token function follows a similar structure but sets a much longer exp (7 days), allowing users to stay logged in without frequent credential re-entry. Both functions use jwt.encode with the SECRET and HS256 algorithm to convert the payload into a secure, signed token string.