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
Log in to your Auth0 dashboard.
Navigate to Applications → APIs. You will only have the default Auth0 Management API there by default as you can see below.
Click on the “Create API” button.
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.
Now, your API is created on the dashboard.
For the
AUTH0_DOMAIN
and theAUTH0_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 theAUTH0_API_AUDIENCE
variable in the last step.AUTH0_DOMAIN=sampledomain.us.auth0.com AUTH0_ISSUER=https://sampledomain.us.auth0.com/
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!