[docs]classOAuth2Grant:""" Generic OAuth 2.0 Grant class. """def__init__(self,token_url:Union[str,URL],token:Optional[dict]=None,**kwargs):""" :param token_url: OAuth 2.0 Token URL :param token: OAuth 2.0 Token :param kwargs: extra arguments used in token request """self.token_url=URL(token_url)self.token=Token.model_validate(token)iftokenelseNoneself.lock=asyncio.Lock()self.session=aiohttp.ClientSession()self.kwargs=kwargsasyncdef__aenter__(self):returnselfasyncdef__aexit__(self,exc_type,exc_value,exc_tb):awaitself.close()
[docs]asyncdefclose(self):""" Close the Grant object and its associated resources. """awaitself.session.close()
[docs]asyncdefensure_active_token(self):""" Ensure that the stored access token is still active. If this is not the case, the token will be refreshed. """asyncwithself.lock:ifself.token.is_expired():awaitself.refresh_token()
[docs]asyncdefprepare_request(self,headers:Optional[LooseHeaders]):""" Prepare the HTTP request by adding the OAuth 2.0 access token to the Authorization header. :param headers: HTTP request headers :return: updated HTTP request headers """headers=dict(headers)ifheaderselse{}asyncwithself.lock:ifnotself.token:# request initial tokenawaitself.fetch_token()awaitself.ensure_active_token()headers["Authorization"]=f"Bearer {self.token.access_token}"returnheaders
[docs]asyncdeffetch_token(self):""" Fetch an OAuth 2.0 token from the token endpoint and store it for subsequent use. """self.token=awaitself._fetch_token()
@abstractmethodasyncdef_fetch_token(self)->Token:""" Fetch an OAuth 2.0 token from the token endpoint. :return: OAuth 2.0 Token """...
[docs]asyncdefrefresh_token(self):""" Obtain a new access token using the refresh token grant and store it for subsequent use. """access_token_request=RefreshTokenAccessTokenRequest(refresh_token=self.token.refresh_token,**self.kwargs,)self.token=awaitself.execute_token_request(access_token_request)
[docs]asyncdefexecute_token_request(self,data:AccessTokenRequest)->Token:""" Execute a token request with the provided data. :param data: token request data :return: OAuth 2.0 Token :raises OAuth2Error: if the token request fails :raises aiohttp.ClientResponseError: if the HTTP error cannot be parsed as an OAuth 2.0 error response """asyncwithself.session.post(url=self.token_url,data=data.model_dump(exclude_none=True),)asresponse:ifnotresponse.ok:try:raiseOAuth2Error(ErrorResponse.model_validate(awaitresponse.json()))exceptValidationError:response.raise_for_status()returnToken.model_validate(awaitresponse.json())