"""Auth dependencies (ADR-0008): resolve `AuthContext` from a bearer token, then gate routes on scope. """ from collections.abc import Awaitable, Callable from typing import Annotated from fastapi import Depends from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from src.application.auth.context import AuthContext from src.application.auth.errors import InvalidApiKeyError, MissingScopeError from src.application.auth.service import resolve_auth_context from src.bootstrap.dependencies import get_sessionmaker _bearer_scheme = HTTPBearer(auto_error=False) async def get_auth_context( credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(_bearer_scheme)], sessionmaker: Annotated[async_sessionmaker[AsyncSession], Depends(get_sessionmaker)], ) -> AuthContext: if credentials is None: raise InvalidApiKeyError("missing Authorization header") return await resolve_auth_context(sessionmaker, credentials.credentials) AuthContextDep = Annotated[AuthContext, Depends(get_auth_context)] def require_scope(scope: str) -> Callable[[AuthContext], Awaitable[AuthContext]]: async def _dependency(auth: AuthContextDep) -> AuthContext: if not auth.has_scope(scope): raise MissingScopeError(f"missing required scope '{scope}'") return auth return _dependency