Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/parameters.py: 13%
120 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:22 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:22 +0000
1"""
2oauthlib.oauth2.rfc6749.parameters
3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5This module contains methods related to `Section 4`_ of the OAuth 2 RFC.
7.. _`Section 4`: https://tools.ietf.org/html/rfc6749#section-4
8"""
9import json
10import os
11import time
12import urllib.parse as urlparse
14from oauthlib.common import add_params_to_qs, add_params_to_uri
15from oauthlib.signals import scope_changed
17from .errors import (
18 InsecureTransportError, MismatchingStateError, MissingCodeError,
19 MissingTokenError, MissingTokenTypeError, raise_from_error,
20)
21from .tokens import OAuth2Token
22from .utils import is_secure_transport, list_to_scope, scope_to_list
25def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
26 scope=None, state=None, code_challenge=None, code_challenge_method='plain', **kwargs):
27 """Prepare the authorization grant request URI.
29 The client constructs the request URI by adding the following
30 parameters to the query component of the authorization endpoint URI
31 using the ``application/x-www-form-urlencoded`` format as defined by
32 [`W3C.REC-html401-19991224`_]:
34 :param uri:
35 :param client_id: The client identifier as described in `Section 2.2`_.
36 :param response_type: To indicate which OAuth 2 grant/flow is required,
37 "code" and "token".
38 :param redirect_uri: The client provided URI to redirect back to after
39 authorization as described in `Section 3.1.2`_.
40 :param scope: The scope of the access request as described by
41 `Section 3.3`_.
42 :param state: An opaque value used by the client to maintain
43 state between the request and callback. The authorization
44 server includes this value when redirecting the user-agent
45 back to the client. The parameter SHOULD be used for
46 preventing cross-site request forgery as described in
47 `Section 10.12`_.
48 :param code_challenge: PKCE parameter. A challenge derived from the
49 code_verifier that is sent in the authorization
50 request, to be verified against later.
51 :param code_challenge_method: PKCE parameter. A method that was used to derive the
52 code_challenge. Defaults to "plain" if not present in the request.
53 :param kwargs: Extra arguments to embed in the grant/authorization URL.
55 An example of an authorization code grant authorization URL:
57 .. code-block:: http
59 GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
60 &code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6&code_challenge_method=S256
61 &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
62 Host: server.example.com
64 .. _`W3C.REC-html401-19991224`: https://tools.ietf.org/html/rfc6749#ref-W3C.REC-html401-19991224
65 .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
66 .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
67 .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
68 .. _`section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
69 """
70 if not is_secure_transport(uri):
71 raise InsecureTransportError()
73 params = [(('response_type', response_type)),
74 (('client_id', client_id))]
76 if redirect_uri:
77 params.append(('redirect_uri', redirect_uri))
78 if scope:
79 params.append(('scope', list_to_scope(scope)))
80 if state:
81 params.append(('state', state))
82 if code_challenge is not None:
83 params.append(('code_challenge', code_challenge))
84 params.append(('code_challenge_method', code_challenge_method))
86 for k in kwargs:
87 if kwargs[k]:
88 params.append((str(k), kwargs[k]))
90 return add_params_to_uri(uri, params)
93def prepare_token_request(grant_type, body='', include_client_id=True, code_verifier=None, **kwargs):
94 """Prepare the access token request.
96 The client makes a request to the token endpoint by adding the
97 following parameters using the ``application/x-www-form-urlencoded``
98 format in the HTTP request entity-body:
100 :param grant_type: To indicate grant type being used, i.e. "password",
101 "authorization_code" or "client_credentials".
103 :param body: Existing request body (URL encoded string) to embed parameters
104 into. This may contain extra parameters. Default ''.
106 :param include_client_id: `True` (default) to send the `client_id` in the
107 body of the upstream request. This is required
108 if the client is not authenticating with the
109 authorization server as described in
110 `Section 3.2.1`_.
111 :type include_client_id: Boolean
113 :param client_id: Unicode client identifier. Will only appear if
114 `include_client_id` is True. *
116 :param client_secret: Unicode client secret. Will only appear if set to a
117 value that is not `None`. Invoking this function with
118 an empty string will send an empty `client_secret`
119 value to the server. *
121 :param code: If using authorization_code grant, pass the previously
122 obtained authorization code as the ``code`` argument. *
124 :param redirect_uri: If the "redirect_uri" parameter was included in the
125 authorization request as described in
126 `Section 4.1.1`_, and their values MUST be identical. *
128 :param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
129 authorization request to the token request.
131 :param kwargs: Extra arguments to embed in the request body.
133 Parameters marked with a `*` above are not explicit arguments in the
134 function signature, but are specially documented arguments for items
135 appearing in the generic `**kwargs` keyworded input.
137 An example of an authorization code token request body:
139 .. code-block:: http
141 grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
142 &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
144 .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1
145 """
146 params = [('grant_type', grant_type)]
148 if 'scope' in kwargs:
149 kwargs['scope'] = list_to_scope(kwargs['scope'])
151 # pull the `client_id` out of the kwargs.
152 client_id = kwargs.pop('client_id', None)
153 if include_client_id:
154 if client_id is not None:
155 params.append(('client_id', client_id))
157 # use code_verifier if code_challenge was passed in the authorization request
158 if code_verifier is not None:
159 params.append(('code_verifier', code_verifier))
161 # the kwargs iteration below only supports including boolean truth (truthy)
162 # values, but some servers may require an empty string for `client_secret`
163 client_secret = kwargs.pop('client_secret', None)
164 if client_secret is not None:
165 params.append(('client_secret', client_secret))
167 # this handles: `code`, `redirect_uri`, and other undocumented params
168 for k in kwargs:
169 if kwargs[k]:
170 params.append((str(k), kwargs[k]))
172 return add_params_to_qs(body, params)
175def prepare_token_revocation_request(url, token, token_type_hint="access_token",
176 callback=None, body='', **kwargs):
177 """Prepare a token revocation request.
179 The client constructs the request by including the following parameters
180 using the ``application/x-www-form-urlencoded`` format in the HTTP request
181 entity-body:
183 :param token: REQUIRED. The token that the client wants to get revoked.
185 :param token_type_hint: OPTIONAL. A hint about the type of the token
186 submitted for revocation. Clients MAY pass this
187 parameter in order to help the authorization server
188 to optimize the token lookup. If the server is
189 unable to locate the token using the given hint, it
190 MUST extend its search across all of its supported
191 token types. An authorization server MAY ignore
192 this parameter, particularly if it is able to detect
193 the token type automatically.
195 This specification defines two values for `token_type_hint`:
197 * access_token: An access token as defined in [RFC6749],
198 `Section 1.4`_
200 * refresh_token: A refresh token as defined in [RFC6749],
201 `Section 1.5`_
203 Specific implementations, profiles, and extensions of this
204 specification MAY define other values for this parameter using the
205 registry defined in `Section 4.1.2`_.
207 .. _`Section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4
208 .. _`Section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5
209 .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc7009#section-4.1.2
211 """
212 if not is_secure_transport(url):
213 raise InsecureTransportError()
215 params = [('token', token)]
217 if token_type_hint:
218 params.append(('token_type_hint', token_type_hint))
220 for k in kwargs:
221 if kwargs[k]:
222 params.append((str(k), kwargs[k]))
224 headers = {'Content-Type': 'application/x-www-form-urlencoded'}
226 if callback:
227 params.append(('callback', callback))
228 return add_params_to_uri(url, params), headers, body
229 else:
230 return url, headers, add_params_to_qs(body, params)
233def parse_authorization_code_response(uri, state=None):
234 """Parse authorization grant response URI into a dict.
236 If the resource owner grants the access request, the authorization
237 server issues an authorization code and delivers it to the client by
238 adding the following parameters to the query component of the
239 redirection URI using the ``application/x-www-form-urlencoded`` format:
241 **code**
242 REQUIRED. The authorization code generated by the
243 authorization server. The authorization code MUST expire
244 shortly after it is issued to mitigate the risk of leaks. A
245 maximum authorization code lifetime of 10 minutes is
246 RECOMMENDED. The client MUST NOT use the authorization code
247 more than once. If an authorization code is used more than
248 once, the authorization server MUST deny the request and SHOULD
249 revoke (when possible) all tokens previously issued based on
250 that authorization code. The authorization code is bound to
251 the client identifier and redirection URI.
253 **state**
254 REQUIRED if the "state" parameter was present in the client
255 authorization request. The exact value received from the
256 client.
258 :param uri: The full redirect URL back to the client.
259 :param state: The state parameter from the authorization request.
261 For example, the authorization server redirects the user-agent by
262 sending the following HTTP response:
264 .. code-block:: http
266 HTTP/1.1 302 Found
267 Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
268 &state=xyz
270 """
271 if not is_secure_transport(uri):
272 raise InsecureTransportError()
274 query = urlparse.urlparse(uri).query
275 params = dict(urlparse.parse_qsl(query))
277 if state and params.get('state', None) != state:
278 raise MismatchingStateError()
280 if 'error' in params:
281 raise_from_error(params.get('error'), params)
283 if not 'code' in params:
284 raise MissingCodeError("Missing code parameter in response.")
286 return params
289def parse_implicit_response(uri, state=None, scope=None):
290 """Parse the implicit token response URI into a dict.
292 If the resource owner grants the access request, the authorization
293 server issues an access token and delivers it to the client by adding
294 the following parameters to the fragment component of the redirection
295 URI using the ``application/x-www-form-urlencoded`` format:
297 **access_token**
298 REQUIRED. The access token issued by the authorization server.
300 **token_type**
301 REQUIRED. The type of the token issued as described in
302 Section 7.1. Value is case insensitive.
304 **expires_in**
305 RECOMMENDED. The lifetime in seconds of the access token. For
306 example, the value "3600" denotes that the access token will
307 expire in one hour from the time the response was generated.
308 If omitted, the authorization server SHOULD provide the
309 expiration time via other means or document the default value.
311 **scope**
312 OPTIONAL, if identical to the scope requested by the client,
313 otherwise REQUIRED. The scope of the access token as described
314 by Section 3.3.
316 **state**
317 REQUIRED if the "state" parameter was present in the client
318 authorization request. The exact value received from the
319 client.
321 :param uri:
322 :param state:
323 :param scope:
325 Similar to the authorization code response, but with a full token provided
326 in the URL fragment:
328 .. code-block:: http
330 HTTP/1.1 302 Found
331 Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
332 &state=xyz&token_type=example&expires_in=3600
333 """
334 if not is_secure_transport(uri):
335 raise InsecureTransportError()
337 fragment = urlparse.urlparse(uri).fragment
338 params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True))
340 for key in ('expires_in',):
341 if key in params: # cast things to int
342 params[key] = int(params[key])
344 if 'scope' in params:
345 params['scope'] = scope_to_list(params['scope'])
347 if 'expires_in' in params:
348 params['expires_at'] = time.time() + int(params['expires_in'])
350 if state and params.get('state', None) != state:
351 raise ValueError("Mismatching or missing state in params.")
353 params = OAuth2Token(params, old_scope=scope)
354 validate_token_parameters(params)
355 return params
358def parse_token_response(body, scope=None):
359 """Parse the JSON token response body into a dict.
361 The authorization server issues an access token and optional refresh
362 token, and constructs the response by adding the following parameters
363 to the entity body of the HTTP response with a 200 (OK) status code:
365 access_token
366 REQUIRED. The access token issued by the authorization server.
367 token_type
368 REQUIRED. The type of the token issued as described in
369 `Section 7.1`_. Value is case insensitive.
370 expires_in
371 RECOMMENDED. The lifetime in seconds of the access token. For
372 example, the value "3600" denotes that the access token will
373 expire in one hour from the time the response was generated.
374 If omitted, the authorization server SHOULD provide the
375 expiration time via other means or document the default value.
376 refresh_token
377 OPTIONAL. The refresh token which can be used to obtain new
378 access tokens using the same authorization grant as described
379 in `Section 6`_.
380 scope
381 OPTIONAL, if identical to the scope requested by the client,
382 otherwise REQUIRED. The scope of the access token as described
383 by `Section 3.3`_.
385 The parameters are included in the entity body of the HTTP response
386 using the "application/json" media type as defined by [`RFC4627`_]. The
387 parameters are serialized into a JSON structure by adding each
388 parameter at the highest structure level. Parameter names and string
389 values are included as JSON strings. Numerical values are included
390 as JSON numbers. The order of parameters does not matter and can
391 vary.
393 :param body: The full json encoded response body.
394 :param scope: The scope requested during authorization.
396 For example:
398 .. code-block:: http
400 HTTP/1.1 200 OK
401 Content-Type: application/json
402 Cache-Control: no-store
403 Pragma: no-cache
405 {
406 "access_token":"2YotnFZFEjr1zCsicMWpAA",
407 "token_type":"example",
408 "expires_in":3600,
409 "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
410 "example_parameter":"example_value"
411 }
413 .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
414 .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6
415 .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
416 .. _`RFC4627`: https://tools.ietf.org/html/rfc4627
417 """
418 try:
419 params = json.loads(body)
420 except ValueError:
422 # Fall back to URL-encoded string, to support old implementations,
423 # including (at time of writing) Facebook. See:
424 # https://github.com/oauthlib/oauthlib/issues/267
426 params = dict(urlparse.parse_qsl(body))
427 for key in ('expires_in',):
428 if key in params: # cast things to int
429 params[key] = int(params[key])
431 if 'scope' in params:
432 params['scope'] = scope_to_list(params['scope'])
434 if 'expires_in' in params:
435 if params['expires_in'] is None:
436 params.pop('expires_in')
437 else:
438 params['expires_at'] = time.time() + int(params['expires_in'])
440 params = OAuth2Token(params, old_scope=scope)
441 validate_token_parameters(params)
442 return params
445def validate_token_parameters(params):
446 """Ensures token presence, token type, expiration and scope in params."""
447 if 'error' in params:
448 raise_from_error(params.get('error'), params)
450 if not 'access_token' in params:
451 raise MissingTokenError(description="Missing access token parameter.")
453 if not 'token_type' in params:
454 if os.environ.get('OAUTHLIB_STRICT_TOKEN_TYPE'):
455 raise MissingTokenTypeError()
457 # If the issued access token scope is different from the one requested by
458 # the client, the authorization server MUST include the "scope" response
459 # parameter to inform the client of the actual scope granted.
460 # https://tools.ietf.org/html/rfc6749#section-3.3
461 if params.scope_changed:
462 message = 'Scope has changed from "{old}" to "{new}".'.format(
463 old=params.old_scope, new=params.scope,
464 )
465 scope_changed.send(message=message, old=params.old_scopes, new=params.scopes)
466 if not os.environ.get('OAUTHLIB_RELAX_TOKEN_SCOPE', None):
467 w = Warning(message)
468 w.token = params
469 w.old_scope = params.old_scopes
470 w.new_scope = params.scopes
471 raise w