Building a Python HTTP client that automatically refreshes access tokens

How often is it that when calling API's you do an initial round of auth to receive a token that expires after a certain amount of time?
It is quite common these days. What isn't common is a generic way to ensure that this authentication process is automatically restarted when the existing token expires.

The first thing to remember is that we should use some persistent storage mechanism to keep the token.

Where are we going to store this access token value then? We need to store it as it needs to be accessed between multiple requests.

The database is not a terrible place, I've seen it done before. Or you can use django's cache API.

Finding an Example

I wish there was a client I could view and see how other people have implemented this in python.

Here is an apt comment from the php community:

...for other APIs I have found the decorator pattern around the HTTP client (especially a PSR-18 client, because its interface is so simple) is a great place to put the renewal logic. That's the point where the expiry response is known, the full original request is known so can be tried again, and the renewed authentication token can be saved in the current client and saved to a persistent storage.

So I understand, here you have a stack of middleware (kind of) handlers that each wrap the request and pass back the response. On an expired token response you try renewing the token, then bounce the same request back down the chain again? The retry count allows you to give up after a number of bounces.

You could inject a closure in this middleware here to handle the persisting of the new token to storage.

JudgeJ comment on guzzle

A Javascript implementation example, the gist:

  1. Store the token
  2. Wrap every request in a decorator that reauths when a `401` is received
  3. Ensure the original request is redone

I managed to find a python implementation for python-fitbit

Storing the Token in Cache

You can store the auth token in cache like so:


from django.core.cache import cache 

@property
def session(self):
    session = requests.Session()
    session.verify = settings.VERIFY_SSL

    # Check there is a token saved
    auth_header = cache.get('auth_header')
    if not auth_header:
        auth_header = self.get_authorization(session)
        cache.set('auth_header', auth_header)

    session.headers.update(auth_header)

    return session