def request(self, method, url, data=None, headers=None, withhold_token=False, **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token and not withhold_token:
            log.debug("Invoking %d protected resource request hooks.", len(self.compliance_hook["protected_request"]))
            for hook in self.compliance_hook["protected_request"]:
                log.debug("Invoking hook %s.", hook)
                url, headers, data = hook(url, headers, data)

            log.debug("Adding token %s to request.", self.token)
            try:
                url, headers, data = self._client.add_token(url, http_method=method, body=data, headers=headers)
            # Attempt to retrieve and save new access token if expired
            except TokenExpiredError:
                if self.auto_refresh_url:
                    log.debug("Auto refresh is set, attempting to refresh at %s.", self.auto_refresh_url)
                    token = self.refresh_token(self.auto_refresh_url, **kwargs)
                    if self.token_updater:
                        log.debug("Updating token to %s using %s.", token, self.token_updater)
                        self.token_updater(token)
                        url, headers, data = self._client.add_token(url, http_method=method, body=data, headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        log.debug("Requesting url %s using method %s.", url, method)
        log.debug("Supplying headers %s and data %s", headers, data)
        log.debug("Passing through key word arguments %s.", kwargs)
        return super(OAuth2Session, self).request(method, url, headers=headers, data=data, **kwargs)
    def request(self, method, url, data=None, headers=None, **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token:
            log.debug('Invoking %d protected resource request hooks.',
                      len(self.compliance_hook['protected_request']))
            for hook in self.compliance_hook['protected_request']:
                url, headers, data = hook(url, headers, data)

            try:
                url, headers, data = self._client.add_token(url,
                        http_method=method, body=data, headers=headers)
            # Attempt to retrieve and save new access token if expired

            except TokenExpiredError:
                if self.auto_refresh_url:
                    token = self.refresh_token(self.auto_refresh_url,auth=self.refresher.auth,**kwargs)
                    if self.token_updater:
                        self.token_updater(token)
                        url, headers, data = self._client.add_token(url,
                                http_method=method, body=data, headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        return super(OAuth2Session, self).request(method, url, 
                headers=headers, data=data, **kwargs)
    def refresh_token(self, token_url, refresh_token=None, body='', auth=None,
                      timeout=None, headers=None, verify=True, **kwargs):
        """Fetch a new access token using a refresh token.

        :param token_url: The token endpoint, must be HTTPS.
        :param refresh_token: The refresh_token to use.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not token_url:
            raise ValueError('No token endpoint set for auto_refresh.')

        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        # Need to nullify token to prevent it from being added to the request
        refresh_token = refresh_token or self.token.get('refresh_token')
        self.token = {}

        log.debug('Adding auto refresh key word arguments %s.',
                  self.auto_refresh_kwargs)
        kwargs.update(self.auto_refresh_kwargs)
        body = self._client.prepare_refresh_body(body=body,
                refresh_token=refresh_token, scope=self.scope, **kwargs)
        log.debug('Prepared refresh token request body %s', body)

        if headers is None:
            headers = {
                'Accept': 'application/json',
                'Content-Type': (
                    'application/x-www-form-urlencoded;charset=UTF-8'
                ),
            }

        r = self.post(token_url, data=dict(urldecode(body)), auth=auth,
                      timeout=timeout, headers=headers, verify=verify)
        log.debug('Request to refresh token completed with status %s.',
                  r.status_code)
        log.debug('Response headers were %s and content %s.',
                  r.headers, r.text)
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['refresh_token_response']))

        r.raise_for_status()

        for hook in self.compliance_hook['refresh_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
        if not 'refresh_token' in self.token:
            log.debug('No new refresh token given. Re-using old.')
            self.token['refresh_token'] = refresh_token
        return self.token
Beispiel #4
0
    def refresh_token(self,
                      token_url,
                      refresh_token=None,
                      body='',
                      auth=None,
                      timeout=None,
                      verify=True,
                      **kwargs):
        """Fetch a new access token using a refresh token.

        :param token_url: The token endpoint, must be HTTPS.
        :param refresh_token: The refresh_token to use.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not token_url:
            raise ValueError('No token endpoint set for auto_refresh.')

        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        # Need to nullify token to prevent it from being added to the request
        refresh_token = refresh_token or self.token.get('refresh_token')
        self.token = {}

        log.debug('Adding auto refresh key word arguments %s.',
                  self.auto_refresh_kwargs)
        kwargs.update(self.auto_refresh_kwargs)
        body = self._client.prepare_refresh_body(body=body,
                                                 refresh_token=refresh_token,
                                                 scope=self.scope,
                                                 **kwargs)
        log.debug('Prepared refresh token request body %s', body)
        r = self.post(token_url,
                      data=dict(urldecode(body)),
                      auth=auth,
                      timeout=timeout,
                      verify=verify)
        log.debug('Request to refresh token completed with status %s.',
                  r.status_code)
        log.debug('Response headers were %s and content %s.', r.headers,
                  r.text)
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['refresh_token_response']))
        for hook in self.compliance_hook['refresh_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        self.token = self._client.parse_request_body_response(r.text,
                                                              scope=self.scope)
        if not 'refresh_token' in self.token:
            log.debug('No new refresh token given. Re-using old.')
            self.token['refresh_token'] = refresh_token
        return self.token
    def refresh_token(
        self, token_url, refresh_token=None, body="", auth=None, timeout=None, headers=None, verify=True, **kwargs
    ):
        """Fetch a new access token using a refresh token.

        :param token_url: The token endpoint, must be HTTPS.
        :param refresh_token: The refresh_token to use.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not token_url:
            raise ValueError("No token endpoint set for auto_refresh.")

        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        refresh_token = refresh_token or self.token.get("refresh_token")

        log.debug("Adding auto refresh key word arguments %s.", self.auto_refresh_kwargs)
        kwargs.update(self.auto_refresh_kwargs)
        body = self._client.prepare_refresh_body(body=body, refresh_token=refresh_token, scope=self.scope, **kwargs)
        log.debug("Prepared refresh token request body %s", body)

        if headers is None:
            headers = {
                "Accept": "application/json",
                "Content-Type": ("application/x-www-form-urlencoded;charset=UTF-8"),
            }

        r = self.post(
            token_url,
            data=dict(urldecode(body)),
            auth=auth,
            timeout=timeout,
            headers=headers,
            verify=verify,
            withhold_token=True,
        )
        log.debug("Request to refresh token completed with status %s.", r.status_code)
        log.debug("Response headers were %s and content %s.", r.headers, r.text)
        log.debug("Invoking %d token response hooks.", len(self.compliance_hook["refresh_token_response"]))
        for hook in self.compliance_hook["refresh_token_response"]:
            log.debug("Invoking hook %s.", hook)
            r = hook(r)

        self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
        if not "refresh_token" in self.token:
            log.debug("No new refresh token given. Re-using old.")
            self.token["refresh_token"] = refresh_token
        return self.token
Beispiel #6
0
    def __call__(self, r):
        """Append an OAuth 2 token to the request.

        Note that currently HTTPS is required for all requests. There may be
        a token type that allows for plain HTTP in the future and then this
        should be updated to allow plain HTTP on a white list basis.
        """
        if not is_secure_transport(r.url):
            raise InsecureTransportError()
        r.url, r.headers, r.body = self._client.add_token(r.url,
                http_method=r.method, body=r.body, headers=r.headers)
        return r
    def __call__(self, r):
        """Append an OAuth 2 token to the request.

        Note that currently HTTPS is required for all requests. There may be
        a token type that allows for plain HTTP in the future and then this
        should be updated to allow plain HTTP on a white list basis.
        """
        if not is_secure_transport(r.url):
            raise InsecureTransportError()
        r.url, r.headers, r.body = self._client.add_token(r.url,
                http_method=r.method, body=r.body, headers=r.headers)
        return r
    def request(self,
                method,
                url,
                data=None,
                headers=None,
                withhold_token=False,
                **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token and not withhold_token:
            log.debug('Invoking %d protected resource request hooks.',
                      len(self.compliance_hook['protected_request']))
            for hook in self.compliance_hook['protected_request']:
                log.debug('Invoking hook %s.', hook)
                url, headers, data = hook(url, headers, data)

            log.debug('Adding token %s to request.', self.token)
            try:
                url, headers, data = self._client.add_token(url,
                                                            http_method=method,
                                                            body=data,
                                                            headers=headers)
            # Attempt to retrieve and save new access token if expired
            except TokenExpiredError:
                if self.auto_refresh_url:
                    log.debug(
                        'Auto refresh is set, attempting to refresh at %s.',
                        self.auto_refresh_url)

                    token = self.refresh_token(self.auto_refresh_url, **kwargs)
                    if self.token_updater:
                        log.debug('Updating token to %s using %s.', token,
                                  self.token_updater)
                        self.token_updater(token)
                        url, headers, data = self._client.add_token(
                            url,
                            http_method=method,
                            body=data,
                            headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        log.debug('Requesting url %s using method %s.', url, method)
        log.debug('Supplying headers %s and data %s', headers, data)
        log.debug('Passing through key word arguments %s.', kwargs)
        return super(OAuth2Session, self).request(method,
                                                  url,
                                                  headers=headers,
                                                  data=data,
                                                  **kwargs)
Beispiel #9
0
    def request(self, method, url, data=None, headers=None, withhold_token=False,
                client_id=None, client_secret=None, **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token and not withhold_token:
            log.debug('Invoking %d protected resource request hooks.',
                      len(self.compliance_hook['protected_request']))
            for hook in self.compliance_hook['protected_request']:
                log.debug('Invoking hook %s.', hook)
                url, headers, data = hook(url, headers, data)

            log.debug('Adding token %s to request.', self.token)
            try:
                url, headers, data = self._client.add_token(url,
                        http_method=method, body=data, headers=headers)
            # Attempt to retrieve and save new access token if expired
            except TokenExpiredError:
                if self.auto_refresh_url:
                    log.debug('Auto refresh is set, attempting to refresh at %s.',
                              self.auto_refresh_url)

                    # We mustn't pass auth twice.
                    auth = kwargs.pop('auth', None)
                    if client_id and client_secret and (auth is None):
                        log.debug('Encoding client_id "%s" with client_secret as Basic auth credentials.', client_id)
                        auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
                    token = self.refresh_token(
                        self.auto_refresh_url, auth=auth, **kwargs
                    )
                    if self.token_updater:
                        log.debug('Updating token to %s using %s.',
                                  token, self.token_updater)
                        self.token_updater(token)
                        url, headers, data = self._client.add_token(url,
                                http_method=method, body=data, headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        log.debug('Requesting url %s using method %s.', url, method)
        log.debug('Supplying headers %s and data %s', headers, data)
        log.debug('Passing through key word arguments %s.', kwargs)
        return super(OAuth2Session, self).request(method, url,
                headers=headers, data=data, **kwargs)
Beispiel #10
0
    async def fetch_token(self,
                          token_url,
                          code=None,
                          authorization_response=None,
                          body="",
                          auth=None,
                          username=None,
                          password=None,
                          method="POST",
                          force_querystring=False,
                          timeout=None,
                          headers=None,
                          verify_ssl=True,
                          proxies=None,
                          include_client_id=None,
                          client_id=None,
                          client_secret=None,
                          **kwargs):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        `token_from_fragment` instead of `fetch_token`.

        The current implementation enforces the RFC guidelines.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by `requests`.
        :param username: Username required by LegacyApplicationClients to appear
                         in the request body.
        :param password: Password required by LegacyApplicationClients to appear
                         in the request body.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param force_querystring: If True, force the request body to be sent
            in the querystring instead.
        :param timeout: Timeout of the request in seconds.
        :param headers: Dict to default request headers with.
        :param verify: Verify SSL certificate.
        :param proxies: The `proxies` argument is passed onto `requests`.
        :param include_client_id: Should the request body include the
                                  `client_id` parameter. Default is `None`,
                                  which will attempt to autodetect. This can be
                                  forced to always include (True) or never
                                  include (False).
        :param client_secret: The `client_secret` paired to the `client_id`.
                              This is generally required unless provided in the
                              `auth` tuple. If the value is `None`, it will be
                              omitted from the request, however if the value is
                              an empty string, an empty string will be sent.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            log.debug('-- response %s', authorization_response)
            self._client.parse_request_uri_response(
                str(authorization_response), state=self._state)
            code = self._client.code
            log.debug('--code %s', code)
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError("Please supply either code or "
                                 "authorization_response parameters.")

        # Earlier versions of this library build an HTTPBasicAuth header out of
        # `username` and `password`. The RFC states, however these attributes
        # must be in the request body and not the header.
        # If an upstream server is not spec compliant and requires them to
        # appear as an Authorization header, supply an explicit `auth` header
        # to this function.
        # This check will allow for empty strings, but not `None`.
        #
        # Refernences
        # 4.3.2 - Resource Owner Password Credentials Grant
        #         https://tools.ietf.org/html/rfc6749#section-4.3.2

        if isinstance(self._client, LegacyApplicationClient):
            if username is None:
                raise ValueError("`LegacyApplicationClient` requires both the "
                                 "`username` and `password` parameters.")
            if password is None:
                raise ValueError(
                    "The required paramter `username` was supplied, "
                    "but `password` was not.")

        # merge username and password into kwargs for `prepare_request_body`
        if username is not None:
            kwargs["username"] = username
        if password is not None:
            kwargs["password"] = password

        # is an auth explicitly supplied?
        if auth is not None:
            # if we're dealing with the default of `include_client_id` (None):
            # we will assume the `auth` argument is for an RFC compliant server
            # and we should not send the `client_id` in the body.
            # This approach allows us to still force the client_id by submitting
            # `include_client_id=True` along with an `auth` object.
            if include_client_id is None:
                include_client_id = False

        # otherwise we may need to create an auth header
        else:
            # since we don't have an auth header, we MAY need to create one
            # it is possible that we want to send the `client_id` in the body
            # if so, `include_client_id` should be set to True
            # otherwise, we will generate an auth header
            if include_client_id is not True:
                client_id = self.client_id
            if client_id:
                log.debug(
                    'Encoding `client_id` "%s" with `client_secret` '
                    "as Basic auth credentials.",
                    client_id,
                )
                client_secret = client_secret if client_secret is not None else ""
                auth = aiohttp.BasicAuth(login=client_id,
                                         password=client_secret)

        if include_client_id:
            # this was pulled out of the params
            # it needs to be passed into prepare_request_body
            if client_secret is not None:
                kwargs["client_secret"] = client_secret

        body = self._client.prepare_request_body(
            code=code,
            body=body,
            redirect_uri=self.redirect_uri,
            include_client_id=include_client_id,
            **kwargs)

        headers = headers or {
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
        }
        self.token = {}
        request_kwargs = {}
        if method.upper() == "POST":
            request_kwargs["params" if force_querystring else "data"] = dict(
                urldecode(body))
        elif method.upper() == "GET":
            request_kwargs["params"] = dict(urldecode(body))
        else:
            raise ValueError("The method kwarg must be POST or GET.")

        async with self.request(method=method,
                                url=token_url,
                                timeout=timeout,
                                headers=headers,
                                auth=auth,
                                verify_ssl=verify_ssl,
                                proxy=proxies,
                                **request_kwargs) as resp:
            log.debug("Request to fetch token completed with status %s.",
                      resp.status)
            log.debug("Request headers were %s", headers)
            log.debug("Request body was %s", body)
            text = await resp.text()

            log.debug("Response headers were %s and content %s.", resp.headers,
                      text)
            (resp, ) = self._invoke_hooks("access_token_response", resp)

        self._client.parse_request_body_response(text, scope=self.scope)
        self.token = self._client.token
        log.debug("Obtained token %s.", self.token)
        return self.token
Beispiel #11
0
    def fetch_token(self, token_url, code=None, authorization_response=None,
            body='', auth=None, username=None, password=None, method='POST',
            timeout=None, headers=None, verify=True, proxies=None, **kwargs):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        token_from_fragment instead of fetch_token.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param username: Username used by LegacyApplicationClients.
        :param password: Password used by LegacyApplicationClients.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param headers: Dict to default request headers with.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            self._client.parse_request_uri_response(authorization_response,
                    state=self._state)
            code = self._client.code
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError('Please supply either code or '
                                 'authorization_response parameters.')


        body = self._client.prepare_request_body(code=code, body=body,
                redirect_uri=self.redirect_uri, username=username,
                password=password, **kwargs)

        client_id = kwargs.get('client_id', '')
        if auth is None:
            if client_id:
                log.debug('Encoding client_id "%s" with client_secret as Basic auth credentials.', client_id)
                client_secret = kwargs.get('client_secret', '')
                client_secret = client_secret if client_secret is not None else ''
                auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
            elif username:
                if password is None:
                    raise ValueError('Username was supplied, but not password.')
                log.debug('Encoding username, password as Basic auth credentials.')
                auth = requests.auth.HTTPBasicAuth(username, password)

        headers = headers or {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
        self.token = {}
        if method.upper() == 'POST':
            r = self.post(token_url, json=dict(urldecode(body)),
                timeout=timeout, headers=headers, auth=auth,
                verify=verify, proxies=proxies)
            log.debug('Prepared fetch token request body %s', body)
        elif method.upper() == 'GET':
            # if method is not 'POST', switch body to querystring and GET
            r = self.get(token_url, params=dict(urldecode(body)),
                timeout=timeout, headers=headers, auth=auth,
                verify=verify, proxies=proxies)
            log.debug('Prepared fetch token request querystring %s', body)
        else:
            raise ValueError('The method kwarg must be POST or GET.')

        log.debug('Request to fetch token completed with status %s.',
                  r.status_code)
        log.debug('Request headers were %s', r.request.headers)
        log.debug('Request body was %s', r.request.body)
        log.debug('Response headers were %s and content %s.',
                  r.headers, r.text)
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['access_token_response']))
        for hook in self.compliance_hook['access_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        self._client.parse_request_body_response(r.text, scope=self.scope)
        self.token = self._client.token
        log.debug('Obtained token %s.', self.token)
        return self.token
Beispiel #12
0
    async def fetch_token(
        self,
        token_url,
        code=None,
        authorization_response=None,
        body="",
        auth=None,
        username=None,
        password=None,
        method="POST",
        timeout=None,
        headers=None,
        verify_ssl=True,
        proxy=None,
        **kwargs,
    ):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        token_from_fragment instead of fetch_token.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by aiohttp.
        :param username: Username used by LegacyApplicationClients.
        :param password: Password used by LegacyApplicationClients.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param headers: Dict to default request headers with.
        :param timeout: Timeout of the request in seconds.
        :param verify_ssl: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            self._client.parse_request_uri_response(authorization_response,
                                                    state=self._state)
            code = self._client.code
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError("Please supply either code or "
                                 "authorization_response parameters.")

        body = self._client.prepare_request_body(
            code=code,
            body=body,
            redirect_uri=self.redirect_uri,
            username=username,
            password=password,
            **kwargs,
        )
        client_id = kwargs.get("client_id", "")
        if auth is None:
            if client_id:
                log.debug(
                    'Encoding client_id "%s" with client_secret as Basic auth credentials.',
                    client_id,
                )
                client_secret = kwargs.get("client_secret", "")
                client_secret = client_secret if client_secret is not None else ""
                auth = aiohttp.BasicAuth(client_id, client_secret)
            elif username:
                if password is None:
                    raise ValueError(
                        "Username was supplied, but not password.")

                log.debug(
                    "Encoding username, password as Basic auth credentials.")
                auth = aiohttp.BasicAuth(username, password)
        headers = headers or {
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
        }
        self.token = {}
        if method.upper() == "POST":
            req = self.post(
                token_url,
                data=dict(urldecode(body)),
                timeout=timeout,
                headers=headers,
                auth=auth,
                verify_ssl=verify_ssl,
                proxy=proxy,
            )
            log.debug("Prepared fetch token request body %s", body)
        elif method.upper() == "GET":
            # if method is not 'POST', switch body to querystring and GET
            req = self.get(
                token_url,
                params=dict(urldecode(body)),
                timeout=timeout,
                headers=headers,
                auth=auth,
                verify_ssl=verify_ssl,
                proxy=proxy,
            )
            log.debug("Prepared fetch token request querystring %s", body)
        else:
            raise ValueError("The method kwarg must be POST or GET.")

        async with req as resp:
            text = await resp.text()
            log.debug("Request to fetch token completed with status %s.",
                      resp.status)
            log.debug("Request headers were %s", resp.request_info.headers)
            log.debug("Response headers were %s and content %s.", resp.headers,
                      text)
            log.debug(
                "Invoking %s token response hooks.",
                len(self.compliance_hook["access_token_response"]),
            )
            for hook in self.compliance_hook["access_token_response"]:
                log.debug("Invoking hook %s.", hook)
                resp = hook(resp)
            self._client.parse_request_body_response(text, scope=self.scope)
            self.token = self._client.token
            log.debug("Obtained token %s.", self.token)
        return self.token
    def refresh_token(self,
                      token_url,
                      refresh_token=None,
                      body='',
                      auth=None,
                      timeout=None,
                      headers=None,
                      verify=True,
                      proxies=None,
                      **kwargs):
        """Fetch a new access token using a refresh token.

        :param token_url: The token endpoint, must be HTTPS.
        :param refresh_token: The refresh_token to use.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not token_url:
            raise ValueError('No token endpoint set for auto_refresh.')

        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        refresh_token = refresh_token or self.token.get('refresh_token')

        log.debug('Adding auto refresh key word arguments %s.',
                  self.auto_refresh_kwargs)
        kwargs.update(self.auto_refresh_kwargs)

        auth = auth or kwargs.get('auth')
        client_id = kwargs.get('client_id')
        client_secret = kwargs.get('client_secret', '')

        if client_id and (auth is None):
            log.debug(
                'Encoding client_id "%s" with client_secret as Basic auth credentials.',
                client_id)
            auth = requests.auth.HTTPBasicAuth(client_id, client_secret)

        body = self._client.prepare_refresh_body(body=body,
                                                 refresh_token=refresh_token,
                                                 scope=self.scope,
                                                 **kwargs)
        log.debug('Prepared refresh token request body %s', body)

        if headers is None:
            headers = {
                'Accept':
                'application/json',
                'Content-Type':
                ('application/x-www-form-urlencoded;charset=UTF-8'),
            }

        r = self.post(token_url,
                      data=dict(urldecode(body)),
                      auth=auth,
                      timeout=timeout,
                      headers=headers,
                      verify=verify,
                      withhold_token=True,
                      proxies=proxies)
        log.debug('Request to refresh token completed with status %s.',
                  r.status_code)
        log.debug('Response headers were %s and content %s.', r.headers,
                  r.text)
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['refresh_token_response']))
        for hook in self.compliance_hook['refresh_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        self.token = self._client.parse_request_body_response(r.text,
                                                              scope=self.scope)
        if not 'refresh_token' in self.token:
            log.debug('No new refresh token given. Re-using old.')
            self.token['refresh_token'] = refresh_token
        return self.token
Beispiel #14
0
    def request(self,
                method,
                url,
                data=None,
                headers=None,
                withhold_token=False,
                client_id=None,
                client_secret=None,
                files=None,
                **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token and not withhold_token:
            log.debug(
                "Invoking %d protected resource request hooks.",
                len(self.compliance_hook["protected_request"]),
            )
            for hook in self.compliance_hook["protected_request"]:
                log.debug("Invoking hook %s.", hook)
                url, headers, data = hook(url, headers, data)

            log.debug("Adding token %s to request.", self.token)
            try:
                url, headers, data = self._client.add_token(url,
                                                            http_method=method,
                                                            body=data,
                                                            headers=headers)
            # Attempt to retrieve and save new access token if expired
            except TokenExpiredError:
                if self.auto_refresh_url:
                    log.debug(
                        "Auto refresh is set, attempting to refresh at %s.",
                        self.auto_refresh_url,
                    )

                    # We mustn't pass auth twice.
                    auth = kwargs.pop("auth", None)
                    if client_id and client_secret and (auth is None):
                        log.debug(
                            'Encoding client_id "%s" with client_secret as Basic auth credentials.',
                            client_id,
                        )
                        auth = requests.auth.HTTPBasicAuth(
                            client_id, client_secret)
                    token = self.refresh_token(self.auto_refresh_url,
                                               auth=auth,
                                               **kwargs)
                    if self.token_updater:
                        log.debug("Updating token to %s using %s.", token,
                                  self.token_updater)
                        self.token_updater(token)
                        url, headers, data = self._client.add_token(
                            url,
                            http_method=method,
                            body=data,
                            headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        log.debug("Requesting url %s using method %s.", url, method)
        log.debug("Supplying headers %s and data %s", headers, data)
        log.debug("Passing through key word arguments %s.", kwargs)
        return super(OAuth2Session, self).request(method,
                                                  url,
                                                  headers=headers,
                                                  data=data,
                                                  files=files,
                                                  **kwargs)
Beispiel #15
0
    async def refresh_token(self,
                            token_url,
                            refresh_token=None,
                            body="",
                            auth=None,
                            timeout=None,
                            headers=None,
                            verify_ssl=True,
                            proxies=None,
                            **kwargs):
        """Fetch a new access token using a refresh token.

        :param token_url: The token endpoint, must be HTTPS.
        :param refresh_token: The refresh_token to use.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by `requests`.
        :param timeout: Timeout of the request in seconds.
        :param headers: A dict of headers to be used by `requests`.
        :param verify: Verify SSL certificate.
        :param proxies: The `proxies` argument will be passed to `requests`.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not token_url:
            raise ValueError("No token endpoint set for auto_refresh.")

        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        refresh_token = refresh_token or self.token.get("refresh_token")

        log.debug("Adding auto refresh key word arguments %s.",
                  self.auto_refresh_kwargs)

        kwargs.update(self.auto_refresh_kwargs)
        body = self._client.prepare_refresh_body(body=body,
                                                 refresh_token=refresh_token,
                                                 scope=self.scope,
                                                 **kwargs)
        log.debug("Prepared refresh token request body %s", body)

        if headers is None:
            headers = {
                "Accept":
                "application/json",
                "Content-Type":
                ("application/x-www-form-urlencoded;charset=UTF-8"),
            }

        async with self.post(
                token_url,
                data=dict(urldecode(body)),
                auth=auth,
                timeout=timeout,
                headers=headers,
                verify_ssl=verify_ssl,
                withhold_token=True,
                # proxy=proxies,
        ) as resp:
            log.debug("Request to refresh token completed with status %s.",
                      resp.status)
            text = await resp.text()
            log.debug("Response headers were %s and content %s.", resp.headers,
                      text)
            (resp, ) = self._invoke_hooks("refresh_token_response", resp)

        self.token = self._client.parse_request_body_response(text,
                                                              scope=self.scope)
        if "refresh_token" not in self.token:
            log.debug("No new refresh token given. Re-using old.")
            self.token["refresh_token"] = refresh_token
        return self.token
    def fetch_token(
        self,
        token_url,
        code=None,
        authorization_response=None,
        body="",
        auth=None,
        username=None,
        password=None,
        method="POST",
        timeout=None,
        headers=None,
        verify=True,
        proxies=None,
        include_client_id=None,
        client_secret=None,
        **kwargs
    ):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        `token_from_fragment` instead of `fetch_token`.

        The current implementation enforces the RFC guidelines.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by `requests`.
        :param username: Username required by LegacyApplicationClients to appear
                         in the request body.
        :param password: Password required by LegacyApplicationClients to appear
                         in the request body.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param timeout: Timeout of the request in seconds.
        :param headers: Dict to default request headers with.
        :param verify: Verify SSL certificate.
        :param proxies: The `proxies` argument is passed onto `requests`.
        :param include_client_id: Should the request body include the
                                  `client_id` parameter. Default is `None`,
                                  which will attempt to autodetect. This can be
                                  forced to always include (True) or never
                                  include (False).
        :param client_secret: The `client_secret` paired to the `client_id`.
                              This is generally required unless provided in the
                              `auth` tuple. If the value is `None`, it will be
                              omitted from the request, however if the value is
                              an empty string, an empty string will be sent.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            self._client.parse_request_uri_response(
                authorization_response, state=self._state
            )
            code = self._client.code
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError(
                    "Please supply either code or " "authorization_response parameters."
                )

        # Earlier versions of this library build an HTTPBasicAuth header out of
        # `username` and `password`. The RFC states, however these attributes
        # must be in the request body and not the header.
        # If an upstream server is not spec compliant and requires them to
        # appear as an Authorization header, supply an explicit `auth` header
        # to this function.
        # This check will allow for empty strings, but not `None`.
        #
        # Refernences
        # 4.3.2 - Resource Owner Password Credentials Grant
        #         https://tools.ietf.org/html/rfc6749#section-4.3.2

        if isinstance(self._client, LegacyApplicationClient):
            if username is None:
                raise ValueError(
                    "`LegacyApplicationClient` requires both the "
                    "`username` and `password` parameters."
                )
            if password is None:
                raise ValueError(
                    "The required paramter `username` was supplied, "
                    "but `password` was not."
                )

        # merge username and password into kwargs for `prepare_request_body`
        if username is not None:
            kwargs["username"] = username
        if password is not None:
            kwargs["password"] = password

        # is an auth explicitly supplied?
        if auth is not None:
            # if we're dealing with the default of `include_client_id` (None):
            # we will assume the `auth` argument is for an RFC compliant server
            # and we should not send the `client_id` in the body.
            # This approach allows us to still force the client_id by submitting
            # `include_client_id=True` along with an `auth` object.
            if include_client_id is None:
                include_client_id = False

        # otherwise we may need to create an auth header
        else:
            # since we don't have an auth header, we MAY need to create one
            # it is possible that we want to send the `client_id` in the body
            # if so, `include_client_id` should be set to True
            # otherwise, we will generate an auth header
            if include_client_id is not True:
                client_id = self.client_id
                if client_id:
                    log.debug(
                        'Encoding `client_id` "%s" with `client_secret` '
                        "as Basic auth credentials.",
                        client_id,
                    )
                    client_secret = client_secret if client_secret is not None else ""
                    auth = requests.auth.HTTPBasicAuth(client_id, client_secret)

        if include_client_id:
            # this was pulled out of the params
            # it needs to be passed into prepare_request_body
            if client_secret is not None:
                kwargs["client_secret"] = client_secret

        body = self._client.prepare_request_body(
            code=code,
            body=body,
            redirect_uri=self.redirect_uri,
            include_client_id=include_client_id,
            **kwargs
        )

        headers = headers or {
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
        }
        self.token = {}
        if method.upper() == "POST":
            r = self.post(
                token_url,
                data=dict(urldecode(body)),
                timeout=timeout,
                headers=headers,
                auth=auth,
                verify=verify,
                proxies=proxies,
            )
            log.debug("Prepared fetch token request body %s", body)
        elif method.upper() == "GET":
            # if method is not 'POST', switch body to querystring and GET
            r = self.get(
                token_url,
                params=dict(urldecode(body)),
                timeout=timeout,
                headers=headers,
                auth=auth,
                verify=verify,
                proxies=proxies,
            )
            log.debug("Prepared fetch token request querystring %s", body)
        else:
            raise ValueError("The method kwarg must be POST or GET.")

        log.debug("Request to fetch token completed with status %s.", r.status_code)
        log.debug("Request headers were %s", r.request.headers)
        log.debug("Request body was %s", r.request.body)
        log.debug("Response headers were %s and content %s.", r.headers, r.text)
        log.debug(
            "Invoking %d token response hooks.",
            len(self.compliance_hook["access_token_response"]),
        )
        for hook in self.compliance_hook["access_token_response"]:
            log.debug("Invoking hook %s.", hook)
            r = hook(r)

        self._client.parse_request_body_response(r.text, scope=self.scope)
        self.token = self._client.token
        log.debug("Obtained token %s.", self.token)
        return self.token
Beispiel #17
0
    async def refresh_token(self,
                            token_url,
                            refresh_token=None,
                            body='',
                            auth=None,
                            timeout=None,
                            headers=None,
                            proxy=None,
                            **kwargs):
        """Fetch a new access token using a refresh token.

        :param token_url: The token endpoint, must be HTTPS.
        :param refresh_token: The refresh_token to use.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by `requests`.
        :param timeout: Timeout of the request in seconds.
        :param headers: A dict of headers to be used by `requests`.
        :param proxy: The `proxy` argument will be passed to `requests`.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not token_url:
            raise ValueError('No token endpoint set for auto_refresh.')

        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        refresh_token = refresh_token or self.token.get('refresh_token')

        log.debug('Adding auto refresh key word arguments %s.',
                  self.auto_refresh_kwargs)
        kwargs.update(self.auto_refresh_kwargs)
        body = self._client.prepare_refresh_body(body=body,
                                                 refresh_token=refresh_token,
                                                 scope=self.scope,
                                                 **kwargs)
        log.debug('Prepared refresh token request body %s', body)

        if headers is None:
            headers = {
                'Accept':
                'application/json',
                'Content-Type':
                ('application/x-www-form-urlencoded;charset=UTF-8'),
            }

        r = await self.request("POST",
                               token_url,
                               data=dict(urldecode(body)),
                               auth=auth,
                               timeout=timeout,
                               headers=headers,
                               withhold_token=True,
                               proxy=proxy)
        log.debug('Request to refresh token completed with status %s.',
                  r.status)
        log.debug('Response headers were %s and content %s.', r.headers, await
                  r.text())
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['refresh_token_response']))
        for hook in self.compliance_hook['refresh_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        self.token = self._client.parse_request_body_response(await r.text(),
                                                              scope=self.scope)
        if 'refresh_token' not in self.token:
            log.debug('No new refresh token given. Re-using old.')
            self.token['refresh_token'] = refresh_token
        return self.token
    def request(self,
                method,
                url,
                data=None,
                headers=None,
                withhold_token=False,
                client_id=None,
                client_secret=None,
                **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token and not withhold_token:
            log.debug('Invoking %d protected resource request hooks.',
                      len(self.compliance_hook['protected_request']))
            for hook in self.compliance_hook['protected_request']:
                log.debug('Invoking hook %s.', hook)
                url, headers, data = hook(url, headers, data)

            log.debug('Adding token %s to request.', self.token)
            try:
                url, headers, data = self._client.add_token(url,
                                                            http_method=method,
                                                            body=data,
                                                            headers=headers)
            # Attempt to retrieve and save new access token if expired
            except TokenExpiredError:
                if self.auto_refresh_url:
                    log.debug(
                        'Auto refresh is set, attempting to refresh at %s.',
                        self.auto_refresh_url)

                    # Someday this code can be eliminated, we should be able to
                    # simply pass **self.auto_refresh_kwargs to
                    # `refresh_token()` what follows is to maintain
                    # compatibility

                    # Start with kwargs explicitly requested for refresh.
                    refresh_kwargs = self.auto_refresh_kwargs.copy()

                    if kwargs.get('auth', None):
                        # If user supplied `auth` to `request()` warn them to
                        # instead use `auto_refresh_kwargs`. But honor their
                        # intent for now.
                        warnings.warn('auth argument supplied. Please specify '
                                      'token refresh authentication in '
                                      'auto_refresh_kwargs.')
                        refresh_kwargs['auth'] = kwargs.pop('auth')
                    elif client_id:
                        # If no auth was provided, but `client_id` and
                        # `client_secret` were, warn the user and create an
                        # HTTP Basic auth header. It is preferred if the user
                        # instead place an auth param into `auto_refresh_kwargs`
                        warnings.warn('client_id argument supplied, Please '
                                      'specify token refresh authentication in'
                                      'auto_refresh_kwargs')
                        log.debug(
                            'Encoding client_id "%s" with client_secret as Basic auth credentials.',
                            client_id)
                        if not client_secret:
                            warnings.warn('client_id provided, but '
                                          'client_secret missing')
                            client_secret = ''
                        refresh_kwargs['auth'] = \
                            requests.auth.HTTPBasicAuth(client_id, client_secret)

                    # End of compat. code.

                    token = self.refresh_token(self.auto_refresh_url,
                                               **refresh_kwargs)
                    if self.token_updater:
                        log.debug('Updating token to %s using %s.', token,
                                  self.token_updater)
                        self.token_updater(token)
                        url, headers, data = self._client.add_token(
                            url,
                            http_method=method,
                            body=data,
                            headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        log.debug('Requesting url %s using method %s.', url, method)
        log.debug('Supplying headers %s and data %s', headers, data)
        log.debug('Passing through key word arguments %s.', kwargs)
        return super(OAuth2Session, self).request(method,
                                                  url,
                                                  headers=headers,
                                                  data=data,
                                                  **kwargs)
Beispiel #19
0
    async def _request(self,
                       method,
                       url,
                       *,
                       data=None,
                       headers=None,
                       withhold_token=False,
                       client_id=None,
                       client_secret=None,
                       **kwargs):
        """Intercept all requests and add the OAuth 2 token if present."""
        if not is_secure_transport(url):
            raise InsecureTransportError()
        if self.token and not withhold_token:

            url, headers, data = self._invoke_hooks("protected_request", url,
                                                    headers, data)
            log.debug("Adding token %s to request.", self.token)
            try:
                url, headers, data = self._client.add_token(url,
                                                            http_method=method,
                                                            body=data,
                                                            headers=headers)
            # Attempt to retrieve and save new access token if expired
            except TokenExpiredError:
                if self.auto_refresh_url:
                    log.debug(
                        "Auto refresh is set, attempting to refresh at %s.",
                        self.auto_refresh_url,
                    )

                    # We mustn't pass auth twice.
                    auth = kwargs.pop("auth", None)
                    if client_id and client_secret and (auth is None):
                        log.debug(
                            'Encoding client_id "%s" with client_secret as Basic auth credentials.',
                            client_id,
                        )
                        auth = aiohttp.BasicAuth(login=client_id,
                                                 password=client_secret)
                    token = await self.refresh_token(self.auto_refresh_url,
                                                     auth=auth,
                                                     **kwargs)
                    if self.token_updater:
                        log.debug("Updating token to %s using %s.", token,
                                  self.token_updater)
                        await self.token_updater(token)
                        url, headers, data = self._client.add_token(
                            url,
                            http_method=method,
                            body=data,
                            headers=headers)
                    else:
                        raise TokenUpdated(token)
                else:
                    raise

        log.debug("Requesting url %s using method %s.", url, method)
        log.debug("Supplying headers %s and data %s", headers, data)
        log.debug("Passing through key word arguments %s.", kwargs)
        return await super()._request(method,
                                      url,
                                      headers=headers,
                                      data=data,
                                      **kwargs)
Beispiel #20
0
    def fetch_token(self, token_url, code=None, authorization_response=None,
            body='', auth=None, username=None, password=None, method='POST',
            timeout=None, headers=None, verify=True, proxies=None,
            include_client_id=None, client_secret=None, **kwargs):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        `token_from_fragment` instead of `fetch_token`.

        The current implementation enforces the RFC guidelines.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by `requests`.
        :param username: Username required by LegacyApplicationClients to appear
                         in the request body.
        :param password: Password required by LegacyApplicationClients to appear
                         in the request body.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param timeout: Timeout of the request in seconds.
        :param headers: Dict to default request headers with.
        :param verify: Verify SSL certificate.
        :param proxies: The `proxies` argument is passed onto `requests`.
        :param include_client_id: Should the request body include the
                                  `client_id` parameter. Default is `None`,
                                  which will attempt to autodetect. This can be
                                  forced to always include (True) or never
                                  include (False).
        :param client_secret: The `client_secret` paired to the `client_id`.
                              This is generally required unless provided in the
                              `auth` tuple. If the value is `None`, it will be
                              omitted from the request, however if the value is
                              an empty string, an empty string will be sent.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            self._client.parse_request_uri_response(authorization_response,
                    state=self._state)
            code = self._client.code
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError('Please supply either code or '
                                 'authorization_response parameters.')

        # Earlier versions of this library build an HTTPBasicAuth header out of
        # `username` and `password`. The RFC states, however these attributes
        # must be in the request body and not the header.
        # If an upstream server is not spec compliant and requires them to
        # appear as an Authorization header, supply an explicit `auth` header
        # to this function.
        # This check will allow for empty strings, but not `None`.
        #
        # Refernences
        # 4.3.2 - Resource Owner Password Credentials Grant
        #         https://tools.ietf.org/html/rfc6749#section-4.3.2

        if isinstance(self._client, LegacyApplicationClient):
            if username is None:
                raise ValueError('`LegacyApplicationClient` requires both the '
                                 '`username` and `password` parameters.')
            if password is None:
                raise ValueError('The required paramter `username` was supplied, '
                                 'but `password` was not.')

        # merge username and password into kwargs for `prepare_request_body`
        if username is not None:
            kwargs['username'] = username
        if password is not None:
            kwargs['password'] = password

        # is an auth explicitly supplied?
        if auth is not None:
            # if we're dealing with the default of `include_client_id` (None):
            # we will assume the `auth` argument is for an RFC compliant server
            # and we should not send the `client_id` in the body.
            # This approach allows us to still force the client_id by submitting
            # `include_client_id=True` along with an `auth` object.
            if include_client_id is None:
                include_client_id = False

        # otherwise we may need to create an auth header
        else:
            # since we don't have an auth header, we MAY need to create one
            # it is possible that we want to send the `client_id` in the body
            # if so, `include_client_id` should be set to True
            # otherwise, we will generate an auth header
            if include_client_id is not True:
                client_id = self.client_id
                if client_id:
                    log.debug('Encoding `client_id` "%s" with `client_secret` '
                              'as Basic auth credentials.', client_id)
                    client_secret = client_secret if client_secret is not None else ''
                    auth = requests.auth.HTTPBasicAuth(client_id, client_secret)

        if include_client_id:
            # this was pulled out of the params
            # it needs to be passed into prepare_request_body
            if client_secret is not None:
                kwargs['client_secret'] = client_secret

        body = self._client.prepare_request_body(code=code, body=body,
                redirect_uri=self.redirect_uri,
                include_client_id=include_client_id, **kwargs)

        headers = headers or {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        }
        self.token = {}
        if method.upper() == 'POST':
            r = self.post(token_url, data=dict(urldecode(body)),
                timeout=timeout, headers=headers, auth=auth,
                verify=verify, proxies=proxies)
            log.debug('Prepared fetch token request body %s', body)
        elif method.upper() == 'GET':
            # if method is not 'POST', switch body to querystring and GET
            r = self.get(token_url, params=dict(urldecode(body)),
                timeout=timeout, headers=headers, auth=auth,
                verify=verify, proxies=proxies)
            log.debug('Prepared fetch token request querystring %s', body)
        else:
            raise ValueError('The method kwarg must be POST or GET.')

        log.debug('Request to fetch token completed with status %s.',
                  r.status_code)
        log.debug('Request headers were %s', r.request.headers)
        log.debug('Request body was %s', r.request.body)
        log.debug('Response headers were %s and content %s.',
                  r.headers, r.text)
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['access_token_response']))
        for hook in self.compliance_hook['access_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        self._client.parse_request_body_response(r.text, scope=self.scope)
        self.token = self._client.token
        log.debug('Obtained token %s.', self.token)
        return self.token
Beispiel #21
0
    def fetch_token(self, token_url, code=None, authorization_response=None,
            body='', auth=None, username=None, password=None, method='POST',
            timeout=None, headers=None, verify=True, **kwargs):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        token_from_fragment instead of fetch_token.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param username: Username used by LegacyApplicationClients.
        :param password: Password used by LegacyApplicationClients.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param headers: Dict to default request headers with.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            self._client.parse_request_uri_response(authorization_response,
                    state=self._state)
            code = self._client.code
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError('Please supply either code or '
                                 'authorization_code parameters.')


        body = self._client.prepare_request_body(code=code, body=body,
                redirect_uri=self.redirect_uri, username=username,
                password=password, **kwargs)

        headers = headers or {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        }
        if method.upper() == 'POST':
            r = self.post(token_url, data=dict(urldecode(body)),
                timeout=timeout, headers=headers, auth=auth,
                verify=verify)
            log.debug('Prepared fetch token request body %s', body)
        elif method.upper() == 'GET':
            # if method is not 'POST', switch body to querystring and GET
            r = self.get(token_url, params=dict(urldecode(body)),
                timeout=timeout, headers=headers, auth=auth,
                verify=verify)
            log.debug('Prepared fetch token request querystring %s', body)
        else:
            raise ValueError('The method kwarg must be POST or GET.')

        log.debug('Request to fetch token completed with status %s.',
                  r.status_code)
        log.debug('Request headers were %s', r.request.headers)
        log.debug('Request body was %s', r.request.body)
        log.debug('Response headers were %s and content %s.',
                  r.headers, r.text)
        log.debug('Invoking %d token response hooks.',
                  len(self.compliance_hook['access_token_response']))
        for hook in self.compliance_hook['access_token_response']:
            log.debug('Invoking hook %s.', hook)
            r = hook(r)

        r.raise_for_status()

        self._client.parse_request_body_response(r.text, scope=self.scope)
        self.token = self._client.token
        log.debug('Obtained token %s.', self.token)
        return self.token
    def fetch_token(
        self,
        token_url,
        code=None,
        authorization_response=None,
        body="",
        auth=None,
        username=None,
        password=None,
        method="POST",
        timeout=None,
        headers=None,
        verify=True,
        proxies=None,
        **kwargs
    ):
        """Generic method for fetching an access token from the token endpoint.

        If you are using the MobileApplicationClient you will want to use
        token_from_fragment instead of fetch_token.

        :param token_url: Token endpoint URL, must use HTTPS.
        :param code: Authorization code (used by WebApplicationClients).
        :param authorization_response: Authorization response URL, the callback
                                       URL of the request back to you. Used by
                                       WebApplicationClients instead of code.
        :param body: Optional application/x-www-form-urlencoded body to add the
                     include in the token request. Prefer kwargs over body.
        :param auth: An auth tuple or method as accepted by requests.
        :param username: Username used by LegacyApplicationClients.
        :param password: Password used by LegacyApplicationClients.
        :param method: The HTTP method used to make the request. Defaults
                       to POST, but may also be GET. Other methods should
                       be added as needed.
        :param headers: Dict to default request headers with.
        :param timeout: Timeout of the request in seconds.
        :param verify: Verify SSL certificate.
        :param kwargs: Extra parameters to include in the token request.
        :return: A token dict
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        if not code and authorization_response:
            self._client.parse_request_uri_response(authorization_response, state=self._state)
            code = self._client.code
        elif not code and isinstance(self._client, WebApplicationClient):
            code = self._client.code
            if not code:
                raise ValueError("Please supply either code or " "authorization_response parameters.")

        body = self._client.prepare_request_body(
            code=code, body=body, redirect_uri=self.redirect_uri, username=username, password=password, **kwargs
        )

        client_id = kwargs.get("client_id", "")
        if auth is None:
            if client_id:
                log.debug('Encoding client_id "%s" with client_secret as Basic auth credentials.', client_id)
                client_secret = kwargs.get("client_secret", "")
                client_secret = client_secret if client_secret is not None else ""
                auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
            elif username:
                if password is None:
                    raise ValueError("Username was supplied, but not password.")
                log.debug("Encoding username, password as Basic auth credentials.")
                auth = requests.auth.HTTPBasicAuth(username, password)

        headers = headers or {
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
        }
        self.token = {}
        if method.upper() == "POST":
            r = self.post(
                token_url,
                data=dict(urldecode(body)),
                timeout=timeout,
                headers=headers,
                auth=auth,
                verify=verify,
                proxies=proxies,
            )
            log.debug("Prepared fetch token request body %s", body)
        elif method.upper() == "GET":
            # if method is not 'POST', switch body to querystring and GET
            r = self.get(
                token_url,
                params=dict(urldecode(body)),
                timeout=timeout,
                headers=headers,
                auth=auth,
                verify=verify,
                proxies=proxies,
            )
            log.debug("Prepared fetch token request querystring %s", body)
        else:
            raise ValueError("The method kwarg must be POST or GET.")

        log.debug("Request to fetch token completed with status %s.", r.status_code)
        log.debug("Request headers were %s", r.request.headers)
        log.debug("Request body was %s", r.request.body)
        log.debug("Response headers were %s and content %s.", r.headers, r.text)
        log.debug("Invoking %d token response hooks.", len(self.compliance_hook["access_token_response"]))
        for hook in self.compliance_hook["access_token_response"]:
            log.debug("Invoking hook %s.", hook)
            r = hook(r)

        self._client.parse_request_body_response(r.text, scope=self.scope)
        self.token = self._client.token
        log.debug("Obtained token %s.", self.token)
        return self.token