3-building-a-secure-api-with-fastapi-and-jwt-authentication.html

Building a Secure API with FastAPI and JWT Authentication

In today's digital landscape, securing APIs is paramount. With the increasing number of cyber threats, developers need robust authentication methods to protect sensitive data. FastAPI, a modern web framework for Python, excels at building APIs quickly, but its real strength lies in its ability to integrate security features seamlessly. In this article, we will explore how to build a secure API using FastAPI with JSON Web Tokens (JWT) for authentication.

What is FastAPI?

FastAPI is a high-performance web framework for building APIs with Python. It is built on top of Starlette and Pydantic, offering features such as:

  • Fast: Asynchronous capabilities make it incredibly fast.
  • Easy to use: Type hints provide automatic data validation and serialization.
  • Great documentation: Interactive API documentation is generated automatically.

Given these features, FastAPI is an excellent choice for developers looking to build secure and efficient APIs.

Understanding JWT Authentication

JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. JWTs are commonly used for authentication and information exchange because they are:

  • Stateless: The server does not need to store session information.
  • Self-contained: JWTs contain all the information needed for authentication.

A typical JWT consists of three parts:

  1. Header: Contains metadata about the token (e.g., type and signing algorithm).
  2. Payload: Contains the claims (e.g., user information).
  3. Signature: Used to verify the integrity of the token.

Use Cases for JWT

  • Single Sign-On (SSO): Users can authenticate once and gain access to multiple applications.
  • Mobile Applications: Securely authenticate users without maintaining session state on the server.
  • Microservices Architecture: Allow different services to authenticate users without sharing session data.

Building a Secure API with FastAPI and JWT

Now, let’s dive into the implementation of a secure API using FastAPI and JWT authentication. We will create a simple user authentication system. Follow these steps:

Step 1: Setting Up Your Environment

First, make sure you have Python installed on your machine. Then, create a new directory for your project and install FastAPI and the necessary dependencies.

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

Step 2: Create the FastAPI Application

Create a file named main.py and set up the basic FastAPI application structure.

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from typing import Optional
from datetime import datetime, timedelta
import jwt

# Constants
SECRET_KEY = "your_secret_key"  # Replace with a strong secret key
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# FastAPI instance
app = FastAPI()

# OAuth2 scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# User model
class User(BaseModel):
    username: str
    email: Optional[str] = None

class UserInDB(User):
    hashed_password: str

# Database simulation
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "$2b$12$KIX2gCz2p1C8v3P2s1M6deh2gOZ3xT1VxZ8w1nYh5b6R38t3nC2mO",  # "secret"
    }
}

Step 3: Implementing User Authentication

Add functions to authenticate users and create JWT tokens.

from passlib.context import CryptContext

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

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)

def create_access_token(data: dict, expires_delta: 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

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = get_user(fake_users_db, form_data.username)
    if not user or not verify_password(form_data.password, user.hashed_password):
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(data={"sub": user.username}, expires_delta=access_token_expires)
    return {"access_token": access_token, "token_type": "bearer"}

Step 4: Protecting Your API Endpoints

Now, let’s secure an endpoint that requires authentication.

@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=401,
        detail="Could not validate credentials",
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except jwt.PyJWTError:
        raise credentials_exception
    user = get_user(fake_users_db, username)
    if user is None:
        raise credentials_exception
    return user

Step 5: Running the Application

To run the application, use the following command:

uvicorn main:app --reload

You can now access the API at http://127.0.0.1:8000. The interactive docs are available at http://127.0.0.1:8000/docs.

Troubleshooting Tips

  • Invalid Token: Ensure your JWT secret key is correct and consistent across your application.
  • Password Hashing Issues: Use the correct hashing scheme for passwords. The Passlib library simplifies this process.
  • Rate Limiting: Consider implementing rate limiting to prevent abuse of your authentication endpoint.

Conclusion

Building a secure API with FastAPI and JWT authentication is straightforward and efficient. By following the steps outlined in this article, you can create a robust authentication system that guards against unauthorized access. FastAPI’s speed and ease of use, combined with JWT's stateless nature, make for a powerful combination in API development.

Now that you have a foundational understanding of FastAPI and JWT, you can expand on this example, adding features like user registration, role-based access control, and more. The possibilities are endless! Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.