Files
mimic-big/backend/src/mimic/auth/soc_token.py

45 lines
1.2 KiB
Python
Raw Normal View History

"""SOC opaque token generation + verification.
Decision D-006: clear token returned once at creation, bcrypt hash persisted.
"""
from __future__ import annotations
import hmac
import secrets
from dataclasses import dataclass
import bcrypt
_TOKEN_BYTES = 32 # 256 bits of entropy
_DEFAULT_ROUNDS = 12
@dataclass(frozen=True, slots=True)
class SocTokenMaterial:
plain: str
hashed: str
def generate_token(*, rounds: int = _DEFAULT_ROUNDS) -> SocTokenMaterial:
"""Generate a fresh SOC token and its bcrypt hash."""
plain = secrets.token_urlsafe(_TOKEN_BYTES)
salt = bcrypt.gensalt(rounds=rounds)
hashed = bcrypt.hashpw(plain.encode("utf-8"), salt).decode("utf-8")
return SocTokenMaterial(plain=plain, hashed=hashed)
def verify_token(plain: str, hashed: str) -> bool:
"""Constant-time bcrypt verification of a SOC token."""
if not plain or not hashed:
return False
try:
return bcrypt.checkpw(plain.encode("utf-8"), hashed.encode("utf-8"))
except (ValueError, TypeError):
return False
def safe_compare(a: str, b: str) -> bool:
"""Constant-time string compare for non-hashed contexts."""
return hmac.compare_digest(a, b)