49 lines
1.5 KiB
Python
Executable File
49 lines
1.5 KiB
Python
Executable File
"""FastAPI dependency providers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
|
|
from fastapi import Depends, HTTPException, Request, status
|
|
from fastapi.security import OAuth2PasswordBearer
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from rehearsalhub.db.engine import get_session
|
|
from rehearsalhub.db.models import Member
|
|
from rehearsalhub.services.auth import decode_token
|
|
from rehearsalhub.repositories.member import MemberRepository
|
|
|
|
# auto_error=False so we can fall back to cookie auth without a 401 from the scheme itself
|
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login", auto_error=False)
|
|
|
|
|
|
async def get_current_member(
|
|
request: Request,
|
|
bearer_token: str | None = Depends(oauth2_scheme),
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> Member:
|
|
# Prefer Authorization: Bearer header; fall back to httpOnly cookie
|
|
token = bearer_token or request.cookies.get("rh_token")
|
|
|
|
credentials_exc = HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid or expired token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
if not token:
|
|
raise credentials_exc
|
|
try:
|
|
payload = decode_token(token)
|
|
member_id_str: str | None = payload.get("sub")
|
|
if member_id_str is None:
|
|
raise credentials_exc
|
|
member_id = uuid.UUID(member_id_str)
|
|
except Exception:
|
|
raise credentials_exc
|
|
|
|
repo = MemberRepository(session)
|
|
member = await repo.get_by_id(member_id)
|
|
if member is None:
|
|
raise credentials_exc
|
|
return member
|