FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
26 lines
1.0 KiB
Python
26 lines
1.0 KiB
Python
"""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
|