Source code for app.services.api_key_service

"""API key issuance + tenant resolution."""

from __future__ import annotations

import hashlib
import secrets

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.errors import TenantViolationError
from app.db.models import ApiKey


def _hash_key(raw: str) -> str:
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()


[docs] async def issue_key( session: AsyncSession, *, tenant_id: str, name: str | None = None ) -> tuple[str, ApiKey]: raw = "sfm_" + secrets.token_urlsafe(32) row = ApiKey(tenant_id=tenant_id, key_hash=_hash_key(raw), name=name) session.add(row) await session.flush() return raw, row
[docs] async def resolve_tenant(session: AsyncSession, *, key: str) -> str: h = _hash_key(key) result = await session.execute(select(ApiKey).where(ApiKey.key_hash == h)) row = result.scalar_one_or_none() if row is None or row.revoked_at is not None: raise TenantViolationError("Invalid or revoked API key") return row.tenant_id