Secure FastAPI APIs with JWT: Step‑by‑Step Guide & Best Practices

This tutorial explains the fundamentals of JSON Web Tokens, their structure, how to encode them with base64Url, and provides a complete FastAPI implementation—including installation, token generation, verification, protected routes, and practical security recommendations—for building robust authentication in distributed systems.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Secure FastAPI APIs with JWT: Step‑by‑Step Guide & Best Practices

JWT Basics

JSON Web Token (JWT) is an open standard (RFC 7519) that provides a compact and secure way to transmit claims between parties, making it ideal for single‑sign‑on (SSO) in distributed sites.

What is JWT?

JWT carries claims in a JSON format. It is compact, can be signed or encrypted, and is typically used to convey authenticated user information from an identity provider to a resource server.

JWT Structure

Header : declares the token type and signing algorithm.

Payload : a JSON object containing the data to be transmitted.

Signature : used to verify the token’s integrity.

base64UrlEncode Method

When generating a JWT, the signature is encoded using a base64UrlEncode function. The algorithm works as follows:

Take a UTF‑8 encoded string s1.

Base64‑encode s1 to obtain s2.

If s2 ends with '=', remove all trailing '=' characters.

Replace any '+' in s2 with '-'.

Replace any '/' in s2 with '_'.

import hmac
import base64
from hashlib import sha256
from urllib import parse as urlp

def b64url(str1):
    if type(str1) == str:
        return str(base64.b64encode(str1.encode('utf-8')), encoding="utf-8").strip('=')\
            .replace('+', '-').replace('/', '_')
    elif type(str1) == bytes:
        return str(base64.b64encode(str1), encoding="utf-8").strip('=')\
            .replace('+', '-').replace('/', '_')
    else:
        raise TypeError("The type of given argument must be string or bytes")

Generate HS256 Encrypted JWT

header = b64url('{"alg":"HS256","typ":"JWT"}')
payload = b64url('{"sub":"1234567890","name":"John Doe","iat":1516239022}')
secret = 'happynewyear'.encode('utf-8')

sig = b64url(hmac.new(secret, (header + '.' + payload).encode('utf-8'), digestmod=sha256).digest())
jwt = header + '.' + payload + '.' + sig
print(jwt)

Integrating JWT in FastAPI

Install Dependencies

Install FastAPI and Uvicorn:

pip install fastapi uvicorn

Implementation Steps

1. Create FastAPI Application

from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import hmac, base64
from hashlib import sha256

app = FastAPI()

# b64url implementation (same as above)

SECRET_KEY = 'happynewyear'.encode('utf-8')

def generate_jwt():
    header = b64url('{"alg":"HS256","typ":"JWT"}')
    payload = b64url('{"sub":"1234567890","name":"John Doe","iat":1516239022}')
    sig = b64url(hmac.new(SECRET_KEY, (header + '.' + payload).encode('utf-8'), digestmod=sha256).digest())
    return header + '.' + payload + '.' + sig

def verify_jwt(token: str):
    parts = token.split('.')
    if len(parts) != 3:
        return False
    header, payload, received_sig = parts
    new_sig = b64url(hmac.new(SECRET_KEY, (header + '.' + payload).encode('utf-8'), digestmod=sha256).digest())
    return new_sig == received_sig

class JWTBearer(HTTPBearer):
    def __init__(self, auto_error: bool = True):
        super(JWTBearer, self).__init__(auto_error=auto_error)

    async def __call__(self, request):
        credentials: HTTPAuthorizationCredentials = await super(JWTBearer, self).__call__(request)
        if credentials:
            if credentials.scheme != "Bearer":
                raise HTTPException(status_code=401, detail="Invalid authentication scheme.")
            if not verify_jwt(credentials.credentials):
                raise HTTPException(status_code=401, detail="Invalid or expired token.")
            return credentials.credentials
        else:
            raise HTTPException(status_code=401, detail="Invalid authorization code.")

2. Create Login Endpoint

@app.post("/login")
def login():
    token = generate_jwt()
    return {"access_token": token, "token_type": "bearer"}

3. Create Protected Endpoint

@app.get("/protected", dependencies=[Depends(JWTBearer())])
def protected_route():
    return {"message": "This is a protected route."}

Run the Application

Save the code as main.py and start the server:

uvicorn main:app --reload

Test the API

Login: curl -X POST http://localhost:8000/login – returns a JWT.

Protected:

curl -X GET http://localhost:8000/protected -H "Authorization: Bearer <your_token>"

Practical JWT Usage Tips

1. Add an exp claim to set token expiration; otherwise a stolen token never expires.

2. Use HTTPS to transmit JWTs and protect them from man‑in‑the‑middle attacks.

3. Do not store sensitive data in the payload because it is only base64‑encoded and can be read.

4. Use a strong secret (e.g., >256‑bit random string) and keep it out of source code.

5. Implement a Refresh Token strategy to issue short‑lived access tokens and longer‑lived refresh tokens.

6. Blacklist revoked tokens (e.g., store them in Redis) since JWTs are stateless.

7. Prevent replay attacks by combining HTTPS, binding tokens to client IP/device, or using one‑time nonces.

Conclusion

JWT provides a secure and convenient way to manage user identity across distributed systems. By customizing claims, expiration, and verification logic, developers can tailor JWT authentication to meet the security requirements of their FastAPI applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonBackend DevelopmentSecurityAuthenticationJWTFastAPI
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.