Guarding the Gates: Security and Authentication
Guarding the Gates: Security and Authentication
Section titled “Guarding the Gates: Security and Authentication”OAuth2 with Password Flow
Section titled “OAuth2 with Password Flow”Security can be daunting, but FastAPI provides tools to implement standard security protocols like OAuth2 without requiring you to be a cryptography expert. We will focus on the OAuth2 with Password (and Hashing) flow, which is ideal for first-party applications (like a frontend and backend owned by the same organization).
In this flow:
- The user sends a username and password to the API.
- The API verifies the credentials.
- The API responds with a token (usually a JWT - JSON Web Token).
- The frontend stores this token and sends it in the
Authorizationheader for subsequent requests.
Defining the Security Scheme
Section titled “Defining the Security Scheme”First, we define how the API expects to receive the token using OAuth2PasswordBearer. The tokenUrl parameter tells the frontend (and the automatic docs) where to send the username/password to get the token.
from fastapi import Depends, FastAPIfrom fastapi.security import OAuth2PasswordBearerfrom typing import Annotated
app = FastAPI()
# The URL where the user sends their credentialsoauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")The get_current_user Dependency
Section titled “The get_current_user Dependency”The core of our security is a dependency commonly named get_current_user. This dependency will rely on the oauth2_scheme we created above.
When you use Depends(oauth2_scheme), FastAPI will automatically look for the Authorization header, extract the Bearer token, and pass it as a string to your function. Inside get_current_user, you would typically:
- Decode the JWT token.
- Check if it is expired.
- Extract the user ID.
- Fetch the user from the database.
- If any step fails, raise a
HTTPExceptionwith status code 401 (Unauthorized).
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): user = decode_token_and_find_user(token) # Pseudocode logic if not user: raise HTTPException( status_code=401, detail="Invalid authentication credentials", headers={"WWW-Authenticate": "Bearer"}, ) return userProtecting Routes
Section titled “Protecting Routes”Now, protecting a route is as simple as adding the dependency to the path operation function. By adding Depends(get_current_user), you ensure that the code inside the function only runs if a valid user was found.
@app.get("/users/me")async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]): return current_userA massive benefit here is the automatic interactive documentation. Swagger UI will now show an “Authorize” button, allowing you to log in directly within the documentation to test your secured endpoints.