Protecting Your FastAPI Routes Using Auth0

Protecting Your FastAPI Routes Using Auth0

A Step-by-Step Guide

Hello everyone! In this blog, I will guide you through protecting your FastAPI routes using Auth0. This guide is structured so you can follow it step by step from scratch and get authentication up and running. Let’s dive in!

Step 1: Setting Up Your FastAPI Project

First, let’s create a new FastAPI project. I prefer using Poetry for dependency management and project setup. Install Poetry if you haven’t already, and then run:

poetry new api
cd api
code .

The poetry new command creates a new Python project with a default folder structure. Replace api with your desired project name. The code . command opens the project in VSCode.

This is what your folder structure would look like within the api folder. In the api folder you will have another api folder and the tests folder.

Next, install the necessary dependencies:

poetry add fastapi pydantic-settings PyJWT

Here’s what these packages do:

  • FastAPI: The web framework for building APIs.

  • Pydantic-Settings: For managing application settings using environment variables.

  • PyJWT: For working with JSON Web Tokens (JWTs).

Step 2: Configuring Environment Variables

Create a .env file in the root of your project with the following contents:

AUTH0_DOMAIN=dev-ckugv3wsww6e6cqh.us.auth0.com
AUTH0_API_AUDIENCE=https://syflow-api.com
AUTH0_ISSUER=https://dev-ckugv3wsww6e6cqh.us.auth0.com/
AUTH0_ALGORITHMS=RS256

Explanation:

  • AUTH0_DOMAIN: Your Auth0 tenant domain.

  • AUTH0_API_AUDIENCE: The identifier for your API in Auth0.

  • AUTH0_ISSUER: The URL of the token issuer (your Auth0 domain).

  • AUTH0_ALGORITHMS: The algorithm used to sign the tokens (RS256 in this case).

You will get these values from your Auth0 dashboard later.

Step 3: Setting Up Configuration

Create a new file named config.py in the api/api folder:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    AUTH0_DOMAIN: str
    AUTH0_API_AUDIENCE: str
    AUTH0_ISSUER: str
    AUTH0_ALGORITHMS: str = "RS256"

    class Config:
        env_file = ".env"

settings = Settings()

This file reads the environment variables defined in the .env file and makes them available as settings.

Step 4: Implementing Token Verification

Create a main.py file in the api/api folder with the following code:

import jwt
from fastapi import Depends, FastAPI, Response, status
from fastapi.security import HTTPBearer
from api.config import settings

app = FastAPI()
token_auth_scheme = HTTPBearer()

def set_up():
    """Sets up configuration for the app"""
    return {
        "DOMAIN": settings.AUTH0_DOMAIN,
        "API_AUDIENCE": settings.AUTH0_API_AUDIENCE,
        "ISSUER": settings.AUTH0_ISSUER,
        "ALGORITHMS": settings.AUTH0_ALGORITHMS,
    }

class VerifyToken:
    """Verifies JWT tokens using PyJWT."""
    def __init__(self, token):
        self.token = token
        self.config = set_up()
        self.jwks_client = jwt.PyJWKClient(f'https://{self.config["DOMAIN"]}/.well-known/jwks.json')

    def verify(self):
        try:
            signing_key = self.jwks_client.get_signing_key_from_jwt(self.token).key
            payload = jwt.decode(
                self.token,
                signing_key,
                algorithms=self.config["ALGORITHMS"],
                audience=self.config["API_AUDIENCE"],
                issuer=self.config["ISSUER"],
            )
            return payload
        except Exception as e:
            return {"status": "error", "message": str(e)}

@app.get("/api/public")
async def public():
    return {"message": "Hello, World!"}

@app.get("/api/private")
def private(response: Response, token: str = Depends(token_auth_scheme)):
    verifier = VerifyToken(token.credentials)
    result = verifier.verify()
    if "status" in result and result["status"] == "error":
        response.status_code = status.HTTP_401_UNAUTHORIZED
        return result
    return {"message": "Access granted", "data": result}

Explanation:

  • set_up Function: Loads the Auth0 configuration.

  • VerifyToken Class: Handles JWT verification using PyJWT and Auth0’s JWKS endpoint.

  • Public Route: Open to everyone.

  • Private Route: Requires a valid JWT in the Authorization header.

Step 5: Running the Server

This is what the final project structure should look like:

Activate the virtual environment:

poetry shell

Run the FastAPI development server:

pip install "fastapi[standard]" # To install the CLI 
fastapi dev api/main.py

Visit http://127.0.0.1:8000 to access your API. Test the routes:

  • Public Route:
curl http://127.0.0.1:8000/api/public

Expected Response:

{"message":"Hello World"}
  • Private Route (Without Token):
curl http://127.0.0.1:8000/api/private

Expected Response:

{"detail":"Not authenticated"}

Step 6: Setting Up Auth0

  1. Log in to your Auth0 dashboard.

  2. Navigate to Applications → APIs. You will only have the default Auth0 Management API there by default as you can see below.

  3. Click on the “Create API” button.

  4. Create a new API with the following:

    • Name: Your API’s name.

    • Identifier: Match this with AUTH0_API_AUDIENCE in your .env file.

    • Signing Algorithm: RS256.

  1. Now, your API is created on the dashboard.

  2. For the AUTH0_DOMAIN and the AUTH0_ISSUER environment variables go to the top left of your dashboard and copy your domain. Use this domain to populate both the fields in your env file. This should complete your env file config since we populated the AUTH0_API_AUDIENCE variable in the last step.

     AUTH0_DOMAIN=sampledomain.us.auth0.com
     AUTH0_ISSUER=https://sampledomain.us.auth0.com/
    
  3. Navigate to the Test tab in your API and copy the generated token

Step 7: Testing the Private Route

Use the token to access the private route:

curl --request GET \
  --url http://127.0.0.1:8000/api/private \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"

Replace YOUR_ACCESS_TOKEN with the token from the Auth0 dashboard.

Expected Response:

{"iss":"https://dev-ckugv7w3ww6e6cqh.us.auth0.com/","sub":"sWJp3EOBM5lbyEYNrD4mZZRmmwgn@clients","aud":"https://syflow-api.com","iat":1737547702,"exp":1737634102,"gty":"client-credentials","azp":"sWufmoJp3EOBM5lbyD4mZZRmmwgn"}

Conclusion

Congratulations! You’ve successfully protected your FastAPI routes using Auth0. This setup ensures that only authorized users can access protected routes, enhancing your API’s security. I hope this guide was helpful. Happy coding!