import datetime from typing import Any, Annotated from pydantic import ( AwareDatetime, BaseModel, ConfigDict, field_serializer, field_validator, StringConstraints, PositiveInt, ) DUMMY_VAL = "dummy" DUMMY_EXPIRES_IN_SECONDS = 60 * 60 * 12 # 12 hours EXPIRATION_FORMAT = "%Y-%m-%d %H:%M:%S.%f%z" class M2MToken(BaseModel): """Representation of an M2M Token.""" token: str expires_at: AwareDatetime client_credentials_secret_arn: str model_config = ConfigDict( str_strip_whitespace=True, ) @field_validator("token", "client_credentials_secret_arn") @classmethod def check_action_not_empty(cls, v: Any) -> Any: """Validate token is not empty.""" assert v != "", "Empty strings are not allowed." return v @field_serializer("expires_at", mode="plain") @classmethod def expires_at_serializer(cls, expires_at: datetime.datetime) -> str: """Serialize expires_at conforming to `EXPIRATION_FORMAT`.""" return expires_at.strftime(EXPIRATION_FORMAT) def is_expired(self, leeway_seconds: int | None = None) -> bool: """Return True if the M2M Token is expired.""" return self.expires_at <= ( datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=leeway_seconds or 0) ) def create_dummy_m2m_token(client_credentials_secret_arn: str) -> M2MToken: return M2MToken( token=DUMMY_VAL, client_credentials_secret_arn=client_credentials_secret_arn, expires_at=datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=DUMMY_EXPIRES_IN_SECONDS), ) class ClientCredentials(BaseModel): """Representation client credentials to send to AUTH0""" audience: Annotated[str, StringConstraints(min_length=1)] client_id: Annotated[str, StringConstraints(min_length=1)] client_secret: Annotated[str, StringConstraints(min_length=1)] grant_type: Annotated[str, StringConstraints(min_length=1)] model_config = ConfigDict(str_strip_whitespace=True) class OAuthToken(BaseModel): """Representation of an OAuth Token.""" access_token: Annotated[str, StringConstraints(min_length=1)] expires_in: PositiveInt token_type: Annotated[str, StringConstraints(min_length=1)] model_config = ConfigDict(str_strip_whitespace=True) def convert_to_m2m_token(self, client_credentials_secret_arn: str) -> M2MToken: return M2MToken.model_validate( { "client_credentials_secret_arn": client_credentials_secret_arn, "token": self.access_token, "expires_at": datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=self.expires_in), } )