| MSALPYTHON(1) | MSAL Python | MSALPYTHON(1) |
msalpython - MSAL Python Documentation
You can find high level conceptual documentations in the project README.
There are many different application scenarios. MSAL Python supports some of them. The following diagram serves as a map. Locate your application scenario on the map. If the corresponding icon is clickable, it will bring you to an MSAL Python sample for that scenario.
NOTE:
Everything else, regardless of their naming, are all internal helpers, which could change at anytime in the future, without prior notice.
The following section is the API Reference of MSAL Python. The API Reference is like a dictionary, which is useful when:
MSAL proposes a clean separation between public client applications and confidential client applications.
They are implemented as two separated classes, with different methods for different authentication scenarios.
For PublicClientApplication, you use None here.
For ConfidentialClientApplication, it supports many different input formats for different scenarios.
Just feed in a string, such as "your client secret".
Feed in a dict in this form:
{
"private_key": "...-----BEGIN PRIVATE KEY-----... in PEM format",
"thumbprint": "A1B2C3D4E5F6...",
"passphrase": "Passphrase if the private_key is encrypted (Optional. Added in version 1.6.0)",
}
MSAL Python requires a "private_key" in PEM format. If your cert is in PKCS12 (.pfx) format, you can convert it to X.509 (.pem) format, by openssl pkcs12 -in file.pfx -out file.pem -nodes.
The thumbprint is available in your app's registration in Azure Portal. Alternatively, you can calculate the thumbprint.
Subject Name/Issuer Auth is an approach to allow easier certificate rotation.
Added in version 0.5.0:
{
"private_key": "...-----BEGIN PRIVATE KEY-----... in PEM format",
"thumbprint": "A1B2C3D4E5F6...",
"public_certificate": "...-----BEGIN CERTIFICATE-----...",
"passphrase": "Passphrase if the private_key is encrypted (Optional. Added in version 1.6.0)",
}
public_certificate (optional) is public key certificate which will be sent through 'x5c' JWT header only for subject name and issuer authentication to support cert auto rolls.
Per specs, "the certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the one used to certify the previous one." However, your certificate's issuer may use a different order. So, if your attempt ends up with an error AADSTS700027 - "The provided signature value did not match the expected signature value", you may try use only the leaf cert (in PEM/str format) instead.
Added in version 1.13.0: It can also be a completely pre-signed assertion that you've assembled yourself. Simply pass a container containing only the key "client_assertion", like this:
{
"client_assertion": "...a JWT with claims aud, exp, iss, jti, nbf, and sub..."
}
Added in version 1.29.0: Feed in a dictionary containing the path to a PFX file:
{
"private_key_pfx_path": "/path/to/your.pfx",
"passphrase": "Passphrase if the private_key is encrypted (Optional)",
}
The following command will generate a .pfx file from your .key and .pem file:
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.pem
Added in version 1.30.0: If your .pfx file contains both the private key and public cert, you can opt in for Subject Name/Issuer Auth like this:
{
"private_key_pfx_path": "/path/to/your.pfx",
"public_certificate": True,
"passphrase": "Passphrase if the private_key is encrypted (Optional)",
}
Added in version 0.5.0: It is a dictionary of extra claims that would be signed by by this ConfidentialClientApplication 's private key. For example, you can use {"client_ip": "x.x.x.x"}. You may also override any of the following default claims:
{
"aud": the_token_endpoint,
"iss": self.client_id,
"sub": same_as_issuer,
"exp": now + 10_min,
"iat": now,
"jti": a_random_uuid
}
A URL that identifies a token authority. It should be of the format https://login.microsoftonline.com/your_tenant By default, we will use https://login.microsoftonline.com/common
Changed in version 1.17: you can also use predefined constant and a builder like this:
from msal.authority import (
AuthorityBuilder,
AZURE_US_GOVERNMENT, AZURE_CHINA, AZURE_PUBLIC) my_authority = AuthorityBuilder(AZURE_PUBLIC, "contoso.onmicrosoft.com") # Now you get an equivalent of # "https://login.microsoftonline.com/contoso.onmicrosoft.com" # You can feed such an authority to msal's ClientApplication from msal import PublicClientApplication app = PublicClientApplication("my_client_id", authority=my_authority, ...)
(optional) Allows configuration of one or more client capabilities, e.g. ["CP1"].
Client capability is meant to inform the Microsoft identity platform (STS) what this client is capable for, so STS can decide to turn on certain features. For example, if client is capable to handle claims challenge, STS may issue Continuous Access Evaluation (CAE) access tokens to resources, knowing that when the resource emits a claims challenge the client will be able to handle those challenges.
Implementation details: Client capability is implemented using "claims" parameter on the wire, for now. MSAL will combine them into claims parameter which you will later provide via one of the acquire-token request.
(optional) Instructs MSAL to use the Entra regional token service. This legacy feature is only available to first-party applications. Only acquire_token_for_client() is supported.
Supports 4 values:
NOTE:
New in version 1.12.0.
MSAL has long been caching tokens in the token_cache. Recently, MSAL also introduced a concept of http_cache, by automatically caching some finite amount of non-token http responses, so that long-lived PublicClientApplication and ConfidentialClientApplication would be more performant and responsive in some situations.
This http_cache parameter accepts any dict-like object. If not provided, MSAL will use an in-memory dict.
If your app is a command-line app (CLI), you would want to persist your http_cache across different CLI runs. The following recipe shows a way to do so:
# Just add the following lines at the beginning of your CLI script import sys, atexit, pickle http_cache_filename = sys.argv[0] + ".http_cache" try:
with open(http_cache_filename, "rb") as f:
persisted_http_cache = pickle.load(f) # Take a snapshot except (
FileNotFoundError, # Or IOError in Python 2
pickle.UnpicklingError, # A corrupted http cache file
AttributeError, # Cache created by a different version of MSAL
):
persisted_http_cache = {} # Recover by starting afresh atexit.register(lambda: pickle.dump(
# When exit, flush it back to the file.
# It may occasionally overwrite another process's concurrent write,
# but that is fine. Subsequent runs will reach eventual consistency.
persisted_http_cache, open(http_cache_file, "wb"))) # And then you can implement your app as you normally would app = msal.PublicClientApplication(
"your_client_id",
...,
http_cache=persisted_http_cache, # Utilize persisted_http_cache
...,
#token_cache=..., # You may combine the old token_cache trick
# Please refer to token_cache recipe at
# https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
) app.acquire_token_interactive(["your", "scope"], ...)
Content inside http_cache are cheap to obtain. There is no need to share them among different apps.
Content inside http_cache will contain no tokens nor Personally Identifiable Information (PII). Encryption is unnecessary.
New in version 1.16.0.
Historically, MSAL would connect to a central endpoint located at https://login.microsoftonline.com to acquire some metadata, especially when using an unfamiliar authority. This behavior is known as Instance Discovery.
This parameter defaults to None, which enables the Instance Discovery.
If you know some authorities which you allow MSAL to operate with as-is, without involving any Instance Discovery, the recommended pattern is:
known_authorities = frozenset([ # Treat your known authorities as const
"https://contoso.com/adfs", "https://login.azs/foo"]) ... authority = "https://contoso.com/adfs" # Assuming your app will use this app1 = PublicClientApplication(
"client_id",
authority=authority,
# Conditionally disable Instance Discovery for known authorities
instance_discovery=authority not in known_authorities,
)
If you do not know some authorities beforehand, yet still want MSAL to accept any authority that you will provide, you can use a False to unconditionally disable Instance Discovery.
New in version 1.19.0.
When enabled, logs may include PII (Personal Identifiable Information). This can be useful in troubleshooting broker behaviors. The default behavior is False.
New in version 1.24.0.
Added in version 1.28.0: It is a URL that identifies an OpenID Connect (OIDC) authority of the format https://contoso.com/tenant. MSAL will append ".well-known/openid-configuration" to the authority and retrieve the OIDC metadata from there, to figure out the endpoints.
Note: Broker will NOT be used for OIDC authority.
It automatically provides nonce protection.
Scopes requested to access a protected API (a resource).
Most of the time, you can leave it empty.
If you requested user consent for multiple resources, here you will need to provide a subset of what you required in initiate_auth_code_flow().
OAuth2 was designed mostly for singleton services, where tokens are always meant for the same resource and the only changes are in the scopes. In Microsoft Entra, tokens can be issued for multiple 3rd party resources. You can ask authorization code for multiple resources, but when you redeem it, the token is for only one intended recipient, called audience. So the developer need to specify a scope so that we can restrict the token to be issued for the corresponding audience.
def authorize(): # A controller in a web app
try:
result = msal_app.acquire_token_by_auth_code_flow(
session.get("flow", {}), request.args)
if "error" in result:
return render_template("error.html", result)
use(result) # Token(s) are available in result and cache
except ValueError: # Usually caused by CSRF
pass # Simply ignore them
return redirect(url_for("index"))
(Required) Scopes requested to access a protected API (a resource).
If you requested user consent for multiple resources, here you will typically want to provide a subset of what you required in AuthCode.
OAuth2 was designed mostly for singleton services, where tokens are always meant for the same resource and the only changes are in the scopes. In Microsoft Entra, tokens can be issued for multiple 3rd party resources. You can ask authorization code for multiple resources, but when you redeem it, the token is for only one intended recipient, called audience. So the developer need to specify a scope so that we can restrict the token to be issued for the corresponding audience.
You use this method only when you have old RTs from elsewhere, and now you want to migrate them into MSAL. Calling this method results in new tokens automatically storing into MSAL.
You do NOT need to use this method if you are already using MSAL. MSAL maintains RT automatically inside its token cache, and an access token can be retrieved when you call acquire_token_silent().
See this page for constraints of Username Password Flow. https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication
You can provide an msal.auth_scheme.PopAuthScheme object so that MSAL will get a Proof-of-Possession (POP) token for you.
New in version 1.26.0.
It has same parameters as the acquire_token_silent_with_error(). The difference is the behavior of the return value. This method will combine the cache empty and refresh error into one return value, None. If your app does not care about the exact token refresh error during token cache look-up, then this method is easier and recommended.
It is done either by finding a valid access token from cache, or by finding a valid refresh token from cache and then automatically use it to redeem a new access token.
This method will differentiate cache empty from token refresh error. If your app cares the exact token refresh error during token cache look-up, then this method is suitable. Otherwise, the other method acquire_token_silent() is recommended.
You can provide an msal.auth_scheme.PopAuthScheme object so that MSAL will get a Proof-of-Possession (POP) token for you.
New in version 1.26.0.
An account can later be used in acquire_token_silent() to find its tokens.
Default value is "code" for an OAuth2 Authorization Code grant.
You could use other content such as "id_token" or "token", which would trigger an Implicit Grant, but that is not recommended.
Later when the response reaches your redirect_uri, you can use acquire_token_by_auth_code_flow() to complete the authentication/authorization.
Can be one of "consumers" or "organizations" or your tenant domain "contoso.com". If included, it will skip the email-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience. More information on possible values available in Auth Code Flow doc and domain_hint doc.
OPTIONAL. Maximum Authentication Age. Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated. If the elapsed time is greater than this value, Microsoft identity platform will actively re-authenticate the End-User.
MSAL Python will also automatically validate the auth_time in ID token.
New in version 1.15.
{
"auth_uri": "https://...", // Guide user to visit this
"state": "...", // You may choose to verify it by yourself,
// or just let acquire_token_by_auth_code_flow()
// do that for you.
"...": "...", // Everything else are reserved and internal
}
The caller is expected to:
NOTE:
What is a broker, and why use it?
A broker is a component installed on your device. Broker implicitly gives your device an identity. By using a broker, your device becomes a factor that can satisfy MFA (Multi-factor authentication). This factor would become mandatory if a tenant's admin enables a corresponding Conditional Access (CA) policy. The broker's presence allows Microsoft identity platform to have higher confidence that the tokens are being issued to your device, and that is more secure.
An additional benefit of broker is, it runs as a long-lived process with your device's OS, and maintains its own cache, so that your broker-enabled apps (even a CLI) could automatically SSO from a previously established signed-in session.
You shall only enable broker when your app:
The fallback behaviors of MSAL Python's broker support
MSAL will either error out, or silently fallback to non-broker flows.
This setting is only effective if your app is running on Windows 10+. This parameter defaults to None, which means MSAL will not utilize a broker.
New in MSAL Python 1.25.0.
This setting is only effective if your app is running on Mac. This parameter defaults to None, which means MSAL will not utilize a broker.
New in MSAL Python 1.31.0.
Prerequisite: In Azure Portal, configure the Redirect URI of your "Mobile and Desktop application" as http://localhost. If you opts in to use broker during PublicClientApplication creation, your app also need this Redirect URI: ms-appx-web://Microsoft.AAD.BrokerPlugin/YOUR_CLIENT_ID
Can be one of "consumers" or "organizations" or your tenant domain "contoso.com". If included, it will skip the email-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience. More information on possible values available in Auth Code Flow doc and domain_hint doc.
OPTIONAL. Maximum Authentication Age. Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated. If the elapsed time is greater than this value, Microsoft identity platform will actively re-authenticate the End-User.
MSAL Python will also automatically validate the auth_time in ID token.
New in version 1.15.
OPTIONAL.
Most Python scripts are console apps.
New in version 1.20.0.
A callback with the form of lambda ui="xyz", **kwargs: print("A {} will be launched".format(ui)), where ui will be either "browser" or "broker". You can use it to inform your end user to expect a pop-up window.
New in version 1.20.0.
You can provide an msal.auth_scheme.PopAuthScheme object so that MSAL will get a Proof-of-Possession (POP) token for you.
New in version 1.26.0.
Since MSAL Python 1.23, it will automatically look for token from cache, and only send request to Identity Provider when cache misses.
The current app is a middle-tier service which was called with a token representing an end user. The current app can use such token (a.k.a. a user assertion) to request another token to access downstream web API, on behalf of that user. See detail docs here .
The current middle-tier app has no user interaction to obtain consent. See how to gain consent upfront for your middle-tier app from this article. https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#gaining-consent-for-the-middle-tier-application
One of the parameters accepted by both PublicClientApplication and ConfidentialClientApplication is the TokenCache.
Although it maintains tokens using unified schema across all MSAL libraries, this class does not serialize/persist them. See subclass SerializableTokenCache for details on serialization.
It is O(1) for AT hits, and O(n) for other types. Note that it holds a lock during the entire search.
You can subclass it to add new behavior, such as, token serialization. See SerializableTokenCache for example.
This class does NOT actually persist the cache on disk/db/etc.. Depending on your need, the following simple recipe for file-based, unencrypted persistence may be sufficient:
import os, atexit, msal cache_filename = os.path.join( # Persist cache into this file
os.getenv(
# Automatically wipe out the cache from Linux when user's ssh session ends.
# See also https://github.com/AzureAD/microsoft-authentication-library-for-python/issues/690
"XDG_RUNTIME_DIR", ""),
"my_cache.bin") cache = msal.SerializableTokenCache() if os.path.exists(cache_filename):
cache.deserialize(open(cache_filename, "r").read()) atexit.register(lambda:
open(cache_filename, "w").write(cache.serialize())
# Hint: The following optional line persists only when state changed
if cache.has_state_changed else None
) app = msal.ClientApplication(..., token_cache=cache) ...
Alternatively, you may use a more sophisticated cache persistence library, MSAL Extensions, which provides token cache persistence with encryption, and more.
The values are based on https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
This is used as the auth_scheme parameter in many of the acquire token methods to support for Proof of Possession (PoP) tokens.
New in MSAL Python 1.26
These are exceptions that MSAL Python may raise. You should not need to create them directly. You may want to catch them to provide a better error message to your end users.
MSAL supports Managed Identity.
You can create one of these two kinds of managed identity configuration objects:
It is equivalent to a Python dict of:
{"ManagedIdentityIdType": "SystemAssigned", "Id": None}
or a JSON blob of:
{"ManagedIdentityIdType": "SystemAssigned", "Id": null}
Depends on the id you provided, the outcome is equivalent to one of the below:
{"ManagedIdentityIdType": "ClientId", "Id": "foo"}
{"ManagedIdentityIdType": "ResourceId", "Id": "foo"}
{"ManagedIdentityIdType": "ObjectId", "Id": "foo"}
And then feed the configuration object into a ManagedIdentityClient object.
It also provides token cache support.
NOTE:
An http client object. For example, you can use requests.Session(), optionally with exponential backoff behavior demonstrated in this recipe:
import msal, requests from requests.adapters import HTTPAdapter, Retry s = requests.Session() retries = Retry(total=3, backoff_factor=0.1, status_forcelist=[
429, 500, 501, 502, 503, 504]) s.mount('https://', HTTPAdapter(max_retries=retries)) managed_identity = ... client = msal.ManagedIdentityClient(managed_identity, http_client=s)
Recipe 1: Hard code a managed identity for your app:
import msal, requests client = msal.ManagedIdentityClient(
msal.UserAssignedManagedIdentity(client_id="foo"),
http_client=requests.Session(),
) token = client.acquire_token_for_client("resource")
Recipe 2: Write once, run everywhere. If you use different managed identity on different deployment, you may use an environment variable (such as MY_MANAGED_IDENTITY_CONFIG) to store a json blob like {"ManagedIdentityIdType": "ClientId", "Id": "foo"} or {"ManagedIdentityIdType": "SystemAssigned", "Id": null}. The following app can load managed identity configuration dynamically:
import json, os, msal, requests
config = os.getenv("MY_MANAGED_IDENTITY_CONFIG")
assert config, "An ENV VAR with value should exist"
client = msal.ManagedIdentityClient(
json.loads(config),
http_client=requests.Session(),
)
token = client.acquire_token_for_client("resource")
The result will be automatically cached. Subsequent calls will automatically search from cache first.
Optional. It is a string representation of a JSON object (which contains lists of claims being requested).
The tenant admin may choose to revoke all Managed Identity tokens, and then a claims challenge will be returned by the target resource, as a claims_challenge directive in the www-authenticate header, even if the app developer did not opt in for the "CP1" client capability. Upon receiving a claims_challenge, MSAL will skip a token cache read, and will attempt to acquire a new token.
NOTE:
This is a service-side behavior that cannot be changed by this library. Azure VM docs
Microsoft
2025, Microsoft
| April 28, 2025 | 1.32.3 |