vgrem / Office365-REST-Python-Client

Microsoft 365 & Microsoft Graph Library for Python
MIT License
1.34k stars 335 forks source link

Q: how to deal with token refresh? #739

Open steffenschumacher opened 1 year ago

steffenschumacher commented 1 year ago

We have an API which integrates with Sharepoint, where ideally we don't obtain tokens for each API call. But from what I can tell, there are no infra to handle expiring tokens/refreshing them. Should I:

PS. I'm basing this on this from TokenResponse:

    def is_valid(self):
        return self.accessToken is not None and self.tokenType == 'Bearer'

Seems not to check anything RE expiry

kellerza commented 1 year ago

Refresh Tokens can be handled by the msal library. It contains a token cache that can be serialized - SerializableTokenCache and then be stored somewhere safe

I'm not sure Refreshing tokens, or storing these forms part of this library, but an example might be useful.

An example of where I use this, is in the aiohttp_msal library: see here for retrieving these from Redis. Although the typical flow would be renewing client tokens based on the Tokencache stored in an aiohttp_session session (mostly using using Redis for the actual storage)

steffenschumacher commented 1 year ago

There are many ways to deal with it, and I’m not arguing that this library should reinvent the wheel, but ideally leverage existing options to deal with expired tokens in some way. I can’t easily see how I would easily inject handling of expired tokens, without touching a fair share of internals of this library?

kellerza commented 1 year ago

Not tested, starting from the function in the readme and adding a token_cache. Stored in clear text.

def acquire_token_func():
    """
    Acquire token via MSAL
    """
    authority_url = 'https://login.microsoftonline.com/{tenant_id_or_name}'
    path = pathlib.Path("/my_token_cache.json")
    token_cache = SerializableTokenCache()
    token_cache.deserialize(path.read_text(encoding="utf8"))
    app = msal.ConfidentialClientApplication(
        authority=authority_url,
        client_id='{client_id}',
        client_credential='{client_secret}'
        token_cache=token_cache
    )
    token = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
    if token_cache.has_state_change:
         cstr = token_cache.serialize()
         path.write_text(cstr, encoding="utf8")
    return token