from datetime import datetime, timedelta from typing import Any, Optional, Union from jose import jwt from passlib.context import CryptContext from pydantic import ValidationError from app.core.config import settings from app.schemas.token import TokenPayload pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") ALGORITHM = "HS256" def create_access_token( subject: Union[str, Any], expires_delta: Optional[timedelta] = None ) -> str: """ Create a JWT access token. """ if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta( minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES ) to_encode = {"exp": expire, "sub": str(subject)} encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def verify_password(plain_password: str, hashed_password: str) -> bool: """ Verify that a plain password matches a hashed password. """ return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: """ Hash a password for storing. """ return pwd_context.hash(password) def decode_token(token: str) -> Optional[TokenPayload]: """ Decode a JWT token and return the payload. """ try: payload = jwt.decode( token, settings.SECRET_KEY, algorithms=[ALGORITHM] ) return TokenPayload(**payload) except (jwt.JWTError, ValidationError): return None