Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
parameters.py529 linesDownload Raw Back to rfc6749
1"""2oauthlib.oauth2.rfc6749.parameters3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~4 5This module contains methods related to `Section 4`_ of the OAuth 2 RFC.6 7.. _`Section 4`: https://tools.ietf.org/html/rfc6749#section-48"""9import json10import os11import time12import urllib.parse as urlparse13 14from oauthlib.common import add_params_to_qs, add_params_to_uri15from oauthlib.signals import scope_changed16 17from .errors import (18    InsecureTransportError, MismatchingStateError, MissingCodeError,19    MissingTokenError, MissingTokenTypeError, raise_from_error,20)21from .tokens import OAuth2Token22from .utils import is_secure_transport, list_to_scope, scope_to_list23 24 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.28 29    The client constructs the request URI by adding the following30    parameters to the query component of the authorization endpoint URI31    using the ``application/x-www-form-urlencoded`` format as defined by32    [`W3C.REC-html401-19991224`_]:33 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 after39                         authorization as described in `Section 3.1.2`_.40    :param scope: The scope of the access request as described by41                  `Section 3.3`_.42    :param state: An opaque value used by the client to maintain43                  state between the request and callback.  The authorization44                  server includes this value when redirecting the user-agent45                  back to the client.  The parameter SHOULD be used for46                  preventing cross-site request forgery as described in47                  `Section 10.12`_.48    :param code_challenge: PKCE parameter. A challenge derived from the49                           code_verifier that is sent in the authorization50                           request, to be verified against later.51    :param code_challenge_method: PKCE parameter. A method that was used to derive the52                                  code_challenge. Defaults to "plain" if not present in the request.53    :param kwargs: Extra arguments to embed in the grant/authorization URL.54 55    An example of an authorization code grant authorization URL:56 57    .. code-block:: http58 59        GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz60            &code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6&code_challenge_method=S25661            &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.162        Host: server.example.com63 64    .. _`W3C.REC-html401-19991224`: https://tools.ietf.org/html/rfc6749#ref-W3C.REC-html401-1999122465    .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.266    .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.267    .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.368    .. _`section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.1269    """70    if not is_secure_transport(uri):71        raise InsecureTransportError()72 73    params = [(('response_type', response_type)),74              (('client_id', client_id))]75 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))85 86    for k in kwargs:87        if kwargs[k]:88            params.append((str(k), kwargs[k]))89 90    return add_params_to_uri(uri, params)91 92 93def prepare_token_request(grant_type, body='', include_client_id=True, code_verifier=None, **kwargs):94    """Prepare the access token request.95 96    The client makes a request to the token endpoint by adding the97    following parameters using the ``application/x-www-form-urlencoded``98    format in the HTTP request entity-body:99 100    :param grant_type: To indicate grant type being used, i.e. "password",101                       "authorization_code" or "client_credentials".102 103    :param body: Existing request body (URL encoded string) to embed parameters104                 into. This may contain extra parameters. Default ''.105 106    :param include_client_id: `True` (default) to send the `client_id` in the107                              body of the upstream request. This is required108                              if the client is not authenticating with the109                              authorization server as described in110                              `Section 3.2.1`_.111    :type include_client_id: Boolean112 113    :param client_id: Unicode client identifier. Will only appear if114                      `include_client_id` is True. *115 116    :param client_secret: Unicode client secret. Will only appear if set to a117                          value that is not `None`. Invoking this function with118                          an empty string will send an empty `client_secret`119                          value to the server. *120 121    :param code: If using authorization_code grant, pass the previously122                 obtained authorization code as the ``code`` argument. *123 124    :param redirect_uri: If the "redirect_uri" parameter was included in the125                         authorization request as described in126                         `Section 4.1.1`_, and their values MUST be identical. *127 128    :param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the129                          authorization request to the token request.130 131    :param kwargs: Extra arguments to embed in the request body.132 133    Parameters marked with a `*` above are not explicit arguments in the134    function signature, but are specially documented arguments for items135    appearing in the generic `**kwargs` keyworded input.136 137    An example of an authorization code token request body:138 139    .. code-block:: http140 141        grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA142        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb143 144    .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1145    """146    params = [('grant_type', grant_type)]147 148    if 'scope' in kwargs:149        kwargs['scope'] = list_to_scope(kwargs['scope'])150 151    # pull the `client_id` out of the kwargs.152    client_id = kwargs.pop('client_id', None)153    if include_client_id and client_id is not None:154        params.append(('client_id', client_id))155 156    # use code_verifier if code_challenge was passed in the authorization request157    if code_verifier is not None:158        params.append(('code_verifier', code_verifier))159 160    # the kwargs iteration below only supports including boolean truth (truthy)161    # values, but some servers may require an empty string for `client_secret`162    client_secret = kwargs.pop('client_secret', None)163    if client_secret is not None:164        params.append(('client_secret', client_secret))165 166    # this handles: `code`, `redirect_uri`, and other undocumented params167    for k in kwargs:168        if kwargs[k]:169            params.append((str(k), kwargs[k]))170 171    return add_params_to_qs(body, params)172 173 174def prepare_token_revocation_request(url, token, token_type_hint="access_token",175        callback=None, body='', **kwargs):176    """Prepare a token revocation request.177 178    The client constructs the request by including the following parameters179    using the ``application/x-www-form-urlencoded`` format in the HTTP request180    entity-body:181 182    :param token: REQUIRED.  The token that the client wants to get revoked.183 184    :param token_type_hint: OPTIONAL.  A hint about the type of the token185                            submitted for revocation. Clients MAY pass this186                            parameter in order to help the authorization server187                            to optimize the token lookup.  If the server is188                            unable to locate the token using the given hint, it189                            MUST extend its search across all of its supported190                            token types.  An authorization server MAY ignore191                            this parameter, particularly if it is able to detect192                            the token type automatically.193 194    This specification defines two values for `token_type_hint`:195 196        * access_token: An access token as defined in [RFC6749],197             `Section 1.4`_198 199        * refresh_token: A refresh token as defined in [RFC6749],200             `Section 1.5`_201 202        Specific implementations, profiles, and extensions of this203        specification MAY define other values for this parameter using the204        registry defined in `Section 4.1.2`_.205 206    .. _`Section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4207    .. _`Section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5208    .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc7009#section-4.1.2209 210    """211    if not is_secure_transport(url):212        raise InsecureTransportError()213 214    params = [('token', token)]215 216    if token_type_hint:217        params.append(('token_type_hint', token_type_hint))218 219    for k in kwargs:220        if kwargs[k]:221            params.append((str(k), kwargs[k]))222 223    headers = {'Content-Type': 'application/x-www-form-urlencoded'}224 225    if callback:226        params.append(('callback', callback))227        return add_params_to_uri(url, params), headers, body228    else:229        return url, headers, add_params_to_qs(body, params)230 231 232def parse_authorization_code_response(uri, state=None):233    """Parse authorization grant response URI into a dict.234 235    If the resource owner grants the access request, the authorization236    server issues an authorization code and delivers it to the client by237    adding the following parameters to the query component of the238    redirection URI using the ``application/x-www-form-urlencoded`` format:239 240    **code**241            REQUIRED.  The authorization code generated by the242            authorization server.  The authorization code MUST expire243            shortly after it is issued to mitigate the risk of leaks.  A244            maximum authorization code lifetime of 10 minutes is245            RECOMMENDED.  The client MUST NOT use the authorization code246            more than once.  If an authorization code is used more than247            once, the authorization server MUST deny the request and SHOULD248            revoke (when possible) all tokens previously issued based on249            that authorization code.  The authorization code is bound to250            the client identifier and redirection URI.251 252    **state**253            REQUIRED if the "state" parameter was present in the client254            authorization request.  The exact value received from the255            client.256 257    :param uri: The full redirect URL back to the client.258    :param state: The state parameter from the authorization request.259 260    For example, the authorization server redirects the user-agent by261    sending the following HTTP response:262 263    .. code-block:: http264 265        HTTP/1.1 302 Found266        Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA267                &state=xyz268 269    """270    if not is_secure_transport(uri):271        raise InsecureTransportError()272 273    query = urlparse.urlparse(uri).query274    params = dict(urlparse.parse_qsl(query))275 276    if state and params.get('state') != state:277        raise MismatchingStateError()278 279    if 'error' in params:280        raise_from_error(params.get('error'), params)281 282    if 'code' not in params:283        raise MissingCodeError("Missing code parameter in response.")284 285    return params286 287 288def parse_implicit_response(uri, state=None, scope=None):289    """Parse the implicit token response URI into a dict.290 291    If the resource owner grants the access request, the authorization292    server issues an access token and delivers it to the client by adding293    the following parameters to the fragment component of the redirection294    URI using the ``application/x-www-form-urlencoded`` format:295 296    **access_token**297            REQUIRED.  The access token issued by the authorization server.298 299    **token_type**300            REQUIRED.  The type of the token issued as described in301            Section 7.1.  Value is case insensitive.302 303    **expires_in**304            RECOMMENDED.  The lifetime in seconds of the access token.  For305            example, the value "3600" denotes that the access token will306            expire in one hour from the time the response was generated.307            If omitted, the authorization server SHOULD provide the308            expiration time via other means or document the default value.309 310    **scope**311            OPTIONAL, if identical to the scope requested by the client,312            otherwise REQUIRED.  The scope of the access token as described313            by Section 3.3.314 315    **state**316            REQUIRED if the "state" parameter was present in the client317            authorization request.  The exact value received from the318            client.319 320    :param uri:321    :param state:322    :param scope:323 324    Similar to the authorization code response, but with a full token provided325    in the URL fragment:326 327    .. code-block:: http328 329        HTTP/1.1 302 Found330        Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA331                &state=xyz&token_type=example&expires_in=3600332    """333    if not is_secure_transport(uri):334        raise InsecureTransportError()335 336    fragment = urlparse.urlparse(uri).fragment337    params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True))338 339    if 'scope' in params:340        params['scope'] = scope_to_list(params['scope'])341 342    vin, vat, v_at = parse_expires(params)343    if vin:344        params['expires_in'] = vin345    elif 'expires_in' in params:346        params.pop('expires_in')347    if vat:348        params['expires_at'] = vat349    elif 'expires_at' in params:350        params.pop('expires_at')351 352    if state and params.get('state') != state:353        raise ValueError("Mismatching or missing state in params.")354 355    params = OAuth2Token(params, old_scope=scope)356    validate_token_parameters(params)357    return params358 359 360def parse_token_response(body, scope=None):361    """Parse the JSON token response body into a dict.362 363    The authorization server issues an access token and optional refresh364    token, and constructs the response by adding the following parameters365    to the entity body of the HTTP response with a 200 (OK) status code:366 367    access_token368            REQUIRED.  The access token issued by the authorization server.369    token_type370            REQUIRED.  The type of the token issued as described in371            `Section 7.1`_.  Value is case insensitive.372    expires_in373            RECOMMENDED.  The lifetime in seconds of the access token.  For374            example, the value "3600" denotes that the access token will375            expire in one hour from the time the response was generated.376            If omitted, the authorization server SHOULD provide the377            expiration time via other means or document the default value.378    refresh_token379            OPTIONAL.  The refresh token which can be used to obtain new380            access tokens using the same authorization grant as described381            in `Section 6`_.382    scope383            OPTIONAL, if identical to the scope requested by the client,384            otherwise REQUIRED.  The scope of the access token as described385            by `Section 3.3`_.386 387    The parameters are included in the entity body of the HTTP response388    using the "application/json" media type as defined by [`RFC4627`_].  The389    parameters are serialized into a JSON structure by adding each390    parameter at the highest structure level.  Parameter names and string391    values are included as JSON strings.  Numerical values are included392    as JSON numbers.  The order of parameters does not matter and can393    vary.394 395    :param body: The full json encoded response body.396    :param scope: The scope requested during authorization.397 398    For example:399 400    .. code-block:: http401 402        HTTP/1.1 200 OK403        Content-Type: application/json404        Cache-Control: no-store405        Pragma: no-cache406 407        {408            "access_token":"2YotnFZFEjr1zCsicMWpAA",409            "token_type":"example",410            "expires_in":3600,411            "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",412            "example_parameter":"example_value"413        }414 415    .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1416    .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6417    .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3418    .. _`RFC4627`: https://tools.ietf.org/html/rfc4627419    """420    try:421        params = json.loads(body)422    except ValueError:423 424        # Fall back to URL-encoded string, to support old implementations,425        # including (at time of writing) Facebook. See:426        #   https://github.com/oauthlib/oauthlib/issues/267427 428        params = dict(urlparse.parse_qsl(body))429 430    if 'scope' in params:431        params['scope'] = scope_to_list(params['scope'])432 433    vin, vat, v_at = parse_expires(params)434    if vin:435        params['expires_in'] = vin436    elif 'expires_in' in params:437        params.pop('expires_in')438    if vat:439        params['expires_at'] = vat440    elif 'expires_at' in params:441        params.pop('expires_at')442 443    params = OAuth2Token(params, old_scope=scope)444    validate_token_parameters(params)445    return params446 447 448def validate_token_parameters(params):449    """Ensures token presence, token type, expiration and scope in params."""450    if 'error' in params:451        raise_from_error(params.get('error'), params)452 453    if 'access_token' not in params:454        raise MissingTokenError(description="Missing access token parameter.")455 456    if 'token_type' not in params and os.environ.get('OAUTHLIB_STRICT_TOKEN_TYPE'):457        raise MissingTokenTypeError()458 459    # If the issued access token scope is different from the one requested by460    # the client, the authorization server MUST include the "scope" response461    # parameter to inform the client of the actual scope granted.462    # https://tools.ietf.org/html/rfc6749#section-3.3463    if params.scope_changed:464        message = 'Scope has changed from "{old}" to "{new}".'.format(465            old=params.old_scope, new=params.scope,466        )467        scope_changed.send(message=message, old=params.old_scopes, new=params.scopes)468        if not os.environ.get('OAUTHLIB_RELAX_TOKEN_SCOPE', None):469            w = Warning(message)470            w.token = params471            w.old_scope = params.old_scopes472            w.new_scope = params.scopes473            raise w474 475def parse_expires(params):476    """Parse `expires_in`, `expires_at` fields from params477 478    Parse following these rules:479    - `expires_in` must be either integer, float or None. If a float, it is converted into an integer.480    - `expires_at` is not in specification so it does its best to:481      - convert into a int, else482      - convert into a float, else483      - reuse the same type as-is (usually string)484    - `_expires_at` is a special internal value returned to be always an `int`, based485    either on the presence of `expires_at`, or reuse the current time plus486    `expires_in`. This is typically used to validate token expiry.487 488    :param params: Dict with expires_in and expires_at optionally set489    :return: Tuple of `expires_in`, `expires_at`, and `_expires_at`. None if not set.490    """491    expires_in = None492    expires_at = None493    _expires_at = None494 495    if 'expires_in' in params:496        if isinstance(params.get('expires_in'), int):497            expires_in = params.get('expires_in')498        elif isinstance(params.get('expires_in'), float):499            expires_in = int(params.get('expires_in'))500        elif isinstance(params.get('expires_in'), str):501            try:502                # Attempt to convert to int503                expires_in = int(params.get('expires_in'))504            except ValueError:505                raise ValueError("expires_in must be an int")506        elif params.get('expires_in') is not None:507            raise ValueError("expires_in must be an int")508 509    if 'expires_at' in params:510        if isinstance(params.get('expires_at'), (float, int)):511            expires_at = params.get('expires_at')512            _expires_at = expires_at513        elif isinstance(params.get('expires_at'), str):514            try:515                # Attempt to convert to int first, then float if int fails516                expires_at = int(params.get('expires_at'))517                _expires_at = expires_at518            except ValueError:519                try:520                    expires_at = float(params.get('expires_at'))521                    _expires_at = expires_at522                except ValueError:523                    # no change from str524                    expires_at = params.get('expires_at')525    if _expires_at is None and expires_in:526        expires_at = round(time.time()) + expires_in527        _expires_at = expires_at528    return expires_in, expires_at, _expires_at529 
codekingpro/portable-devtools · Team Ai