Information Security 11 min read

Secure FastAPI APIs with JWT: Step‑by‑Step Authentication & Role‑Based Authorization

Learn how to implement stateless JWT authentication and role‑based authorization in FastAPI, covering token structure, installation of PyJWT, creating login and protected endpoints, custom dependencies, and testing via Swagger UI, while highlighting security benefits and best practices for robust backend APIs.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Secure FastAPI APIs with JWT: Step‑by‑Step Authentication & Role‑Based Authorization

In system development, authentication and authorization are key to ensuring security and privacy.

Authentication : Verifies user identity, commonly using OAuth 2.0 or JWT (JSON Web Token) to securely transmit information and reduce server load.

Authorization : Determines access rights based on user roles, e.g., administrators have full control while regular users access limited functions.

This article focuses on JWT authentication and authorization. JWT, defined in RFC 7519, is an open standard for securely transmitting JSON objects between parties. A JWT consists of three parts—Header, Payload, and Signature—separated by dots ( . ).

Header : Contains metadata such as the signing algorithm (e.g., HMAC SHA256 or RSA) and is Base64‑encoded.

Payload (Claims): Holds the data to be transmitted, like user ID and role. Although encoded, it is not encrypted, so sensitive information should not be placed here.

Signature : Validates the token’s integrity and the sender’s identity by signing the Base64‑encoded Header and Payload with a secret key and algorithm.

JWT’s main advantage is its statelessness, eliminating the need for server‑side session storage and reducing server load. However, token revocation can be challenging, so short‑lived tokens combined with refresh mechanisms are commonly used.

JWT is widely applied in single sign‑on (SSO), API authorization, and other scenarios due to its simplicity, cross‑language support, and ease of implementation.

Step 1: Understand Authentication & Authorization in FastAPI

FastAPI provides two primary ways to control access:

Authentication : Verifies a user’s identity, typically via tokens or credentials.

Authorization : Determines which resources a user can access based on roles or permissions.

We will use JWT tokens to implement secure authentication and restrict certain endpoints to authenticated users only.

Why Use JWT for Authentication?

Stateless : No server‑side session required.

Secure : Tokens are signed to prevent tampering.

Broadly Supported : Compatible with many frameworks and libraries.

Step 2: Set Up FastAPI JWT Authentication

First, install the PyJWT package to handle JWT creation and verification.

<code>pip install PyJWT</code>

In main.py , define two key endpoints:

/login : Generates a JWT token.

/protected : Accessible only to authenticated users.

Step 3: Define JWT Utility Functions

Create helper functions for generating and verifying JWTs. Replace "SECRET_KEY" with a secure, unique key for production.

<code>import jwt
from datetime import datetime, timedelta
from fastapi import HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

def create_jwt_token(data: dict):
    to_encode = data.copy()
    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

def verify_jwt_token(token: str):
    try:
        decoded_token = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return decoded_token if decoded_token["exp"] >= datetime.utcnow() else None
    except jwt.PyJWTError:
        return None</code>

create_jwt_token : Accepts data and returns a JWT.

verify_jwt_token : Decodes and validates the token, returning the payload if valid.

Step 4: Implement the Login Endpoint

Create a /login endpoint where users provide credentials to receive a JWT.

<code>from fastapi import FastAPI, Depends, HTTPException, status
from pydantic import BaseModel

app = FastAPI()

fake_users_db = {"johndoe": {"username": "johndoe", "password": "secretpassword"}}

class Login(BaseModel):
    username: str
    password: str

@app.post("/login")
async def login(user: Login):
    db_user = fake_users_db.get(user.username)
    if not db_user or db_user["password"] != user.password:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username or password")
    token_data = {"sub": user.username}
    token = create_jwt_token(data=token_data)
    return {"access_token": token, "token_type": "bearer"}</code>

fake_users_db : A mock user database.

/login : Validates credentials and returns an access token on success.

When a user logs in with valid credentials, they receive a JWT that must be used to access protected resources.

Step 5: Protect Endpoints with JWT Authentication

Create a custom dependency using HTTPBearer to validate JWTs.

<code>security = HTTPBearer()

async def get_current_user(credentials: HTTPAuthorizationCredentials = Security(security)):
    token = credentials.credentials
    payload = verify_jwt_token(token)
    if payload is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token")
    return payload</code>

get_current_user : Extracts and verifies the JWT from the Authorization header.

HTTPBearer : Provides a Bearer token security scheme commonly used for JWT authentication.

Apply Depends(get_current_user) to protected routes.

Step 6: Add a Protected Endpoint

Define a /protected endpoint that only authenticated users can access.

<code>@app.get("/protected")
async def protected_route(current_user: dict = Depends(get_current_user)):
    return {"message": f"Hello, {current_user['sub']}! You are authenticated."}</code>

The dependency ensures the request includes a valid token before granting access.

Step 7: Role‑Based Authorization (Optional)

Extend the JWT payload with a user role to implement role‑based access control.

Role‑Based Authorization Steps

Update create_jwt_token to accept a role parameter.

Include the role in the token payload when creating the token.

<code>def create_jwt_token(data: dict, role: str):
    to_encode = data.copy()
    to_encode.update({"role": role, "exp": datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt</code>

Protect endpoints based on role.

<code>@app.get("/admin")
async def admin_route(current_user: dict = Depends(get_current_user)):
    if current_user.get("role") != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access forbidden")
    return {"message": "Welcome, admin!"}</code>

Only users with the admin role can access /admin , adding an extra layer of authorization.

Step 8: Test Authentication & Authorization in Swagger UI

FastAPI automatically updates the Swagger UI to include the JWT authentication endpoints:

Visit http://127.0.0.1:8000/docs .

Use the /login endpoint to obtain a JWT token.

Click the “Authorize” button in Swagger UI and paste the token.

Test the protected endpoints /protected and /admin .

This setup makes testing authentication and role‑based authorization seamless through the interactive UI.

Conclusion

By implementing JWT authentication and optional role‑based authorization in FastAPI, we create a robust and secure API that restricts access to authorized users only. These techniques are essential for protecting sensitive data and resources in real‑world applications.

backendPythonAuthenticationJWTfastapiauthorization
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.