import hashlib import secrets from fastapi import Depends, HTTPException, Security from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy import select from sqlalchemy.orm import Session from app.database import get_db from app.models import ApiKey security_scheme = HTTPBearer(auto_error=False) def hash_api_key(raw_key: str) -> str: return hashlib.sha256(raw_key.encode("utf-8")).hexdigest() def generate_api_key() -> tuple[str, str, str]: """Returns (full_key, prefix, hash).""" raw = f"sac_{secrets.token_urlsafe(32)}" prefix = raw[:12] return raw, prefix, hash_api_key(raw) def verify_api_key(db: Session, raw_key: str) -> bool: key_hash = hash_api_key(raw_key) row = db.scalar(select(ApiKey).where(ApiKey.key_hash == key_hash, ApiKey.is_active.is_(True))) return row is not None def get_api_key_auth( credentials: HTTPAuthorizationCredentials | None = Security(security_scheme), db: Session = Depends(get_db), ) -> str: if credentials is None or credentials.scheme.lower() != "bearer": raise HTTPException(status_code=401, detail="Missing or invalid Authorization header") if not verify_api_key(db, credentials.credentials): raise HTTPException(status_code=401, detail="Invalid API key") return credentials.credentials