codekingpro/portable-devtools
114k
1# -*- coding: utf-8 -*-2"""3oauthlib.oauth2.rfc67494~~~~~~~~~~~~~~~~~~~~~~~5 6This module is an implementation of various logic needed7for consuming and providing OAuth 2.0 RFC6749.8"""9import warnings10 11from ..parameters import (12 parse_authorization_code_response, prepare_grant_uri,13 prepare_token_request,14)15from .base import Client16 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 web23 server. Resource owners access the client via an HTML user24 interface rendered in a user-agent on the device used by the25 resource owner. The client credentials as well as any access26 token issued to the client are stored on the web server and are27 not exposed to or accessible by the resource owner.28 29 The authorization code grant type is used to obtain both access30 tokens and refresh tokens and is optimized for confidential clients.31 As a redirection-based flow, the client must be capable of32 interacting with the resource owner's user-agent (typically a web33 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 = code42 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 URI46 47 The client constructs the request URI by adding the following48 parameters to the query component of the authorization endpoint URI49 using the "application/x-www-form-urlencoded" format, per `Appendix B`_:50 51 :param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI52 and it should have been registered with the OAuth53 provider prior to use. As described in `Section 3.1.2`_.54 55 :param scope: OPTIONAL. The scope of the access request as described by56 Section 3.3`_. These may be any string but are commonly57 URIs or various categories such as ``videos`` or ``documents``.58 59 :param state: RECOMMENDED. An opaque value used by the client to maintain60 state between the request and callback. The authorization61 server includes this value when redirecting the user-agent back62 to the client. The parameter SHOULD be used for preventing63 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 WebApplicationClient79 >>> 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-B94 .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.295 .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.296 .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.397 .. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.1298 """99 scope = self.scope if scope is None else scope100 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 the109 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 the113 authorization server.114 115 :param redirect_uri: REQUIRED, if the "redirect_uri" parameter was included in the116 authorization request as described in `Section 4.1.1`_, and their117 values MUST be identical.118 119 :param body: Existing request body (URL encoded string) to embed parameters120 into. This may contain extra parameters. Default ''.121 122 :param include_client_id: `True` (default) to send the `client_id` in the123 body of the upstream request. This is required124 if the client is not authenticating with the125 authorization server as described in `Section 3.2.1`_.126 :type include_client_id: Boolean127 128 :param code_verifier: OPTIONAL. A cryptographically random string that is used to correlate the129 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 to134 ``authorization_code``.135 136 If the client type is confidential or the client was issued client137 credentials (or assigned other authentication requirements), the138 client MUST authenticate with the authorization server as described139 in `Section 3.2.1`_::140 141 >>> from oauthlib.oauth2 import WebApplicationClient142 >>> 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 token152 endpoint, an unauthenticated client MUST send its "client_id" to153 prevent itself from inadvertently accepting a code intended for a154 client with a different "client_id". This protects the client from155 substitution of the authentication code. (It provides no additional156 security for the protected resource.)157 158 .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1159 .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1160 """161 code = code or self.code162 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_id172 kwargs['include_client_id'] = include_client_id173 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 authorization180 server issues an authorization code and delivers it to the client by181 adding the following parameters to the query component of the182 redirection URI using the "application/x-www-form-urlencoded" format:183 184 :param uri: The callback URI that resulted from the user being redirected185 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 issued191 to mitigate the risk of leaks. A maximum authorization code192 lifetime of 10 minutes is RECOMMENDED. The client MUST NOT193 use the authorization code more than once. If an authorization194 code is used more than once, the authorization server MUST deny195 the request and SHOULD revoke (when possible) all tokens196 previously issued based on that authorization code.197 The authorization code is bound to the client identifier and198 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 with204 the added benefit of easily extracting parameters from the URI::205 206 >>> from oauthlib.oauth2 import WebApplicationClient207 >>> 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_response215 back from the provider to you, the client.216 File "oauthlib/oauth2/rfc6749/parameters.py", line 153, in parse_authorization_code_response217 raise MismatchingStateError()218 oauthlib.oauth2.rfc6749.errors.MismatchingStateError219 """220 response = parse_authorization_code_response(uri, state=state)221 self.populate_code_attributes(response)222 return response223 