Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Redis Cache? #135

Open
arnoldknott opened this issue Dec 7, 2024 · 5 comments
Open

Support for Redis Cache? #135

arnoldknott opened this issue Dec 7, 2024 · 5 comments
Labels
answered question Further information is requested

Comments

@arnoldknott
Copy link

arnoldknott commented Dec 7, 2024

I was wondering if there is a use for supporting Reis as a Cache?
I've seen some samples from the node-version of Microsoft Authentication Library MSAL.

It appears to me, that it might be straight forward, implementing a similar adapter based on the class BasePersistence of the python extensions for MSAL from this library. Something like that:

import redis
import json
from msal import ConfidentialClientApplication
from msal_extensions.token_cache import PersistedTokenCache
from msal_extensions.persistence import BasePersistence
from datetime import datetime, timedelta

# Redis connection:
redis_client = redis.Redis(
    host=<redis-host>,
    port=<redis-port>,
    password=<redis-password>
)

class RedisPersistence(BasePersistence):
    """Redis persistence class for the token cache"""

    def __init__(self, user_account):
        self.user_account = user_account

    def save(self, content):
        """Saves the token to the cache"""
        result = redis_client.json().set(self.get_location(), ".", content)
        return json.dumps(result)

    def load(self):
        """Loads the token from the cache"""
        result = redis_client.json().get(self.get_location())
        return json.dumps(result)

    def get_location(self):
        """Returns the location in the cache"""
        location = f"msal:{self.user_account['homeAccountId']}"
        return location

    def time_last_modified(self):
        """Returns the time the cache was last accessed"""
        try:
            idle_time = redis_client.object("idletime", self.get_location())
            if idle_time:
                last_accessed_time = datetime.now() - timedelta(seconds=idle_time)
                return last_accessed_time.timestamp()
        except Exception:
            raise Exception("no modification time available")


# Instantiate the cache:
def get_persistent_cache(user_account):
    """Returns the persistent cache for the user account"""
    persistence = RedisPersistence(user_account)
    persistedTokenCache = PersistedTokenCache(persistence)
    return persistedTokenCache


# use microsoft Authentication Library with Redis for a specific user_account,
# coming from the authentication stage:
    cache = get_persistent_cache(user_account)
    msal_client = ConfidentialClientApplication(
        client_id=<client-id>,
        client_credential=<client-secret>,
        authority=<authority>,
        token_cache=cache,
    )

    accounts = msal_client.get_accounts(user_account["username"])
    for account in accounts:
        result = msal_conf_client.acquire_token_silent([<scopes>], account=account)
        if "access_token" in result:
            return result["access_token"]
@rayluo
Copy link
Contributor

rayluo commented Dec 9, 2024

Hi @arnoldknott , thanks for sharing your code snippet, so that the world can benefit from it!

To answer your questions:

I was wondering if there is a use for supporting Reis as a Cache? I've seen some samples from the node-version of Microsoft Authentication Library MSAL.

It appears to me, that it might be straight forward, implementing a similar adapter based on the class * BasePersistence** of the python extensions for MSAL from this library. Something like that:

I suppose there might always be a need for something different than the default implementation. And yes, by accepting an app developer provided token_cache instance, MSAL Python always allow different cache backends as long as the same TokenCache API remains available.

One more thing. Depends on your use case, if you are building a website, nowadays we have web app samples (such as this web app sample for Flask) that stores the token cache in the user's session, so that you don't need to explicitly alter MSAL's token cache (but then you may want to customize the session backend of that web app).

@arnoldknott
Copy link
Author

arnoldknott commented Dec 9, 2024

You are so welcome, @rayluo

Well, it appears to me, that the limitation of the built in caching from MSAL is a few hundred users. Afterwards the in-memory caching is expected to become a perfomance limitation. So having more than a few hundred users, which is realistic for web applications but not for desktop apps, a caching in a database like Redis makes sense to me.

I think I understood the difference between using Microsoft identity libraries (as the Flask example you are suggesting) and MSAL in a way, that Microsoft Identity (Azure Identity) libraries allows the app (represented as a managed identity, service principle, user principle) to access Azure resources, e.g. a keyvault. MSAL allows user authentication and enables authorisation. Here's a question and answer on that topic.

So, as you keep emerging the MSAL libraries across different platforms: are you considering to make Redis one of those future default implementations, you are talking about?

Thanks for all the good work on MSAL. 👍

@rayluo
Copy link
Contributor

rayluo commented Dec 10, 2024

Before I answer your latest questions, @arnoldknott , it would be helpful to first understand the "layer" that MSAL library operates in. MSAL can be said as a "general purpose" auth library so it does not necessarily optimize for any specific scenario. MSAL's default token cache stays in memory, partially because that is the most common factor in any scenario. That default setup happens to work well for Desktop apps, but not so much for some backend apps that would maintain tokens for many users. So, accepting a TokenCache-like object is MSAL Python's answer to token cache polymorphism.

I believe the Azure Identity library also works in this layer, but with better integration with Azure SDK library to access all kinds of Azure services.

The web apps, on the other hand, are in a higher layer. More specific design choices can be made in that layer, but not in MSAL's layer. The sample that I referenced to, is powered by yet another library - which was built on top of MSAL - and happens to be also named "identity" (sorry, we are running out of names in this domain :-) ). In that web-focused "identity" library, you won't have to worry about partitioning token cache into Redis, because the user-owned tokens are stored in the session of the web framework.

@arnoldknott
Copy link
Author

@rayluo : I fully understand. Implementing Redis itself seems also out-of-scope for MSAL to me. I believe it would be worth an example sample to me. 😉

And yeah - the naming and multiple use of identity and principles can get confusing. The more we develop the more of those we get. 😬

@rayluo
Copy link
Contributor

rayluo commented Dec 10, 2024

Implementing Redis itself seems also out-of-scope for MSAL to me. I believe it would be worth an example sample to me. 😉

Thanks for your suggestion. I'll give it some thoughts. Meanwhile, your code snippet above serves as a good hint for others. Thanks again for your contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
answered question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants