feat: service token introduction

This commit is contained in:
2026-08-18 19:56:14 +07:00
parent 3d79ffb384
commit 3b7606107b
12 changed files with 170 additions and 14 deletions

View File

@@ -4,17 +4,47 @@ from fastapi import HTTPException
from pydantic import ValidationError
from sqlalchemy.ext.asyncio import AsyncSession
from core.secrets import decode_jwt
from core.auth.fetch_sub import fetch_subject_from_service
from core.secrets import decode_jwt, decode_user_jwt, get_kid_from_token
from repositories.service_signatures import get_active_signature_by_kid
from repositories.users import UserRepository
from schemas.dto import AuthContext
from schemas.jwt import JWTPayload
from schemas.jwt import ServiceJWTPayload, UserJWTPayload
async def authorize_bot(kid: str, token: str, session: AsyncSession) -> AuthContext:
signature = await get_active_signature_by_kid(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, session)
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, session: AsyncSession, service: str | None = None) -> AuthContext:
content = decode_jwt(token)
kid = get_kid_from_token(token)
if kid:
return await authorize_bot(kid, token, session)
content = decode_user_jwt(token)
try:
payload = JWTPayload.model_validate(content)
payload = UserJWTPayload.model_validate(content)
except ValidationError:
raise HTTPException(status_code=401, detail="Invalid credentials") from None