Beispiel #1
0
  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 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'),
      }

    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 #2
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 #3
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 #4
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
Beispiel #5
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")

        kwargs.update(self.auto_refresh_kwargs)
        body = self._client.prepare_refresh_body(body=body,
                                                 refresh_token=refresh_token,
                                                 **kwargs)

        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:
            text = await resp.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:
            self.token["refresh_token"] = refresh_token
        return self.token