A Python MSAL Token Cache for Confidential Clients
Published: 2021-06-30 17:15:58

MSAL, the Microsoft Authentication Library, helps developers implement authentication and authorization using Azure AD or Azure AD B2C. With a few lines of code, you can add the necessary functionality sign in users and secure resources. MSAL comes with a lot of built-in capabilities to help you do as little work as possible. MFA, Conditional Access and advanced features are available out of the box - no code needed. In many cases, token caching is also provided out of the box. For example, the default cache is **in memory**. This works great for most dev scenarios, like testing web apps and APIs. 

However, there are certain scenarios that in-memory token caching doesn't work. Ideally, any workload deployed to production should have a persisted and scalable token cache.

In this blog post, we'll look at how to setup a file-based token cache for our Python-based daemon app. The reason why we need a token cache is because daemon apps are ephemeral. The app runs for a bit to execute a certain task and then terminates. The problem here is that without a token cache, every time we run the daemon, we have to authenticate and acquire a token from Azure AD - which is not efficient!

Let's build a cache. 

## Building the MSAL Python Cache

The good news is that we don't really have to do a lot of work for this. The MSAL team has already built an [extension library](https://github.com/AzureAD/microsoft-authentication-extensions-for-python) for Python to provide the basic plumbing for our token cache. 

In our existing Python app, we need to add the new library and implement a bit of code to set everything up.

Open the `requirements.txt`, add the following and save the file:

```
msal-extensions>=0.3.0
```
We can now update our dependencies

```
pip install -r requirement.txt
```
Create a new file `msalcache.py` and add the following code

```
import sys
import logging

from msal_extensions import *

def build_persistence(location, fallback_to_plaintext=False):
    if sys.platform.startswith('win'):
        return FilePersistenceWithDataProtection(location)
    if sys.platform.startswith('darwin'):
        return KeychainPersistence(location, "my_service_name", "my_account_name")
    if sys.platform.startswith('linux'):
        try:
            return LibsecretPersistence(
                location,
                schema_name="my_schema_name",
                attributes={"attr1": "hello", "attr2": "world"},
                )
        except:
            if not fallback_to_plaintext:
                raise
            logging.exception("Encryption unavailable. Opting in to plain text.")
    return FilePersistence(location)
```
That's it. We can now use this code to instantiate a cache and assign it to our MSAL Confidential Client.

## Using the token cache with MSAL

Before instantiating our MSAL `ConfidentialClient` we need to create a cache. Add the following code to `console.py` (or your main code file)

```
persistence =msalcache.build_persistence("token_cache.bin")
print(f'The MSAL Cache supports encryption: {persistence.is_encrypted}')
cache = PersistedTokenCache(persistence)
```
Finally, we have to update our `ConfidentialClient` client to use the cache. Update the code as per below:

```
app = msal.ConfidentialClientApplication(
    client_id=config["client_id"],
    client_credential=aad_client_secret.value,
    authority=config["authority"],
    token_cache=cache
)
```
That's it!

Running the code from now on will make use of the cache and the call the `acquire_token_silent()` will yield an access token after the first time we run our daemon :)

You can get the source code for this project from [our GitHub repo](https://github.com/425show/python-console-aad/tree/TokenSerialization)

If you want to learn more about the MSAL for Python library, you can read the docs [here](https://msal-python.readthedocs.io/en/latest/)