FastAPI Security: JWTs

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints. When it comes to securing APIs, JSON Web Tokens (JWTs) are a popular choice. JWTs are a compact, URL-safe means of representing claims to be transferred between two parties. They are commonly used for authentication and information exchange in web applications. In this blog, we will explore how to use JWTs for security in FastAPI applications, covering fundamental concepts, usage methods, common practices, and best practices.

Table of Contents

  1. Fundamental Concepts of JWTs
  2. Setting Up a FastAPI Project
  3. Generating and Verifying JWTs in FastAPI
  4. Protecting Routes with JWTs
  5. Common Practices
  6. Best Practices
  7. Conclusion
  8. References

Fundamental Concepts of JWTs

What is a JWT?

A JSON Web Token consists of three parts: a header, a payload, and a signature.

  • Header: Contains information about the token type (usually “JWT”) and the signing algorithm used (e.g., HMAC SHA256 or RSA).
  • Payload: Holds the claims, which are statements about an entity (typically the user) and additional data. Claims can be classified as registered, public, or private.
  • Signature: Used to verify that the message wasn’t changed along the way and, in the case of tokens signed with a private key, that the sender of the JWT is who it says it is.

How JWTs are used for Authentication

When a user logs in, the server generates a JWT and sends it to the client. The client then includes this JWT in the Authorization header of subsequent requests. The server verifies the JWT on each request to authenticate the user.

Setting Up a FastAPI Project

First, make sure you have Python installed. Then, create a virtual environment and install FastAPI and related libraries:

python -m venv venv
source venv/bin/activate  # On Windows, use `venv\Scripts\activate`
pip install fastapi uvicorn python-jose[cryptography] passlib[bcrypt]

Here is a basic FastAPI application structure:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

To run the application, use the following command:

uvicorn main:app --reload

Generating and Verifying JWTs in FastAPI

Generating JWTs

We will use the python-jose library to generate and verify JWTs. Here is an example of generating a JWT:

from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext

# Secret key for signing the JWT
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

Verifying JWTs

def verify_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        return None

Protecting Routes with JWTs

We can create a dependency function to verify the JWT on each request. Here is an example:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def get_current_user(token: str = Depends(oauth2_scheme)):
    payload = verify_token(token)
    if not payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return payload


@app.get("/protected")
def protected_route(user: dict = Depends(get_current_user)):
    return {"message": "This is a protected route", "user": user}

Common Practices

  • Token Expiration: Set an appropriate expiration time for JWTs to limit the time a token can be used.
  • Refresh Tokens: Use refresh tokens to allow users to obtain new access tokens without logging in again.
  • Secure Storage: On the client side, store JWTs securely, such as in HTTP-only cookies.

Best Practices

  • Use Strong Secret Keys: Use a long, random secret key for signing JWTs to prevent brute-force attacks.
  • Validate Input: Validate all user input, including the JWTs received from the client.
  • Regularly Rotate Keys: Rotate the secret keys periodically to reduce the risk of a compromised key.

Conclusion

JWTs are a powerful tool for securing FastAPI applications. By understanding the fundamental concepts, usage methods, and best practices, you can effectively implement authentication and authorization in your APIs. Remember to always follow security best practices to protect your application from potential threats.

References