from datetime import datetime, timedelta, timezone
from fastapi.security import OAuth2PasswordBearer
from jose import jwt
from passlib.context import CryptContext
SECRET_KEY = "change-me-in-production-use-env-var"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
def create_access_token(subject: str, expires_delta: timedelta | None = None) -> str:
expire = datetime.now(timezone.utc) + (
expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
payload = {"sub": subject, "exp": expire}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
from fastapi import Depends, HTTPException, status
from jose import JWTError, jwt
from sqlalchemy.orm import Session
from .database import get_db
from .models import User
from .schemas import TokenData
from .security import ALGORITHM, SECRET_KEY, oauth2_scheme
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db),
) -> User:
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = db.query(User).filter(User.username == token_data.username).first()
if user is None:
raise credentials_exception
return user
def get_current_active_user(
current_user: User = Depends(get_current_user),
) -> User:
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from .database import get_db
from .deps import get_current_active_user
from .models import User
from .schemas import Token, UserPublic
from .security import create_access_token, verify_password
router = APIRouter(tags=["auth"])
def authenticate_user(db: Session, username: str, password: str) -> User | None:
user = db.query(User).filter(User.username == username).first()
if not user or not verify_password(password, user.hashed_password):
return None
return user
@router.post("/token", response_model=Token)
def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_db),
):
user = authenticate_user(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token = create_access_token(subject=user.username)
return Token(access_token=access_token, token_type="bearer")
@router.get("/users/me", response_model=UserPublic)
def read_current_user(current_user: User = Depends(get_current_active_user)):
return current_user
from pydantic import BaseModel, ConfigDict
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
class TokenData(BaseModel):
username: str | None = None
class UserPublic(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
username: str
disabled: bool = False
This snippet shows the standard way to protect FastAPI routes with a bearer token issued from a password login flow, then decoded and validated on every request through a reusable dependency. The design centers on FastAPI's dependency injection: a single get_current_user dependency does the token work once and is reused everywhere via Depends, so route handlers stay free of auth boilerplate.
In security.py, OAuth2PasswordBearer is instantiated with a tokenUrl pointing at the login endpoint. That instance is itself a callable dependency — when used as Depends(oauth2_scheme), it extracts the Authorization: Bearer <token> header, raises a 401 automatically when it is missing, and also drives the interactive Swagger UI "Authorize" button. The module wraps python-jose and passlib so the rest of the app never touches raw crypto: create_access_token signs a payload with an exp claim and HS256, while verify_password and get_password_hash use a bcrypt CryptContext. Keeping the secret and algorithm here centralizes the trust boundary.
The core of the pattern lives in deps.py. get_current_user depends on both oauth2_scheme (for the raw token) and a DB session, decodes the JWT inside a try/except JWTError, and pulls the subject from the sub claim. A shared credentials_exception returns 401 with the WWW-Authenticate: Bearer header required by the OAuth2 spec. Because decoding failures, missing claims, and unknown users all funnel to the same exception, the endpoint never leaks which check failed. A second dependency, get_current_active_user, layers on top to reject disabled accounts, demonstrating how dependencies compose to build coarser guarantees from smaller ones.
In auth_router.py, the /token route accepts OAuth2PasswordRequestForm, which parses the form-encoded username/password that OAuth2 clients send, authenticates against the store, and returns a Token response. The protected /users/me route simply declares current_user: User = Depends(get_current_active_user) and receives a fully validated user.
The main trade-off of stateless JWTs is revocation: tokens remain valid until exp, so short lifetimes or a denylist are needed for immediate logout. Reaching for this pattern makes sense whenever an API needs standards-compliant token auth with minimal per-route code.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
#!/usr/bin/env bash
set -euo pipefail
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"
Secrets management with environment isolation and Vault
<?php
namespace App\Providers;
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;
Laravel service container and dependency injection
Share this code
Here's the card — post it anywhere.