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.
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 uvicornImplementation 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 --reloadTest 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
