????
Current Path : C:/opt/pgsql/pgAdmin 4/python/Lib/site-packages/authlib/oauth2/rfc8628/ |
Current File : C:/opt/pgsql/pgAdmin 4/python/Lib/site-packages/authlib/oauth2/rfc8628/device_code.py |
import logging from ..rfc6749.errors import ( InvalidRequestError, UnauthorizedClientError, AccessDeniedError, ) from ..rfc6749 import BaseGrant, TokenEndpointMixin from .errors import ( AuthorizationPendingError, ExpiredTokenError, SlowDownError, ) log = logging.getLogger(__name__) DEVICE_CODE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code' class DeviceCodeGrant(BaseGrant, TokenEndpointMixin): """This OAuth 2.0 [RFC6749] protocol extension enables OAuth clients to request user authorization from applications on devices that have limited input capabilities or lack a suitable browser. Such devices include smart TVs, media consoles, picture frames, and printers, which lack an easy input method or a suitable browser required for traditional OAuth interactions. Here is the authorization flow:: +----------+ +----------------+ | |>---(A)-- Client Identifier --->| | | | | | | |<---(B)-- Device Code, ---<| | | | User Code, | | | Device | & Verification URI | | | Client | | | | | [polling] | | | |>---(E)-- Device Code --->| | | | & Client Identifier | | | | | Authorization | | |<---(F)-- Access Token ---<| Server | +----------+ (& Optional Refresh Token) | | v | | : | | (C) User Code & Verification URI | | : | | v | | +----------+ | | | End User | | | | at |<---(D)-- End user reviews --->| | | Browser | authorization request | | +----------+ +----------------+ This DeviceCodeGrant is the implementation of step (E) and (F). (E) While the end user reviews the client's request (step D), the client repeatedly polls the authorization server to find out if the user completed the user authorization step. The client includes the device code and its client identifier. (F) The authorization server validates the device code provided by the client and responds with the access token if the client is granted access, an error if they are denied access, or an indication that the client should continue to poll. """ GRANT_TYPE = DEVICE_CODE_GRANT_TYPE TOKEN_ENDPOINT_AUTH_METHODS = ['client_secret_basic', 'client_secret_post', 'none'] def validate_token_request(self): """After displaying instructions to the user, the client creates an access token request and sends it to the token endpoint with the following parameters: grant_type REQUIRED. Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code". device_code REQUIRED. The device verification code, "device_code" from the device authorization response. client_id REQUIRED if the client is not authenticating with the authorization server as described in Section 3.2.1. of [RFC6749]. The client identifier as described in Section 2.2 of [RFC6749]. For example, the client makes the following HTTPS request:: POST /token HTTP/1.1 Host: server.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code &device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS &client_id=1406020730 """ device_code = self.request.data.get('device_code') if not device_code: raise InvalidRequestError('Missing "device_code" in payload') client = self.authenticate_token_endpoint_client() if not client.check_grant_type(self.GRANT_TYPE): raise UnauthorizedClientError() credential = self.query_device_credential(device_code) if not credential: raise InvalidRequestError('Invalid "device_code" in payload') if credential.get_client_id() != client.get_client_id(): raise UnauthorizedClientError() user = self.validate_device_credential(credential) self.request.user = user self.request.client = client self.request.credential = credential def create_token_response(self): """If the access token request is valid and authorized, the authorization server issues an access token and optional refresh token. """ client = self.request.client scope = self.request.credential.get_scope() token = self.generate_token( user=self.request.user, scope=scope, include_refresh_token=client.check_grant_type('refresh_token'), ) log.debug('Issue token %r to %r', token, client) self.save_token(token) self.execute_hook('process_token', token=token) return 200, token, self.TOKEN_RESPONSE_HEADER def validate_device_credential(self, credential): if credential.is_expired(): raise ExpiredTokenError() user_code = credential.get_user_code() user_grant = self.query_user_grant(user_code) if user_grant is not None: user, approved = user_grant if not approved: raise AccessDeniedError() return user if self.should_slow_down(credential): raise SlowDownError() raise AuthorizationPendingError() def query_device_credential(self, device_code): """Get device credential from previously savings via ``DeviceAuthorizationEndpoint``. Developers MUST implement it in subclass:: def query_device_credential(self, device_code): return DeviceCredential.get(device_code) :param device_code: a string represent the code. :return: DeviceCredential instance """ raise NotImplementedError() def query_user_grant(self, user_code): """Get user and grant via the given user code. Developers MUST implement it in subclass:: def query_user_grant(self, user_code): # e.g. we saved user grant info in redis data = redis.get('oauth_user_grant:' + user_code) if not data: return None user_id, allowed = data.split() user = User.get(user_id) return user, bool(allowed) Note, user grant information is saved by verification endpoint. """ raise NotImplementedError() def should_slow_down(self, credential): """The authorization request is still pending and polling should continue, but the interval MUST be increased by 5 seconds for this and all subsequent requests. """ raise NotImplementedError()