TradingAgents/backend/app/services/auth_utils.py

102 lines
2.6 KiB
Python

"""
Authentication utilities - JWT and encryption
"""
import os
import jwt
import hashlib
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from cryptography.fernet import Fernet
import base64
# JWT Configuration
JWT_SECRET = os.getenv("JWT_SECRET", "dev-secret-please-change-in-production")
JWT_ALGORITHM = "HS256"
JWT_EXPIRATION_HOURS = 24 * 7 # 7 days
# Encryption key derived from JWT_SECRET (for settings encryption)
def _get_fernet_key() -> bytes:
"""Generate a Fernet-compatible key from JWT_SECRET"""
# Use SHA256 to get a consistent 32-byte key, then base64 encode
key_hash = hashlib.sha256(JWT_SECRET.encode()).digest()
return base64.urlsafe_b64encode(key_hash)
_fernet = Fernet(_get_fernet_key())
def create_access_token(user_data: Dict[str, Any]) -> str:
"""
Create a JWT access token for a user
Args:
user_data: Dict containing user info (id, email, name, avatar_url)
Returns:
JWT token string
"""
payload = {
"sub": str(user_data["id"]), # Subject (user ID)
"email": user_data["email"],
"name": user_data.get("name"),
"avatar_url": user_data.get("avatar_url"),
"exp": datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS),
"iat": datetime.utcnow(),
}
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
def verify_access_token(token: str) -> Optional[Dict[str, Any]]:
"""
Verify and decode a JWT access token
Args:
token: JWT token string
Returns:
Decoded payload if valid, None if invalid/expired
"""
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None
def encrypt_settings(settings_json: str) -> str:
"""
Encrypt user settings JSON string
Args:
settings_json: JSON string of user settings
Returns:
Encrypted string (base64 encoded)
"""
encrypted = _fernet.encrypt(settings_json.encode())
return encrypted.decode()
def decrypt_settings(encrypted_settings: str) -> str:
"""
Decrypt user settings
Args:
encrypted_settings: Encrypted settings string
Returns:
Decrypted JSON string
"""
decrypted = _fernet.decrypt(encrypted_settings.encode())
return decrypted.decode()
def get_user_id_from_token(token: str) -> Optional[str]:
"""Extract user ID from a valid token"""
payload = verify_access_token(token)
if payload:
return payload.get("sub")
return None