Гайды
Аутентификация JWT и OAuth2 в FastAPI
OAuth2PasswordBearer, эндпоинт /token, PyJWT, passlib, scopes, внешний IdP и практики безопасности.
~12 мин чтения
Аутентификация JWT и OAuth2 в FastAPI
FastAPI предоставляет OAuth2PasswordBearer и утилиты для схемы password flow (логин/пароль → токен) — удобно для SPA + собственный backend. Для соцлогинов используют OAuth2 authorization code с редиректом на провайдера (отдельная интеграция). Зависимости — Маршрутизация и Dependency Injection.
1. Схема: access + refresh (рекомендация)
- Access JWT — короткий TTL (5–15 мин), передаётся в
Authorization: Bearer. - Refresh — httpOnly cookie или отдельный эндпоинт с ротацией; длинный TTL, хранится в БД с возможностью отзыва.
Ниже упрощённо только access; в проде добавьте refresh и blacklist/revocation по jti.
2. Зависимость OAuth2PasswordBearer
from typing import Annotated
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
def decode_token(token: Annotated[str, Depends(oauth2_scheme)]) -> dict:
try:
payload = verify_jwt(token) # ваша функция (issuer, audience, exp)
return payload
except Exception:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"},
)
CurrentUser = Annotated[dict, Depends(decode_token)]
@app.get("/protected")
def protected(user: CurrentUser):
return {"sub": user.get("sub")}
tokenUrl — путь, который Swagger покажет для получения токена (должен совпадать с реальным эндпоинтом).
3. Эндпоинт /token (password flow)
from fastapi import APIRouter
from fastapi.security import OAuth2PasswordRequestForm
router = APIRouter()
@router.post("/token")
def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(401, "Incorrect username or password")
token = create_access_token({"sub": str(user.id), "scopes": form_data.scopes})
return {"access_token": token, "token_type": "bearer"}
OAuth2PasswordRequestForm — поля username, password (form-urlencoded), опционально scope.
4. JWT: создание и проверка
Используйте python-jose или PyJWT с явным алгоритмом (RS256 в проде с ключами из vault; HS256 только если один доверенный issuer).
Проверяйте exp, iat, при необходимости iss, aud, nbf.
import jwt
from datetime import datetime, timedelta, UTC
SECRET = "change-me" # из settings
ALGO = "HS256"
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
to_encode = data.copy()
expire = datetime.now(UTC) + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET, algorithm=ALGO)
def verify_jwt(token: str) -> dict:
return jwt.decode(token, SECRET, algorithms=[ALGO])
5. Хеш пароля
passlib с bcrypt или argon2:
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain: str, hashed: str) -> bool:
return pwd_context.verify(plain, hashed)
def hash_password(plain: str) -> str:
return pwd_context.hash(plain)
6. Scopes
В токен кладут scope строкой ("items:read items:write"). В FastAPI — SecurityScopes и зависимость, проверяющая пересечение с правами пользователя.
7. OAuth2 с внешним IdP (Google и т.д.)
Поток: редирект → callback с code → обмен code на токены у провайдера → создание своей сессии/JWT. Обычно библиотеки httpx-oauth / authlib; не храните client_secret в фронтенде.
8. Безопасность
- HTTPS обязателен для bearer в заголовке.
- CORS не разрешайте
*с credentials — см. CORS и лимиты. - Ограничьте
subи права минимально необходимыми. - Ротация секретов и ключей для RS256.
Связанные материалы
- Тестирование FastAPI
- OpenAPI и Swagger (
persistAuthorization)
Дальше: Быстрый старт · тег FastAPI