DEV Community

Cover image for FastAPI for AI Engineers - Part 7: Protecting Routes with JWT Tokens
Ananya S
Ananya S

Posted on

FastAPI for AI Engineers - Part 7: Protecting Routes with JWT Tokens

In the previous article, we learned how to:

  • Register users
  • Hash passwords using bcrypt
  • Verify passwords during login
  • Generate JWT tokens

However, generating a token alone doesn't secure an application.

Anyone can still access endpoints unless we verify the token before granting access.

Today we'll learn how FastAPI identifies users from JWT tokens and protects routes from unauthorized access.

Do check out the previous post to understand this:

The Problem

Suppose we have:

@app.get("/profile")
def get_profile():
    return {"message": "My profile"}
Enter fullscreen mode Exit fullscreen mode

Anyone can access this endpoint.

There is no verification of:

  • Who is making the request
  • Whether they are logged in
  • Whether their token is valid

Authentication Flow

Login
   ↓
Generate JWT
   ↓
Store JWT
   ↓
Send JWT with Request
   ↓
Verify JWT
   ↓
Allow Access

Enter fullscreen mode Exit fullscreen mode

Extracting the Token

OAuth2PasswordBearer

OAuth2PasswordBearer is a class provided by FastAPI to handle security and authentication using OAuth2 with the Password flow and Bearer tokens. This class simplifies the process of implementing secure authentication in your FastAPI application.

To use OAuth2PasswordBearer, you need to create an instance of it and pass the tokenUrl parameter, which specifies the URL where the client will send the username and password to obtain a token.

from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(
    tokenUrl="login"
)
Enter fullscreen mode Exit fullscreen mode

When a request arrives:

Authorization: Bearer eyJhbGc...

FastAPI automatically extracts the token.

Decoding JWT Tokens

from jose import jwt
from jose import JWTError
def verify_token(token: str):

    try:
        payload = jwt.decode(
            token,
            SECRET_KEY,
            algorithms=[ALGORITHM]
        )

        username = payload.get("sub")

        return username

    except JWTError:
        return None
Enter fullscreen mode Exit fullscreen mode

JWT contains:

{
  "sub": "suman",
  "exp": ...
}
Enter fullscreen mode Exit fullscreen mode

We extract the username from "sub".

Creating get_current_user()

This is the most important concept.

from fastapi import Depends

def get_current_user(
    token: str = Depends(oauth2_scheme)
):

    username = verify_token(token)

    if username is None:
        raise Exception("Invalid token")

    return username

Enter fullscreen mode Exit fullscreen mode

Every protected endpoint will use:

Depends(get_current_user)

Enter fullscreen mode Exit fullscreen mode

This ensures that users who have registered and who's JWT token has been verified only they have access to the protected route.

Protecting Routes

@app.get("/profile")
def get_profile(
    current_user: str = Depends(
        get_current_user
    )
):

    return {
        "message": f"Welcome {current_user}"
    }
Enter fullscreen mode Exit fullscreen mode

Now:

Valid Token (When access is granted as JWT token matches)

{
  "message": "Welcome suman"
}
Enter fullscreen mode Exit fullscreen mode

Invalid Token (Access not granted)

{
  "detail": "Could not validate credentials"
}
Enter fullscreen mode Exit fullscreen mode

Visual Flow

User Login
      ↓
JWT Token Generated
      ↓
Token Sent in Request
      ↓
FastAPI Extracts Token
      ↓
JWT Verification
      ↓
Current User Identified
      ↓
Protected Route Access
Enter fullscreen mode Exit fullscreen mode

Conclusion

Today we learned:

  • OAuth2PasswordBearer
  • Extracting JWT tokens
  • Decoding JWT tokens
  • Creating get_current_user()
  • Protecting routes using Depends()

In the next article, we'll move beyond authentication and implement Role-Based Access Control (RBAC), allowing different users to have different permissions.

Top comments (0)