1""" 
    2oauthlib.oauth2.rfc8628 
    3~~~~~~~~~~~~~~~~~~~~~~~ 
    4 
    5This module is an implementation of various logic needed 
    6for consuming and providing OAuth 2.0 Device Authorization RFC8628. 
    7""" 
    8from oauthlib.common import add_params_to_uri 
    9from oauthlib.oauth2 import BackendApplicationClient, Client 
    10from oauthlib.oauth2.rfc6749.errors import InsecureTransportError 
    11from oauthlib.oauth2.rfc6749.parameters import prepare_token_request 
    12from oauthlib.oauth2.rfc6749.utils import is_secure_transport, list_to_scope 
    13 
    14 
    15class DeviceClient(Client): 
    16 
    17    """A public client utilizing the device authorization workflow. 
    18 
    19    The client can request an access token using a device code and 
    20    a public client id associated with the device code as defined 
    21    in RFC8628. 
    22 
    23    The device authorization grant type can be used to obtain both 
    24    access tokens and refresh tokens and is intended to be used in 
    25    a scenario where the device being authorized does not have a 
    26    user interface that is suitable for performing authentication. 
    27    """ 
    28 
    29    grant_type = 'urn:ietf:params:oauth:grant-type:device_code' 
    30 
    31    def __init__(self, client_id, **kwargs): 
    32        super().__init__(client_id, **kwargs) 
    33        self.client_secret = kwargs.get('client_secret') 
    34 
    35    def prepare_request_uri(self, uri, scope=None, **kwargs): 
    36        if not is_secure_transport(uri): 
    37            raise InsecureTransportError() 
    38 
    39        scope = self.scope if scope is None else scope 
    40        params = [(('client_id', self.client_id)), (('grant_type', self.grant_type))] 
    41 
    42        if self.client_secret is not None: 
    43            params.append(('client_secret', self.client_secret)) 
    44 
    45        if scope: 
    46            params.append(('scope', list_to_scope(scope))) 
    47 
    48        for k,v in kwargs.items(): 
    49            if v: 
    50                params.append((str(k), v)) 
    51 
    52        return add_params_to_uri(uri, params) 
    53 
    54    def prepare_request_body(self, device_code, body='', scope=None, 
    55                             include_client_id=False, **kwargs): 
    56        """Add device_code to request body 
    57 
    58        The client makes a request to the token endpoint by adding the 
    59        device_code as a parameter using the 
    60        "application/x-www-form-urlencoded" format to the HTTP request 
    61        body. 
    62 
    63        :param body: Existing request body (URL encoded string) to embed parameters 
    64                     into. This may contain extra parameters. Default ''. 
    65        :param scope:   The scope of the access request as described by 
    66                        `Section 3.3`_. 
    67 
    68        :param include_client_id: `True` to send the `client_id` in the 
    69                                  body of the upstream request. This is required 
    70                                  if the client is not authenticating with the 
    71                                  authorization server as described in 
    72                                  `Section 3.2.1`_. False otherwise (default). 
    73        :type include_client_id: Boolean 
    74 
    75        :param kwargs:  Extra credentials to include in the token request. 
    76 
    77        The prepared body will include all provided device_code as well as 
    78        the ``grant_type`` parameter set to 
    79        ``urn:ietf:params:oauth:grant-type:device_code``:: 
    80 
    81            >>> from oauthlib.oauth2 import DeviceClient 
    82            >>> client = DeviceClient('your_id', 'your_code') 
    83            >>> client.prepare_request_body(scope=['hello', 'world']) 
    84            'grant_type=urn:ietf:params:oauth:grant-type:device_code&scope=hello+world' 
    85 
    86        .. _`Section 3.2.1`: https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1 
    87        .. _`Section 3.3`: https://datatracker.ietf.org/doc/html/rfc6749#section-3.3 
    88        .. _`Section 3.4`: https://datatracker.ietf.org/doc/html/rfc8628#section-3.4 
    89        """ 
    90 
    91        kwargs['client_id'] = self.client_id 
    92        kwargs['include_client_id'] = include_client_id 
    93        scope = self.scope if scope is None else scope 
    94        return prepare_token_request(self.grant_type, body=body, device_code=device_code, 
    95                                     scope=scope, **kwargs)