When building web applications, security is paramount. Two crucial aspects of security are authentication and authorization. In this blog post, we'll explore how to implement these features in FastAPI, a modern, fast (high-performance) Python web framework.
Before diving into the implementation details, let's clarify the difference between authentication and authorization:
Let's start with a simple example of basic authentication:
from fastapi import FastAPI, Depends, HTTPException from fastapi.security import HTTPBasic, HTTPBasicCredentials from starlette.status import HTTP_401_UNAUTHORIZED app = FastAPI() security = HTTPBasic() def authenticate_user(credentials: HTTPBasicCredentials = Depends(security)): correct_username = "admin" correct_password = "secretpassword" if credentials.username != correct_username or credentials.password != correct_password: raise HTTPException( status_code=HTTP_401_UNAUTHORIZED, detail="Invalid credentials", headers={"WWW-Authenticate": "Basic"}, ) return credentials.username @app.get("/protected") def protected_route(username: str = Depends(authenticate_user)): return {"message": f"Hello, {username}!"}
In this example, we use FastAPI's built-in HTTPBasic
for basic authentication. The authenticate_user
function checks the provided credentials against hardcoded values (in a real-world scenario, you'd validate against a database).
For more complex applications, JSON Web Tokens (JWT) are often preferred. Here's how you can implement JWT authentication in FastAPI:
from fastapi import FastAPI, Depends, HTTPException from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt from passlib.context import CryptContext from datetime import datetime, timedelta SECRET_KEY = "your-secret-key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def create_access_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 get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=401, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception return username @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): # Validate user credentials (replace with your own logic) if form_data.username != "testuser" or form_data.password != "testpassword": raise HTTPException(status_code=400, detail="Incorrect username or password") access_token = create_access_token(data={"sub": form_data.username}) return {"access_token": access_token, "token_type": "bearer"} @app.get("/protected") async def protected_route(current_user: str = Depends(get_current_user)): return {"message": f"Hello, {current_user}!"}
This example demonstrates JWT authentication using the python-jose
library for JWT handling and passlib
for password hashing.
Once a user is authenticated, you'll often want to control what resources they can access. Here's a simple role-based authorization example:
from fastapi import FastAPI, Depends, HTTPException from fastapi.security import OAuth2PasswordBearer app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def get_current_user(token: str = Depends(oauth2_scheme)): # Implement your token validation logic here # For this example, we'll use a dummy user return {"username": "testuser", "role": "user"} def admin_only(current_user: dict = Depends(get_current_user)): if current_user["role"] != "admin": raise HTTPException(status_code=403, detail="Not authorized") return current_user @app.get("/user") async def user_route(current_user: dict = Depends(get_current_user)): return {"message": f"Hello, {current_user['username']}!"} @app.get("/admin") async def admin_route(current_user: dict = Depends(admin_only)): return {"message": "Welcome, admin!"}
In this example, we define an admin_only
dependency that checks if the current user has the "admin" role. The /admin
route is protected and only accessible to users with the admin role.
Implementing robust authentication and authorization in FastAPI is crucial for building secure web applications. By following these patterns and best practices, you can ensure that your app is protected against common security threats.
21/09/2024 | Python
15/10/2024 | Python
08/11/2024 | Python
06/10/2024 | Python
14/11/2024 | Python
26/10/2024 | Python
17/11/2024 | Python
26/10/2024 | Python
26/10/2024 | Python
26/10/2024 | Python
06/10/2024 | Python
14/11/2024 | Python