JWT Authentication in FastAPI

3 min read

I still remember my first encounter with JWT tokens - I thought they were just fancy cookies with a longer name! 😅 But here's the thing: once you understand how elegant JWT authentication is, you'll never want to go back to session-based auth for APIs.

JWT (JSON Web Tokens) are like digital passports for your API. You show your credentials once, get a token, and use that token to access protected resources. The beauty? The server doesn't need to remember anything about you - the token carries all the necessary information.

First things first, let's get our requirements sorted. Update your requirements.txt with PyJWT:

fastapi==0.115.12
uvicorn==0.27.1
python-dotenv==1.1.1
psycopg2-binary==2.9.10
sqlmodel==0.0.24
alembic==1.16.4
PyJWT==2.10.1

Notice I'm using PyJWT instead of python-jose. Why? PyJWT is more lightweight, actively maintained, and does exactly what we need without the extra baggage. Sometimes simpler is better!

Since installing the requirements is a layer of Dockerfile, We will need to rebuild the containers.

docker compose up --build

Configuration Setup

Let's start with our configuration. Here's our core/config.py:

import os
from dotenv import load_dotenv

load_dotenv()

class Settings:
    TITLE: str = "GenAI API"
    DESCRIPTION: str = "Blog API Powered by Generative AI"
    VERSION: str = "1.0.0"
    DATABASE_URL: str = f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}"
    
    SECRET_KEY: str = os.getenv("SECRET_KEY")
    ALGORITHM: str = "HS256"

settings: Settings = Settings()

And your .env file should look like this:

DB_NAME=blog
DB_USER=nofoobar
DB_PASSWORD=Testing123
#don't use @ in password
DB_PORT=5432
DB_HOST=db

[email protected]
PGADMIN_DEFAULT_PASSWORD=Testing123
SECRET_KEY=Testing123

Pro tip: Generate your secret key using Python's secrets.token_urlsafe(32). Don't use "mysecretkey123" - I'm looking at you, past me! 👀

The Security Module

Now let's focus on JWT token creation. Create core/security.py:

import jwt
from datetime import datetime, timedelta, timezone
from core.config import settings

def create_access_token(subject: str, expires_delta_minutes: int = 60*24*15) -> str:
    expire = datetime.now(timezone.utc) + timedelta(minutes=expires_delta_minutes)
    to_encode = {"exp": expire, "sub": str(subject)}
    encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
    return encoded_jwt

Let me break this down for you:

  • subject: This is usually the user's email or username - something unique to identify them
  • expires_delta_minutes: Token expiry time (default: 15 days). You can adjust this based on your security needs
  • exp: Expiration timestamp in UTC (JWT standard)

Testing Our Token Creation

Let us test out our implementation.

PS C:\fastapi\blog> docker-compose exec -it web /bin/bash
root@12eaefb9291c:/app# python

>>> from core.security import create_access_token
>>> create_access_token(subject="[email protected]")
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTM0MjYzMDAsInN1YiI6InBpbmdAZmFzdGFwaXR1dG9yaWFsLmNvbSJ9.b6Rz8cXunw5Fwfas5Xx1Xy559lrj8HSbfzBcvO49ng8'

# try creating a token with increased exp and see at jwt.io
>>> create_access_token(subject="[email protected]")
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTM0MjgzNTQsInN1YiI6InBpbmdAZmFzdGFwaXR1dG9yaWFsLmNvbSJ9.iwRIGKOpIG3hJoqpRyelcUmirdXHrEQsFmrajq-6nK8'
>>> 

Notice the token is successfully generated. We can copy it and test out at https://www.jwt.io/

Common Gotchas (Learnings from My Mistakes!)

1. Secret Key Security: Never commit your secret key to Git. 😅

2. Token Expiry: Don't make tokens that never expire. That's like giving someone a permanent key to your house.

JWT authentication might seem complex at first, but it's actually quite elegant once you get the hang of it. The stateless nature makes it perfect for APIs, and the standardized format means it works everywhere.


Note: This is not a full blown implementation of JWT, its good because if someone gets access of our jwt token our account is compromized for a maximum of 15 days. Ideally we should use the concept of refresh tokens. Access tokens will be short lived like 10 minutes, and refresh token which are long lived tokens they will help us get access tokens once in a while. 

FastAPITutorial

My priority is to help build a production-ready application using FastAPI

I prioritize quality over simplicity. Our challenging approach ensures you're prepared for real-world development.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated with our latest tutorials and resources.

© Copyright 2022-2025 Team FastAPITutorial. All rights reserved.