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 params_from_uri(uri): params = dict(urldecode(urlparse(uri).query)) if 'scope' in params: params['scope'] = scope_to_list(params['scope']) return params
def collect_parameters(uri_query='', body=[], headers=None, exclude_oauth_signature=True, with_realm=False): """**Parameter Sources** Parameters starting with `oauth_` will be unescaped. Body parameters must be supplied as a dict, a list of 2-tuples, or a formencoded query string. Headers must be supplied as a dict. Per `section 3.4.1.3.1`_ of the spec. For example, the HTTP request:: POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Authorization: OAuth realm="Example", oauth_consumer_key="9djdj82h48djs9d2", oauth_token="kkk9d7dh3k39sjv7", oauth_signature_method="HMAC-SHA1", oauth_timestamp="137131201", oauth_nonce="7d8f3e4a", oauth_signature="djosJKDKJSD8743243%2Fjdk33klY%3D" c2&a3=2+q contains the following (fully decoded) parameters used in the signature base sting:: +------------------------+------------------+ | Name | Value | +------------------------+------------------+ | b5 | =%3D | | a3 | a | | c@ | | | a2 | r b | | oauth_consumer_key | 9djdj82h48djs9d2 | | oauth_token | kkk9d7dh3k39sjv7 | | oauth_signature_method | HMAC-SHA1 | | oauth_timestamp | 137131201 | | oauth_nonce | 7d8f3e4a | | c2 | | | a3 | 2 q | +------------------------+------------------+ Note that the value of "b5" is "=%3D" and not "==". Both "c@" and "c2" have empty values. While the encoding rules specified in this specification for the purpose of constructing the signature base string exclude the use of a "+" character (ASCII code 43) to represent an encoded space character (ASCII code 32), this practice is widely used in "application/x-www-form-urlencoded" encoded values, and MUST be properly decoded, as demonstrated by one of the "a3" parameter instances (the "a3" parameter is used twice in this request). .. _`section 3.4.1.3.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 """ headers = headers or {} params = [] # The parameters from the following sources are collected into a single # list of name/value pairs: # * The query component of the HTTP request URI as defined by # `RFC3986, Section 3.4`_. The query component is parsed into a list # of name/value pairs by treating it as an # "application/x-www-form-urlencoded" string, separating the names # and values and decoding them as defined by # `W3C.REC-html40-19980424`_, Section 17.13.4. # # .. _`RFC3986, Section 3.4`: http://tools.ietf.org/html/rfc3986#section-3.4 # .. _`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424 if uri_query: params.extend(urldecode(uri_query)) # * The OAuth HTTP "Authorization" header field (`Section 3.5.1`_) if # present. The header's content is parsed into a list of name/value # pairs excluding the "realm" parameter if present. The parameter # values are decoded as defined by `Section 3.5.1`_. # # .. _`Section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1 if headers: headers_lower = dict((k.lower(), v) for k, v in headers.items()) authorization_header = headers_lower.get('authorization') if authorization_header is not None: params.extend([i for i in utils.parse_authorization_header( authorization_header) if with_realm or i[0] != 'realm']) # * The HTTP request entity-body, but only if all of the following # conditions are met: # * The entity-body is single-part. # # * The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # `W3C.REC-html40-19980424`_. # * The HTTP request entity-header includes the "Content-Type" # header field set to "application/x-www-form-urlencoded". # # .._`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424 # TODO: enforce header param inclusion conditions bodyparams = extract_params(body) or [] params.extend(bodyparams) # ensure all oauth params are unescaped unescaped_params = [] for k, v in params: if k.startswith('oauth_'): v = utils.unescape(v) unescaped_params.append((k, v)) # The "oauth_signature" parameter MUST be excluded from the signature # base string if present. if exclude_oauth_signature: unescaped_params = list(filter(lambda i: i[0] != 'oauth_signature', unescaped_params)) return unescaped_params
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) 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, **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) 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