MSAL Python turns token acquisition into a cache lookup and one call
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".
At a glance
- What is it?
- The Microsoft Authentication Library for Python is the client side of the Microsoft identity platform, and the documentation is unusually explicit about the shape of the work. You pick a client type, you keep one application object, you consult the token cache before you ask the network for anything, and you check the result dictionary for an access token or an error with a correlation id. The repository around it is a properly maintained package, with a benchmark suite and a test lab that pulls its secrets from Azure Key Vault.
- Who is it for?
- MSAL Python suits any Python application that has to authenticate against Microsoft Entra ID, Azure AD B2C or Graph, since it implements the platform's protocols properly and hands you a token rather than a session.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One library, several identity flavours
MSAL Python enables applications to integrate with the Microsoft identity platform, and the set of identities it covers is broader than the name Microsoft Entra ID suggests. You can sign in users or applications with Entra ID accounts, with external identities, with plain Microsoft accounts, and with Azure AD B2C accounts, and in each case the point of the library is to obtain tokens you can use against Microsoft Graph or against your own APIs registered on the platform. The implementation is built on industry standard OAuth2 and OpenID Connect protocols, so what you are getting is a well behaved client for the platform's flows rather than a proprietary mechanism. The library is also not the only Microsoft identity SDK, and the README says so directly, pointing at a wiki page listing the other client libraries, which is worth reading before you commit to it, because the SDK choice is often made by the language of the existing service rather than by the flows you need.
Public or confidential client is the first decision
The usage documentation starts by separating two kinds of application, and it grounds that separation in the OAuth2 specification rather than in Microsoft's own taxonomy. A public client application is one that cannot hold a secret, a desktop app, a mobile app or a script running on a user's machine, and a confidential client application is one that can, a web service or a daemon with credentials of its own. You create a `PublicClientApplication` or a `ConfidentialClientApplication` accordingly, and the documentation is clear that you should reuse that instance for the lifetime of your application rather than creating a new one per call, since the instance is what owns the token cache. The example is deliberately minimal:
from msal import PublicClientApplication
app = PublicClientApplication(
"your_client_id",
authority="https://login.microsoftonline.com/Enter_the_Tenant_Name_Here")Two details in it are worth pausing on. The authority carries the tenant, and the placeholder in the example is there to be replaced with your own tenant rather than a common one, since the tenant determines who can sign in. And the client id is not a secret, it identifies the application, which is why a public client can hold one in a shipped binary. Before any of this runs, the application has to be registered with the identity platform, and the README links the registration walkthrough as a prerequisite rather than assuming it.
The token cache is the step most people skip
The second step is the one the documentation pushes hardest, and it is the step that turns a working prototype into a working application. The API model gives you explicit control over the token cache, and the documentation says the cache is technically optional but strongly recommended because it handles token refresh for you. The pattern it shows is to look for an account the end user has already signed in with, let the user pick one, and then ask for a token silently:
accounts = app.get_accounts()
if accounts:
print("Pick the account you want to use to proceed:")
for a in accounts:
print(a["username"])
chosen = accounts[0]
result = app.acquire_token_silent(["your_scope"], account=chosen)The word silent is the important one, since the call returns a cached token when there is a usable one and does not prompt the user. Only when there is no suitable token, or when you skipped the lookup, do you go to the network. The cache is also what makes a multi user application workable, since the accounts list is per user and the silent call is per account, so the code has to keep track of which user it is acting for. The scope argument is where you name the permissions you want, and it is the same string you would put in the token request itself.
Always check the result, and keep the correlation id
The third step is the network call, and the documentation is careful to present it as a placeholder because the method depends on your client type and your flow. What it does show is the shape of the result and, more usefully, how to handle failure:
if not result:
result = app.acquire_token_by_one_of_the_actual_method(..., scopes=["User.Read"])
if "access_token" in result:
print(result["access_token"]) # Yay!
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You may need this when reporting a bugThe pattern is that the method returns a dictionary that either contains an access token or does not, and the failure path is not an exception but a dictionary with an error and an error description. The correlation id is the last line for a reason, since that is the identifier support uses to find the request in the platform's logs, and losing it turns a support ticket into a guessing game. The other observation is that the error path in a flow with an interactive component can mean the user declined, or the request was wrong, or the tenant blocked it, and the three look identical unless you print the description.
The samples cover the flows you will actually need
The `sample/` directory in the repository is a map of the supported scenarios, and the file names are the map. There is an authorization code flow sample in its own directory, a confidential client sample, a device flow sample for input constrained devices, an interactive sample, a managed identity sample, a username and password sample, a vault JWT sample, and a `migrate_rt.py` sample that deals with refresh tokens, which is the piece you need when you are moving tokens across a boundary. The environment file samples are equally informative, with separate ones for Entra ID, for external identities, for an external identity with a custom domain, and for managed identity, so you can see which configuration each flow expects. The documentation also points at a static map of scenarios with clickable links to the proper samples, and says to use the runnable samples hosted in the repository rather than the conceptual pattern, because the variations between flows are where the mistakes happen.
A benchmarked package with a Key Vault backed test lab
The parts of the repository that are not the library are worth a look if you care about how a Microsoft SDK is maintained. The `requirements.txt` lists the development dependencies and they explain the shape of the test setup: pytest-benchmark and a perf baseline package for the performance suite, and azure-identity together with azure-keyvault-secrets, with a comment saying they are there for lab_config.py to fetch test secrets from Key Vault. So the benchmarks run against a lab whose secrets are pulled from Azure Key Vault rather than from a file on a developer machine, which is the arrangement you want if the tests touch anything real. Around that sit tox.ini for the test matrix, an azure-pipelines.yml with a `.Pipelines/` directory, a `docker_run.sh`, a `spikes/` directory for experiments, and the usual documents, CONTRIBUTING, the release guide and a separate RELEASES file. The README also carries a benchmark diagram and badges for build status, documentation status and download counts, and a note that the library follows semantic versioning with the changes for each version listed on the releases page.
ADAL is finished, and the licence is worth a look
Two closing points for anyone planning an adoption. The first is the migration. If your application is using ADAL Python, the documentation recommends moving to MSAL Python, and it says plainly that no new feature work will be done in ADAL Python, with a migration guide linked. That is the standard Microsoft signal that a library is in maintenance mode, so an application still on ADAL is accumulating risk. The second is licensing. The repository records a custom licence that GitHub cannot classify, with the terms in the LICENSE file, and this review does not interpret them. The difference from a standard permissive licence is exactly the sort of thing a legal team should read before the code goes into a product. The project itself is clearly alive, with releases 1.39.0 on 2026-09-17, 1.38.0 on 2026-08-24 and 1.37.0 on 2026-06-01, and the last push to the repository on 2026-10-15.
Editorial conclusion
MSAL Python suits any Python application that has to authenticate against Microsoft Entra ID, Azure AD B2C or Graph, since it implements the platform's protocols properly and hands you a token rather than a session. It is a poor fit if you are still on ADAL, because the documentation states that no new feature work will be done on ADAL Python and points you at a migration guide instead, and it is a poor fit if you want a vendored copy with your own terms, since the repository records a custom licence that GitHub cannot classify. Before you depend on it, check three things: the version you pin, with 1.39.0 published on 2026-09-17 and the last push on 2026-10-15, that you use the token cache rather than calling for a token every time, since that is where the silent refresh behaviour lives, and which client type your application actually is, since a public client on a server is a design mistake the library cannot fix for you.
Frequently asked questions
How do I install MSAL Python?
With `pip install msal` from PyPI. The README asks you to make sure the pip in your environment is reasonably recent, noting that it was tested with pip 18.1, and the package follows semantic versioning, with the changes for each version listed on the releases page.
What is the difference between a public and a confidential client in MSAL Python?
The separation comes from the OAuth2 specification. A public client cannot hold a secret, such as a desktop app or a script, and is created with PublicClientApplication, while a confidential client can, such as a web service, and is created with ConfidentialClientApplication. The documentation recommends creating one instance and reusing it for the lifetime of the application.
Why should I use the MSAL Python token cache?
The cache is technically optional but strongly recommended, because it handles token refresh for you. The documented pattern is to list the accounts the user has signed in with, pick one, and call acquire_token_silent with the scopes and that account, which returns a cached token without prompting when one is usable.
How do I handle errors when acquiring a token with MSAL Python?
The result is a dictionary, so you check whether it contains an access token. If it does not, the dictionary carries an error, an error_description and a correlation_id, and the documentation says to print the correlation id because you may need it when reporting a bug.
Is ADAL Python still being developed?
No new feature work will be done on ADAL Python, according to the README, which recommends moving to MSAL Python and links a migration guide. MSAL itself is active, with 1.39.0 published on 2026-09-17 and the last push to the repository on 2026-10-15.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/azuread-microsoft-authentication-library-for-python)Was this useful?