from datetime import datetime, timedelta, timezone from typing import Any, Optional, Union import bcrypt from jose import jwt, JWTError import enum from backend.app.core.config import settings class UserRole(str, enum.Enum): USER = "USER" PATIENT = "PATIENT" DOCTOR = "DOCTOR" ADMIN = "ADMIN" def verify_password(plain_password: str, hashed_password: str) -> bool: try: pwd_bytes = plain_password.encode("utf-8")[:72] hash_bytes = hashed_password.encode("utf-8") return bcrypt.checkpw(pwd_bytes, hash_bytes) except Exception: return False def get_password_hash(password: str) -> str: pwd_bytes = password.encode("utf-8")[:72] salt = bcrypt.gensalt() return bcrypt.hashpw(pwd_bytes, salt).decode("utf-8") def create_access_token( subject: Union[str, Any], role: str = UserRole.PATIENT.value, expires_delta: Optional[timedelta] = None, ) -> str: if expires_delta: expire = datetime.now(timezone.utc) + expires_delta else: expire = datetime.now(timezone.utc) + timedelta( minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES ) to_encode = { "sub": str(subject), "role": role, "type": "access", "exp": expire, "iat": datetime.now(timezone.utc), } return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) def create_refresh_token( subject: Union[str, Any], role: str = UserRole.PATIENT.value, expires_delta: Optional[timedelta] = None, ) -> str: if expires_delta: expire = datetime.now(timezone.utc) + expires_delta else: expire = datetime.now(timezone.utc) + timedelta( days=settings.REFRESH_TOKEN_EXPIRE_DAYS ) to_encode = { "sub": str(subject), "role": role, "type": "refresh", "exp": expire, "iat": datetime.now(timezone.utc), } return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) def decode_token(token: str) -> Optional[dict]: try: payload = jwt.decode( token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] ) return payload except JWTError: return None