4-building-secure-api-endpoints-with-fastapi-and-jwt-authentication.html

Building Secure API Endpoints with FastAPI and JWT Authentication

In today's digital landscape, creating secure and efficient APIs is essential for modern web applications. FastAPI has emerged as one of the most popular frameworks for building APIs in Python, thanks to its speed, simplicity, and support for asynchronous programming. Coupled with JSON Web Tokens (JWT) for authentication, FastAPI provides a robust solution for securing your API endpoints. In this article, we'll explore how to build secure API endpoints using FastAPI and JWT authentication, providing you with actionable insights and clear code examples.

Understanding FastAPI and JWT

What is FastAPI?

FastAPI is a modern web framework for building APIs in Python 3.6 and later. It is designed to be fast, with automatic generation of OpenAPI documentation, support for asynchronous programming, and easy integration with other libraries. Its type hinting feature enhances code readability and facilitates validation.

What is JWT?

JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure. JWT is widely used for authentication and information exchange securely.

Use Cases for FastAPI and JWT

  • User Authentication: Secure user login and access to resources.
  • Microservices: Authenticate requests between microservices securely.
  • Single Page Applications (SPAs): Manage user sessions in SPAs efficiently.

Setting Up Your Environment

Before diving into the code, ensure you have Python and FastAPI installed. You can set up a virtual environment and install the necessary packages using pip.

# Create a virtual environment
python -m venv fastapi-env
source fastapi-env/bin/activate  # On Windows use `fastapi-env\Scripts\activate`

# Install FastAPI and an ASGI server (e.g., uvicorn)
pip install fastapi uvicorn python-jose[cryptography] passlib[bcrypt]

Required Packages

  • FastAPI: For building API endpoints.
  • uvicorn: For serving the application.
  • python-jose: For creating and verifying JWTs.
  • passlib: For hashing passwords securely.

Building the FastAPI Application

Step 1: Create the Main Application File

Create a file named main.py and start building your FastAPI application.

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from typing import List
from passlib.context import CryptContext
import jwt
import datetime

app = FastAPI()

# Security settings
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

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

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

# Sample user database
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": pwd_context.hash("password"),
        "disabled": False,
    }
}

# Pydantic models
class User(BaseModel):
    username: str
    email: str
    full_name: str = None
    disabled: bool = None

class UserInDB(User):
    hashed_password: str

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: str

Step 2: Create User Authentication Logic

Next, implement the functions to authenticate users and create JWT tokens.

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: datetime.timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.datetime.utcnow() + expires_delta
    else:
        expire = datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

Step 3: Create the Token Endpoint

Now, add an endpoint to handle user login and token issuance.

@app.post("/token", response_model=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=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = datetime.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 API Endpoints

Now that we can issue tokens, let’s create a protected endpoint that requires a valid JWT.

@app.get("/users/me", response_model=User)
async def read_users_me(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except jwt.PyJWTError:
        raise credentials_exception

    user = get_user(fake_users_db, token_data.username)
    if user is None:
        raise credentials_exception
    return user

Step 5: Running the Application

To run your FastAPI application, use the following command:

uvicorn main:app --reload

Step 6: Testing the API

Use tools like Postman or CURL to test your API endpoints. First, obtain a JWT token by sending a POST request to /token with the username and password. Then, use this token to access the protected /users/me endpoint.

Conclusion

Building secure API endpoints with FastAPI and JWT authentication is a powerful way to protect your applications. With FastAPI’s simplicity and the robustness of JWT, you can easily implement user authentication and safeguard your resources.

This guide provided the foundational steps for creating a secure API. As you further develop your application, consider integrating additional features like user registration, password reset functionality, and more sophisticated user management.

By following these practices, you enhance your API's security while maintaining a smooth user experience. 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.