google-code-export / google-api-python-client

Automatically exported from code.google.com/p/google-api-python-client
Other
1 stars 0 forks source link

SignedJwtAssertionCredentials are missing token_expiry value after retrieving from Storage #330

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Steps to reproduce:

1. Create SignedJwtAssertionCredentials(), use them to make an API call and put 
them in storage
2. Retrieve the credentials from storage after 5 minutes
3. Examine the credentials.token_expiry field

You would expect to see the expiration of the token (an hour for service 
accounts).  Instead the value is null.

Example code to reproduce:

===========================
storage = StorageByKeyName(CredentialsModel, credKey, 'credentials', 
cache=memcache)

credentials = SignedJwtAssertionCredentials(    config.SERVICE_EMAIL,
                                                            config.SERVICE_KEY,
                                                            scope='https://www.googleapis.com/auth/calendar',
                                                            sub=accountObj.email )

storage.put(credentials)
credentials.set_store(storage)
http = httplib2.Http(cache=memcache, timeout=60)
http = credentials.authorize(http)
service = build('calendar', 'v3', http=http)

....
do something with calendar service

5 minutes later get the credentials out of storage
.....
credentials = storage.get()

print credentials.token_expiry

============================

The missing token_expiry is significant because this value is used by 
access_token_expired().  If token_expiry is None then access_token_expired() 
returns False, even though in reality the token may in fact be expired. This in 
turn leads to needless extra API calls, because when a request is made with an 
expired token a 401 result is returned and then the token must be refreshed.

If token_expiry was maintained correctly and access_token_expired() returned an 
accurate result then a developer could know in advance to refresh the token and 
make only 1 API call instead of having to make a call and then get a 401 and 
then refresh and then make the call again.

The problem is in the from_json() method of SignedJwtAssertionCredentials.

The following code fixes the issue:

  class SignedJwtAssertionCredentials(AssertionCredentials):
     ....

    @classmethod
    def from_json(cls, s):
      data = simplejson.loads(s)
      retval = SignedJwtAssertionCredentials(
          data['service_account_name'],
          base64.b64decode(data['private_key']),
          data['scope'],
          private_key_password=data['private_key_password'],
          user_agent=data['user_agent'],
          token_uri=data['token_uri'],
          **data['kwargs']
          )
      retval.invalid = data['invalid']
      retval.access_token = data['access_token']

      # ========= ADD THESE LINESE TO CORRECTLY RESTORE THE TOKEN_EXPIRY ===========
      if 'token_expiry' in data and not isinstance(data['token_expiry'], datetime.datetime):
          try:
            retval.token_expiry = datetime.datetime.strptime(data['token_expiry'], EXPIRY_FORMAT)
          except:
            retval.token_expiry = None
      #==============================

      return retval

Original issue reported on code.google.com by henne...@gmail.com on 11 Mar 2014 at 1:45

GoogleCodeExporter commented 9 years ago

Original comment by jcgregorio@google.com on 11 Mar 2014 at 1:29