"""Symmetric encryption for secrets stored at rest - currently just per-user API keys (app/models/user_api_key.py). Not used for passwords (those are one-way hashed via app.core.security, never decrypted) - this is specifically for secrets the app must later read back out in plaintext to actually call a third-party API on the user's behalf. """ from __future__ import annotations from cryptography.fernet import Fernet, InvalidToken from app.core.config import Settings def encrypt_secret(plaintext: str, settings: Settings) -> str: return Fernet(settings.api_key_encryption_secret).encrypt(plaintext.encode()).decode() def decrypt_secret(ciphertext: str, settings: Settings) -> str: try: return Fernet(settings.api_key_encryption_secret).decrypt(ciphertext.encode()).decode() except InvalidToken as exc: # Only real cause in practice: api_key_encryption_secret was # rotated after this value was encrypted under the old one. raise ValueError("Stored value cannot be decrypted with the current key") from exc