[docs]classDeviceCodeGrant(OAuth2Grant):""" OAuth 2.0 Device Code grant. Obtain user authorization on devices with limited input capabilities or lack a suitable browser to handle an interactive log in procedure. The user is instructed to review the authorization request on a secondary device, which does have the requisite input and browser capabilities to complete the user interaction. """def__init__(self,token_url:Union[str,URL],device_authorization_url:Union[str,URL],client_id:str,token:Optional[dict]=None,pkce:bool=False,**kwargs,):""" :param token_url: OAuth 2.0 Token URL :param device_authorization_url: OAuth 2.0 Device Authorization URL :param client_id: client identifier :param token: OAuth 2.0 Token :param pkce: use PKCE """super().__init__(token_url,token,**kwargs)self.device_authorization_url=URL(device_authorization_url)self.client_id=client_idself.pkce=PKCE()ifpkceelseNoneasyncdef_fetch_token(self)->Token:time_start=time.time()ifself.pkce:authorization_request=DeviceAuthorizationRequestPKCE(client_id=self.client_id,code_challenge=self.pkce.code_challenge,code_challenge_method=self.pkce.code_challenge_method,**self.kwargs,)else:authorization_request=DeviceAuthorizationRequest(client_id=self.client_id,**self.kwargs)asyncwithself.session.post(url=self.device_authorization_url,data=authorization_request.model_dump(exclude_none=True),)asresponse:response.raise_for_status()device_authorization=DeviceAuthorizationResponse.model_validate(awaitresponse.json())token_request=DeviceAccessTokenRequest(device_code=device_authorization.device_code,client_id=self.client_id,**self.kwargs,)ifself.pkce:token_request.code_verifier=str(self.pkce.code_verifier,"utf-8")_txt=(f"Visit {device_authorization.verification_uri_complete} to authenticate"ifdevice_authorization.verification_uri_completeelsef"Visit {device_authorization.verification_uri} and enter code {device_authorization.user_code} to authenticate.")print(_txt)whilenottime.time()>time_start+device_authorization.expires_in:awaitasyncio.sleep(device_authorization.interval)try:token=awaitself.execute_token_request(token_request)returntokenexceptOAuth2Errorase:ife.response.error=="authorization_pending":passelife.response.error=="slow_down":device_authorization.interval+=5else:raiseeraiseAuthError("The device code has expired.")