I'm a true n00b (amateur, learning Python for fun), so as a programming exercise (to test my newfound knowledge of OAuth and App Engine), I adapted Mike Knapp's OAuth on App Engine code and was able to successfully get a request token and exchange it for an access token from Twitter.
However, when I attempted to do the same for Google's various APIs, I consistently receive HTTP 400 error with a cryptic error msg of "signature_invalid base_string
" followed by my base_string. After hours of fiddling to no avail, I checked Google's OAuth Playground and saw that the OAuth Playground could pull a request token using my consumer key and secret (and exchange for an access token, etc).
I even found that when I copied verbatim the oauth_nonce
, oauth_callback
and oauth_timestamp
values for successful OAuth Playground runs that I was unable to replicate the oauth_signature
that the Playground was generating.
Questions (4, very related):
- What causes a "
signature_invalid base_string
" error in an initial request_token pull? - What is different between the Twitter request token process and Google's OAuth API request token process?
- Why are the
oauth_nonce
's I'm generating (i.e. 15321613214272404748 usingstr(getrandbits(64))
) so much shorter than the oauth_nonce's the OAuth Playground generates (i.e. f23d148edd312aac6984c6639ddab3fb) - Why can't I replicate the OAuth Playground's signatures even when all the parameters are the same?
Thanks, would much appreciate some help
Edit: adding code pertaining to the encoding (apologies for the choppiness, but I broke out each of these steps so that I could inspect into each to see if anything weird was happening at any step); the first function is preparing a dictionary of parameters and the basestring itself; the second is the actual encoding
def prepareParameters(method, url, oauth_callback, consumer_key, oauth_token = None, oauth_verifier = None, scopes = None, oauth_version = "1.0", extra_parameters = None):
def encode(text):
return urlquote(str(text), '')
parameters = {
'oauth_consumer_key': consumer_key,
'oauth_signature_method': 'HMAC-SHA1',
'oauth_timestamp': str(int(time())),
'oauth_nonce': str(getrandbits(64)),
'oauth_version': oauth_version
}
if scopes:
if not isinstance(scopes, (list, tuple)):
scopes = [scopes,]
scopestring = ' '.join([str(scope) for scope in scopes])
parameters['scope'] = scopestring
if oauth_callback:
parameters['oauth_callback'] = oauth_callback
if oauth_token:
parameters['oauth_token'] = oauth_token
if oauth_verifier:
parameters['oauth_verifier'] = oauth_veriifer
if extra_parameters:
parameters.update(extra_parameters)
for k,v in parameters.items():
if isinstance(v, unicode):
parameters[k] = v.encode('utf8')
parametersstring = '&'.join(['%s=%s' % (encode(k), encode(parameters[k])) for k in sorted(parameters)])
basestring = '&'.join((method, encode(url), encode(parametersstring)))
return parameters, basestring
def encodeBaseString(basestring, consumer_secret, parameters, token_secret = ''):
key = '%s&%s' % (consumer_secret, token_secret)
signature = hmac(key, basestring, sha1)
encoded = signature.digest().encode('base64').strip()
parameters['oauth_signature'] = encoded
return parameters, encoded