61 lines
2.1 KiB
Python
61 lines
2.1 KiB
Python
from datetime import UTC, datetime
|
|
|
|
from fastapi import HTTPException
|
|
from pydantic import ValidationError
|
|
|
|
from core.auth.fetch_sub import fetch_subject_from_service
|
|
from core.secrets import decode_jwt, decode_user_jwt, get_kid_from_token
|
|
from db.session import UnitOfWork
|
|
from repositories.service_signatures import get_active_signature_by_kid
|
|
from repositories.users import UserRepository
|
|
from schemas.dto import AuthContext
|
|
from schemas.jwt import ServiceJWTPayload, UserJWTPayload
|
|
|
|
|
|
async def authorize_bot(kid: str, token: str, uow: UnitOfWork) -> AuthContext:
|
|
signature = await get_active_signature_by_kid(uow.session, kid)
|
|
|
|
if not signature:
|
|
raise HTTPException(401, detail="Invalid service signature")
|
|
|
|
content = decode_jwt(token, signature.public_key, algo="EdDSA")
|
|
|
|
try:
|
|
payload = ServiceJWTPayload.model_validate(content)
|
|
except ValidationError:
|
|
raise HTTPException(status_code=401, detail="Invalid credentials") from None
|
|
|
|
if payload.exp < datetime.now(UTC).timestamp():
|
|
raise HTTPException(status_code=401, detail="Access token expired")
|
|
|
|
subject = await fetch_subject_from_service(payload, uow)
|
|
if not subject:
|
|
raise HTTPException(status_code=401, detail="User not found")
|
|
|
|
return AuthContext(subject, auth_method="service", service=kid)
|
|
|
|
|
|
async def authorize(token: str, uow: UnitOfWork, service: str | None = None) -> AuthContext:
|
|
kid = get_kid_from_token(token)
|
|
|
|
if kid:
|
|
return await authorize_bot(kid, token, uow)
|
|
|
|
content = decode_user_jwt(token)
|
|
|
|
try:
|
|
payload = UserJWTPayload.model_validate(content)
|
|
except ValidationError:
|
|
raise HTTPException(status_code=401, detail="Invalid credentials") from None
|
|
|
|
if payload.exp < datetime.now(UTC).timestamp():
|
|
raise HTTPException(status_code=401, detail="Access token expired")
|
|
|
|
repo = UserRepository(uow)
|
|
user = await repo.get_user_by_id(int(payload.sub))
|
|
|
|
if not user:
|
|
raise HTTPException(status_code=401, detail="User not found")
|
|
|
|
return AuthContext(user, auth_method="jwt", service=service)
|