from datetime import timedelta from uuid import UUID from jose import JWTError from jose.jwt import decode, encode from api.application.protocols.date_time import DateTimeProvider from api.application.protocols.jwt import JwtTokenProcessor from api.domain.user.error import UserInvalidCredentialsError from api.domain.user.model import UserEmail, UserId from api.infrastructure.auth.jwt_settings import JwtSettings class JoseJwtTokenProcessor(JwtTokenProcessor): def __init__(self, jwt_options: JwtSettings, date_time_provider: DateTimeProvider) -> None: self.jwt_options = jwt_options self.date_time_provider = date_time_provider def generate_token(self, user_id: UserId, user_email: UserEmail) -> str: issued_at = self.date_time_provider.get_current_time() expiration_time = issued_at + timedelta(minutes=self.jwt_options.expires_in) claims = { "iat": issued_at, "exp": expiration_time, "sub": str(user_id.value), "email": user_email.value, } return encode(claims, self.jwt_options.secret, self.jwt_options.algorithm) def validate_token(self, token: str) -> tuple[UserId, UserEmail] | None: try: payload = decode(token, self.jwt_options.secret, [self.jwt_options.algorithm]) return UserId(UUID(payload["sub"])), UserEmail(payload["email"]) except (JWTError, ValueError, KeyError): return None def refresh_token(self, token: str) -> str: token_data = self.validate_token(token) if token_data is None: raise UserInvalidCredentialsError("invalid token") return self.generate_token(token_data[0], token_data[1])