"""Jetons : lien magique (aléatoire, haché en base) et session JWT.""" import datetime as dt import hashlib import secrets import jwt from app.config import get_settings from app.models import Account _ALGORITHM = "HS256" def new_raw_token() -> str: """Jeton de lien magique en clair (transmis à l'utilisateur une seule fois).""" return secrets.token_urlsafe(32) def hash_token(raw: str) -> str: """Empreinte stockée en base. Le jeton a 256 bits d'entropie : un SHA-256 simple suffit (pas de sel ni de KDF nécessaires).""" return hashlib.sha256(raw.encode("utf-8")).hexdigest() def create_access_token(account: Account) -> str: settings = get_settings() now = dt.datetime.now(dt.UTC) payload = { "sub": str(account.id), "edi": str(account.edition_id), "role": account.role.value, "iat": now, "exp": now + dt.timedelta(hours=settings.session_ttl_hours), } return jwt.encode(payload, settings.jwt_secret, algorithm=_ALGORITHM) def decode_access_token(token: str) -> dict: """Lève `jwt.PyJWTError` si le jeton est invalide ou expiré.""" return jwt.decode( token, get_settings().jwt_secret, algorithms=[_ALGORITHM] )