AzureAD / microsoft-authentication-library-for-python

Microsoft Authentication Library (MSAL) for Python makes it easy to authenticate to Microsoft Entra ID. General docs are available here https://learn.microsoft.com/entra/msal/python/ Stable APIs are documented here https://msal-python.readthedocs.io. Questions can be asked on www.stackoverflow.com with tag "msal" + "python".
https://stackoverflow.com/questions/tagged/azure-ad-msal+python
Other
788 stars 194 forks source link

Can a PublicClientApplication use the cache without having to select account? The answer is no. #399

Closed kevindixon closed 3 years ago

kevindixon commented 3 years ago

Currently, in order to utilise the cache in a PublicClientApplication and leverage acquire_token_silent you MUST provide an account. This in turn means you have to have the user in the client app select an account.

I'd like to avoid having to have the user select an account in the client app - given they do this themselves in the auth UI on initial acquire_token_interactive - but still utilise the cache.

Maybe acquire_token_silent should return the account selected so it can be passed later to acquire_token_silent?

See also #206

rayluo commented 3 years ago

It is true that MSAL's PublicClientApplication is designed with a accounts_already_signed_in = get_accounts(...) and then acquire_token_silent(..., account=chosen_account) pattern, detailed in our README here. Such a pattern supports multi-account apps, so that your app could allow user to switch between their multiple active accounts.

If you are working on a single account app, you could gracefully bypass the selecting account experience, by changing the step 2 of the aforementioned pattern into something like this:

...
app = PublicClientApplication(...)
result = None

accounts_already_signed_in = app.get_accounts(...)
if accounts_already_signed_in:
    assert len(accounts_already_signed_in) == 1, "This app expects only one active account"
    chosen_account = accounts_already_signed_in[0]  # Automatically choose the first (and only?) signed-in account
    result = app.acquire_token_silent(["your_scope"], account=chosen_account)

if not result:
    # So no suitable token exists in cache. Let's get a new one from AAD.
    result = app.acquire_token_interactive(...)

if "access_token" in result:
    print(result["access_token"])  # Yay!
else:
    print(result.get("error"))
kevindixon commented 3 years ago

Yep, already doing that. Not very elegant, and seems odd to have to do this when acquire_token_interactive could return the account that was signed in, which be clearer and more elegant.

rayluo commented 3 years ago

Not very elegant, and seems odd...

Indeed, the current select account process is not very elegant for single account apps. :-( We may revisit this to see whether we can mitigate this. But the high level concept/pattern has been consistent, so far: your app would need to somehow get a hold of an account object, in order to call public_client_app.acquire_token_silent(..., account=chosen_account) which would utilize the token cache.

acquire_token_interactive could return the account that was signed in, which be clearer and more elegant

While the acquire_token_interactive() is indeed self-contained to get the tokens you want, the big difference is that acquire_token_interactive() (and all other acquire_token_by_xyz(...) methods, for that matter) does not utilize token cache. So, your app would be less performant, also the user experience would be slightly intrusive in a sense that acquire_token_interactive(...) would inevitably launch a new browser window which would also require user interaction to close it afterwards.