Hello! Welcome back.
In our last lesson, we successfully created a /token endpoint. This allows a user to exchange their username and password for a JWT access token. Now we have the key, but all the doors in our application are still unlocked. Today, we'll learn how to install the locks.
Our learning outcome is to implement a dependency using OAuth2PasswordBearer that extracts and validates a JWT from request headers. This is the mechanism that will stand guard at our protected endpoints, check for a valid token, and identify the user making the request.
This is the direct counterpart to Django REST Framework's authentication classes, like rest_framework_simplejwt.authentication.JWTAuthentication. In DRF, the authentication class automatically inspects the request headers, validates the token, and attaches a user object to the request. We are about to build the FastAPI equivalent of this process from scratch using its powerful dependency injection system.
1. The Tool for the Job: OAuth2PasswordBearer
To start, we need to tell FastAPI how clients are expected to provide the token. The standard method is using an Authorization header with a "Bearer" scheme, which looks like this: Authorization: Bearer <your_long_token_string>.
FastAPI provides a utility class specifically for this: OAuth2PasswordBearer. Let's create an instance of it.
from fastapi.security import OAuth2PasswordBearer
# The tokenUrl is the URL that the client will use to get the token.
# In our case, it's the endpoint we built in the last lesson.
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
The tokenUrl parameter might seem a bit confusing at first. It does not mean that this object will send a request to that URL to validate the token. Instead, its primary purpose is for the OpenAPI documentation. When you use this scheme, Swagger UI will show an "Authorize" button. The tokenUrl tells the interactive docs which endpoint to use to acquire a token when you enter your username and password in the UI's authorization modal.

2. Creating the Core Validation Dependency
Now that we have our oauth2_scheme, we can use it to build our main dependency. This function will be responsible for extracting the token, decoding it, validating its contents, and fetching the corresponding user. Let's call it get_current_user.
The official FastAPI documentation provides the canonical implementation for this. It's a critical piece of code to understand.
OAuth2 with Password (and hashing), Bearer with JWT tokens
Please read the official documentation on how to create the get_current_user dependency. This is the heart of today's lesson.
Read the sections titled 'Update the dependencies' and then the full code block right below it. Focus on the get_current_user function. Notice how it takes token: Annotated[str, Depends(oauth2_scheme)] as an argument. Then, follow the logic inside: the try/except block, the call to jwt.decode, and how it raises an HTTPException on failure.
Let's break down the function you just read about. Here is a typical implementation, combining the concepts from the documentation:
from typing import Annotated
from datetime import datetime
from fastapi import Depends, HTTPException, status
import jwt # PyJWT library
from pydantic import BaseModel
from . import crud, schemas # Your project's modules
from .utils import SECRET_KEY, ALGORITHM # From previous lesson
# This is the scheme we just defined
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
class TokenData(BaseModel):
username: str | None = None
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
# 1. Decode the token
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# 2. Extract the username from the 'sub' claim
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except jwt.PyJWTError:
# This catches any error from PyJWT (e.g., invalid signature, expired token)
raise credentials_exception
# 3. Fetch the user from the database
user = crud.get_user(username=token_data.username) # This function queries your DB
if user is None:
raise credentials_exception
# 4. Return the user object (e.g., a Pydantic model)
return user
Let's dissect this powerful function:
token: Annotated[str, Depends(oauth2_scheme)]: This is the dependency injection in action.Depends(oauth2_scheme)tells FastAPI to execute ouroauth2_schemeinstance.- This instance looks for the
Authorizationheader, checks if it's aBearertoken, and if so, passes the token string to ourtokenparameter. - If no token is found, or the header is malformed, it automatically raises a 401 error. Your function's code won't even run.
jwt.decode(...): We use thePyJWTlibrary to decode the token. This also verifies the signature and the expiration time. If either is invalid, it raises an error.try...except jwt.PyJWTError: This is crucial for security. We wrap the decoding in atry...exceptblock. Any failure in decoding means the token is invalid, so we immediatelyraiseourcredentials_exception.- Payload and User Validation: After decoding, we extract the user identifier (from the
subclaim) and use it to fetch the user from our database. If the user no longer exists, we again raise thecredentials_exception. - Return User: If all checks pass, the function returns the user object. FastAPI will then inject this return value into the path operation function wherever it's requested.
For a dynamic walkthrough of this process, the following video is an excellent supplement.
FastAPI JWT Tutorial | How to add User Authentication
This video by Eric Roby demonstrates creating the get_current_user dependency from scratch. Seeing it coded live can help connect all the pieces.
Watch the segment from 00:17:07 to 00:18:20. The author creates the get_current_user function, explaining how OAuth2PasswordBearer is used and how the JWT is decoded and validated.
3. Protecting an Endpoint
Now that we've built our guard, let's post it at a door. To protect a path operation, you simply add get_current_user as another dependency.
Imagine we want to create an endpoint /users/me that returns the profile of the currently logged-in user.
@router.get("/users/me", response_model=schemas.User)
async def read_users_me(
current_user: Annotated[schemas.User, Depends(get_current_user)]
):
"""
Get the profile for the current user.
"""
return current_user
This is where the elegance of FastAPI's dependency injection shines:
- Before running
read_users_me, FastAPI first runsget_current_user. - If
get_current_userraises anHTTPException, the request is immediately terminated with a401 Unauthorizedresponse. The code insideread_users_meis never executed. - If
get_current_usersucceeds, it returns a user object. FastAPI assigns this object to thecurrent_userparameter. - Your path operation function can now simply use the
current_userobject, confident that the user is authenticated.
This simple act of adding the dependency is what puts the padlock icon next to your endpoints in the Swagger UI.

This pattern is incredibly powerful. The dependency handles the complex and repetitive logic of authentication, keeping your actual business logic clean and focused.
Conclusion
Today you've implemented the most critical part of your API's security. You now have a reusable dependency that can be used to protect any endpoint.
Key Takeaways:
OAuth2PasswordBeareris a utility class that creates a dependency for extracting a Bearer token from theAuthorizationheader.- The
tokenUrlparameter is used by the interactive API docs to know where to request a token. - A validation dependency (e.g.,
get_current_user) is anasync deffunction that usesDepends(oauth2_scheme)to get the token. - Inside this dependency, you must decode the token, handle potential errors with a
try...exceptblock, and verify the user exists in your database. - Invalid tokens or users should trigger a
401 UnauthorizedHTTPException. - To protect an endpoint, you add this validation function as a dependency to the path operation's signature. The return value of the dependency is injected as an argument.
Preview of the Next Lesson:
We've now built the get_current_user dependency and seen how to use it. In the next lesson, we will focus on protecting routes by injecting the current authenticated user via a dependency and formally compare this pattern to DRF's permission_classes. We'll explore how to chain dependencies to create more granular checks, such as verifying if a user is active or has specific permissions, which will set us up for implementing role-based access control.