views:

155

answers:

0

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):

  1. What causes a "signature_invalid base_string" error in an initial request_token pull?
  2. What is different between the Twitter request token process and Google's OAuth API request token process?
  3. Why are the oauth_nonce's I'm generating (i.e. 15321613214272404748 using str(getrandbits(64))) so much shorter than the oauth_nonce's the OAuth Playground generates (i.e. f23d148edd312aac6984c6639ddab3fb)
  4. 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