feat(01-03): configure rate limiting and CSRF protection

- Add slowapi limiter with 100/minute default limit
- Create CsrfSettings Pydantic model for fastapi-csrf-protect
- Add deps.py with get_db re-export and validate_csrf dependency
- Configure secure cookie settings (httponly, samesite=lax)
This commit is contained in:
Mikkel Georgsen 2026-01-25 20:17:49 +00:00
parent 389fae97f8
commit 81486fc4f8
2 changed files with 67 additions and 0 deletions

41
backend/app/api/deps.py Normal file
View file

@ -0,0 +1,41 @@
"""FastAPI dependency injection utilities."""
from typing import Annotated
from fastapi import Depends, Request
from fastapi_csrf_protect import CsrfProtect
from sqlalchemy.ext.asyncio import AsyncSession
from backend.app.core.security import CsrfSettings
from backend.app.db.session import get_db as _get_db
# Re-export get_db for cleaner imports in endpoints
get_db = _get_db
# Type alias for common dependency
DbSession = Annotated[AsyncSession, Depends(get_db)]
@CsrfProtect.load_config
def get_csrf_config() -> CsrfSettings:
"""Load CSRF configuration for fastapi-csrf-protect."""
return CsrfSettings()
async def validate_csrf(
request: Request,
csrf_protect: CsrfProtect = Depends(),
) -> None:
"""Validate CSRF token for state-changing requests.
Use as dependency on POST/PUT/DELETE endpoints that need CSRF protection:
@router.post("/items")
async def create_item(
_: None = Depends(validate_csrf),
db: AsyncSession = Depends(get_db),
):
...
"""
await csrf_protect.validate_csrf(request)

View file

@ -0,0 +1,26 @@
"""Security configuration for rate limiting and CSRF protection."""
from pydantic import BaseModel
from slowapi import Limiter
from slowapi.util import get_remote_address
from backend.app.core.config import settings
# Rate limiter configuration
# See: 01-RESEARCH.md Pattern 3: FastAPI Security Middleware Stack
limiter = Limiter(
key_func=get_remote_address,
default_limits=["100/minute"],
# For production, use Redis: storage_uri="redis://localhost:6379"
# For development, uses in-memory storage by default
)
class CsrfSettings(BaseModel):
"""CSRF protection settings for fastapi-csrf-protect."""
secret_key: str = settings.csrf_secret_key
cookie_samesite: str = "lax"
cookie_secure: bool = True # HTTPS only
cookie_httponly: bool = True
cookie_domain: str = settings.cookie_domain