1# -*- coding: utf-8 -*- 
    2""" 
    3oauthlib.oauth2.rfc6749 
    4~~~~~~~~~~~~~~~~~~~~~~~ 
    5 
    6This module is an implementation of various logic needed 
    7for consuming and providing OAuth 2.0 RFC6749. 
    8""" 
    9import warnings 
    10 
    11from ..parameters import ( 
    12    parse_authorization_code_response, prepare_grant_uri, 
    13    prepare_token_request, 
    14) 
    15from .base import Client 
    16 
    17 
    18class WebApplicationClient(Client): 
    19 
    20    """A client utilizing the authorization code grant workflow. 
    21 
    22    A web application is a confidential client running on a web 
    23    server.  Resource owners access the client via an HTML user 
    24    interface rendered in a user-agent on the device used by the 
    25    resource owner.  The client credentials as well as any access 
    26    token issued to the client are stored on the web server and are 
    27    not exposed to or accessible by the resource owner. 
    28 
    29    The authorization code grant type is used to obtain both access 
    30    tokens and refresh tokens and is optimized for confidential clients. 
    31    As a redirection-based flow, the client must be capable of 
    32    interacting with the resource owner's user-agent (typically a web 
    33    browser) and capable of receiving incoming requests (via redirection) 
    34    from the authorization server. 
    35    """ 
    36 
    37    grant_type = 'authorization_code' 
    38 
    39    def __init__(self, client_id, code=None, **kwargs): 
    40        super().__init__(client_id, **kwargs) 
    41        self.code = code 
    42 
    43    def prepare_request_uri(self, uri, redirect_uri=None, scope=None, 
    44                            state=None, code_challenge=None, code_challenge_method='plain', **kwargs): 
    45        """Prepare the authorization code request URI 
    46 
    47        The client constructs the request URI by adding the following 
    48        parameters to the query component of the authorization endpoint URI 
    49        using the "application/x-www-form-urlencoded" format, per `Appendix B`_: 
    50 
    51        :param redirect_uri:  OPTIONAL. The redirect URI must be an absolute URI 
    52                              and it should have been registered with the OAuth 
    53                              provider prior to use. As described in `Section 3.1.2`_. 
    54 
    55        :param scope:  OPTIONAL. The scope of the access request as described by 
    56                       Section 3.3`_. These may be any string but are commonly 
    57                       URIs or various categories such as ``videos`` or ``documents``. 
    58 
    59        :param state:   RECOMMENDED.  An opaque value used by the client to maintain 
    60                        state between the request and callback.  The authorization 
    61                        server includes this value when redirecting the user-agent back 
    62                        to the client.  The parameter SHOULD be used for preventing 
    63                        cross-site request forgery as described in `Section 10.12`_. 
    64 
    65        :param code_challenge: OPTIONAL. PKCE parameter. REQUIRED if PKCE is enforced. 
    66                        A challenge derived from the code_verifier that is sent in the 
    67                        authorization request, to be verified against later. 
    68 
    69        :param code_challenge_method: OPTIONAL. PKCE parameter. A method that was used to derive code challenge. 
    70                                      Defaults to "plain" if not present in the request. 
    71 
    72        :param kwargs:  Extra arguments to include in the request URI. 
    73 
    74        In addition to supplied parameters, OAuthLib will append the ``client_id`` 
    75        that was provided in the constructor as well as the mandatory ``response_type`` 
    76        argument, set to ``code``:: 
    77 
    78            >>> from oauthlib.oauth2 import WebApplicationClient 
    79            >>> client = WebApplicationClient('your_id') 
    80            >>> client.prepare_request_uri('https://example.com') 
    81            'https://example.com?client_id=your_id&response_type=code' 
    82            >>> client.prepare_request_uri('https://example.com', redirect_uri='https://a.b/callback') 
    83            'https://example.com?client_id=your_id&response_type=code&redirect_uri=https%3A%2F%2Fa.b%2Fcallback' 
    84            >>> client.prepare_request_uri('https://example.com', scope=['profile', 'pictures']) 
    85            'https://example.com?client_id=your_id&response_type=code&scope=profile+pictures' 
    86            >>> client.prepare_request_uri('https://example.com', code_challenge='kjasBS523KdkAILD2k78NdcJSk2k3KHG6') 
    87            'https://example.com?client_id=your_id&response_type=code&code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6' 
    88            >>> client.prepare_request_uri('https://example.com', code_challenge_method='S256') 
    89            'https://example.com?client_id=your_id&response_type=code&code_challenge_method=S256' 
    90            >>> client.prepare_request_uri('https://example.com', foo='bar') 
    91            'https://example.com?client_id=your_id&response_type=code&foo=bar' 
    92 
    93        .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B 
    94        .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2 
    95        .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2 
    96        .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3 
    97        .. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12 
    98        """ 
    99        scope = self.scope if scope is None else scope 
    100        return prepare_grant_uri(uri, self.client_id, 'code', 
    101                                 redirect_uri=redirect_uri, scope=scope, state=state, code_challenge=code_challenge, 
    102                                 code_challenge_method=code_challenge_method, **kwargs) 
    103 
    104    def prepare_request_body(self, code=None, redirect_uri=None, body='', 
    105                             include_client_id=True, code_verifier=None, **kwargs): 
    106        """Prepare the access token request body. 
    107 
    108        The client makes a request to the token endpoint by adding the 
    109        following parameters using the "application/x-www-form-urlencoded" 
    110        format in the HTTP request entity-body: 
    111 
    112        :param code:    REQUIRED. The authorization code received from the 
    113                        authorization server. 
    114 
    115        :param redirect_uri:    REQUIRED, if the "redirect_uri" parameter was included in the 
    116                                authorization request as described in `Section 4.1.1`_, and their 
    117                                values MUST be identical. 
    118 
    119        :param body: Existing request body (URL encoded string) to embed parameters 
    120                     into. This may contain extra parameters. Default ''. 
    121 
    122        :param include_client_id: `True` (default) to send the `client_id` in the 
    123                                  body of the upstream request. This is required 
    124                                  if the client is not authenticating with the 
    125                                  authorization server as described in `Section 3.2.1`_. 
    126        :type include_client_id: Boolean 
    127 
    128        :param code_verifier: OPTIONAL. A cryptographically random string that is used to correlate the 
    129                                        authorization request to the token request. 
    130 
    131        :param kwargs: Extra parameters to include in the token request. 
    132 
    133        In addition OAuthLib will add the ``grant_type`` parameter set to 
    134        ``authorization_code``. 
    135 
    136        If the client type is confidential or the client was issued client 
    137        credentials (or assigned other authentication requirements), the 
    138        client MUST authenticate with the authorization server as described 
    139        in `Section 3.2.1`_:: 
    140 
    141            >>> from oauthlib.oauth2 import WebApplicationClient 
    142            >>> client = WebApplicationClient('your_id') 
    143            >>> client.prepare_request_body(code='sh35ksdf09sf') 
    144            'grant_type=authorization_code&code=sh35ksdf09sf' 
    145            >>> client.prepare_request_body(code_verifier='KB46DCKJ873NCGXK5GD682NHDKK34GR') 
    146            'grant_type=authorization_code&code_verifier=KB46DCKJ873NCGXK5GD682NHDKK34GR' 
    147            >>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar') 
    148            'grant_type=authorization_code&code=sh35ksdf09sf&foo=bar' 
    149 
    150        `Section 3.2.1` also states: 
    151            In the "authorization_code" "grant_type" request to the token 
    152            endpoint, an unauthenticated client MUST send its "client_id" to 
    153            prevent itself from inadvertently accepting a code intended for a 
    154            client with a different "client_id".  This protects the client from 
    155            substitution of the authentication code.  (It provides no additional 
    156            security for the protected resource.) 
    157 
    158        .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1 
    159        .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1 
    160        """ 
    161        code = code or self.code 
    162        if 'client_id' in kwargs: 
    163            warnings.warn("`client_id` has been deprecated in favor of " 
    164                          "`include_client_id`, a boolean value which will " 
    165                          "include the already configured `self.client_id`.", 
    166                          DeprecationWarning) 
    167            if kwargs['client_id'] != self.client_id: 
    168                raise ValueError("`client_id` was supplied as an argument, but " 
    169                                 "it does not match `self.client_id`") 
    170 
    171        kwargs['client_id'] = self.client_id 
    172        kwargs['include_client_id'] = include_client_id 
    173        return prepare_token_request(self.grant_type, code=code, body=body, 
    174                                     redirect_uri=redirect_uri, code_verifier=code_verifier, **kwargs) 
    175 
    176    def parse_request_uri_response(self, uri, state=None): 
    177        """Parse the URI query for code and state. 
    178 
    179        If the resource owner grants the access request, the authorization 
    180        server issues an authorization code and delivers it to the client by 
    181        adding the following parameters to the query component of the 
    182        redirection URI using the "application/x-www-form-urlencoded" format: 
    183 
    184        :param uri: The callback URI that resulted from the user being redirected 
    185                    back from the provider to you, the client. 
    186        :param state: The state provided in the authorization request. 
    187 
    188        **code** 
    189            The authorization code generated by the authorization server. 
    190            The authorization code MUST expire shortly after it is issued 
    191            to mitigate the risk of leaks. A maximum authorization code 
    192            lifetime of 10 minutes is RECOMMENDED. The client MUST NOT 
    193            use the authorization code more than once. If an authorization 
    194            code is used more than once, the authorization server MUST deny 
    195            the request and SHOULD revoke (when possible) all tokens 
    196            previously issued based on that authorization code. 
    197            The authorization code is bound to the client identifier and 
    198            redirection URI. 
    199 
    200        **state** 
    201                If the "state" parameter was present in the authorization request. 
    202 
    203        This method is mainly intended to enforce strict state checking with 
    204        the added benefit of easily extracting parameters from the URI:: 
    205 
    206            >>> from oauthlib.oauth2 import WebApplicationClient 
    207            >>> client = WebApplicationClient('your_id') 
    208            >>> uri = 'https://example.com/callback?code=sdfkjh345&state=sfetw45' 
    209            >>> client.parse_request_uri_response(uri, state='sfetw45') 
    210            {'state': 'sfetw45', 'code': 'sdfkjh345'} 
    211            >>> client.parse_request_uri_response(uri, state='other') 
    212            Traceback (most recent call last): 
    213                File "<stdin>", line 1, in <module> 
    214                File "oauthlib/oauth2/rfc6749/__init__.py", line 357, in parse_request_uri_response 
    215                    back from the provider to you, the client. 
    216                File "oauthlib/oauth2/rfc6749/parameters.py", line 153, in parse_authorization_code_response 
    217                    raise MismatchingStateError() 
    218            oauthlib.oauth2.rfc6749.errors.MismatchingStateError 
    219        """ 
    220        response = parse_authorization_code_response(uri, state=state) 
    221        self.populate_code_attributes(response) 
    222        return response