diff --git a/README.md b/README.md index e46efc0..680630e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Validate content links](https://github.com/MicrosoftDocs/microsoft-authentication-library-for-python/actions/workflows/linkchecker.yml/badge.svg)](https://github.com/MicrosoftDocs/microsoft-authentication-library-for-python/actions/workflows/linkchecker.yml) -This is the documentation repository for Microsoft Authentication Library (MSAL) for .NET. If you are looking for the library, refer to [AzureAD/microsoft-authentication-library-for-python](https://github.com/AzureAD/microsoft-authentication-library-for-python). +This is the documentation repository for Microsoft Authentication Library (MSAL) for Python. If you are looking for the library, refer to [AzureAD/microsoft-authentication-library-for-python](https://github.com/AzureAD/microsoft-authentication-library-for-python). ## Contributions diff --git a/msal-python-conceptual/TOC.yml b/msal-python-conceptual/TOC.yml index 35dd4c2..ac36c72 100644 --- a/msal-python-conceptual/TOC.yml +++ b/msal-python-conceptual/TOC.yml @@ -13,19 +13,19 @@ - name: Using MSAL Python with Web Account Manager href: advanced/wam.md - name: Migrate to MSAL Python - href: /azure/active-directory/develop/migrate-python-adal-msal + href: advanced/migrate-python-adal-msal.md - name: Logging - href: /azure/active-directory/develop/msal-logging-python + href: advanced/msal-logging-python.md - name: Handle errors and exceptions - href: /azure/active-directory/develop/msal-error-handling-python + href: advanced/msal-error-handling-python.md - name: Conditional access href: advanced/conditional-access.md - name: Token cache serialization - href: /azure/active-directory/develop/msal-python-token-cache-serialization + href: advanced/msal-python-token-cache-serialization.md - name: Developing an Azure AD B2C app with MSAL Python href: advanced/aad-b2c.md - name: Active Directory Federation Services (ADFS) Support - href: /azure/active-directory/develop/msal-python-adfs-support + href: advanced/msal-python-adfs-support.md - name: National clouds href: /azure/active-directory/develop/msal-national-cloud?tabs=python - name: Username and password authentication @@ -34,10 +34,6 @@ href: advanced/client-credentials.md - name: Handle SameSite cookie changes in Chrome href: /azure/active-directory/develop/howto-handle-samesite-cookie-changes-chrome-browser?tabs=python - - name: Migrate existing refresh tokens into MSAL Python - href: advanced/migrate.md - - name: Logging - href: advanced/logging.md - name: Instance metadata caching href: advanced/instance-metadata-caching.md - name: Client capabilities diff --git a/msal-python-conceptual/advanced/migrate-python-adal-msal.md b/msal-python-conceptual/advanced/migrate-python-adal-msal.md new file mode 100644 index 0000000..61f1d02 --- /dev/null +++ b/msal-python-conceptual/advanced/migrate-python-adal-msal.md @@ -0,0 +1,108 @@ +--- +title: Python ADAL to MSAL migration guide +description: Learn how to migrate your Azure Active Directory Authentication Library (ADAL) Python app to the Microsoft Authentication Library (MSAL) for Python. +--- + +# ADAL to MSAL migration guide for Python + +This article highlights changes you need to make to migrate an app that uses the Azure Active Directory Authentication Library (ADAL) to use the Microsoft Authentication Library (MSAL). + +You can learn more about MSAL and get started with an [overview of the Microsoft Authentication Library for Python](../index.md). + +## Difference highlights + +ADAL works with the Azure Active Directory (Azure AD) v1.0 endpoint. The Microsoft Authentication Library (MSAL) works with the Microsoft identity platform--formerly known as the Azure Active Directory v2.0 endpoint. The Microsoft identity platform differs from Azure AD v1.0 in that it: + +Supports: + +- Work and school accounts (Azure AD provisioned accounts) +- Personal accounts (such as Outlook.com or Hotmail.com) +- Your customers who bring their own email or social identity (such as LinkedIn, Facebook, Google) via the Azure AD B2C offering + +- Is standards compatible with: + - OAuth v2.0 + - OpenID Connect (OIDC) + +For more information about MSAL, see [MSAL overview](../index.md). + +### Scopes not resources + +ADAL Python acquires tokens for resources, but MSAL Python acquires tokens for scopes. The API surface in MSAL Python doesn't have resource parameter anymore. You would need to provide scopes as a list of strings that declare the desired permissions and resources that are requested. To see some example of scopes, see [Microsoft Graph's scopes](/graph/permissions-reference). + +You can add the `/.default` scope suffix to the resource to help migrate your apps from the v1.0 endpoint (ADAL) to the Microsoft identity platform (MSAL). For example, for the resource value of `https://graph.microsoft.com`, the equivalent scope value is `https://graph.microsoft.com/.default`. If the resource isn't in the URL form, but a resource ID of the form `XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX`, you can still use the scope value as `XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX/.default`. + +For more details about the different types of scopes, refer to [Permissions and consent in the Microsoft identity platform](/azure/active-directory/develop/permissions-consent-overview) and the [Scopes for a Web API accepting v1.0 tokens](/azure/active-directory/develop/msal-v1-app-scopes) articles. + +### Error handling + +ADAL for Python uses the exception `AdalError` to indicate that there's been a problem. MSAL for Python typically uses error codes, instead. For more information, see [MSAL for Python error handling](msal-error-handling-python.md). + +### API changes + +The following table lists an API in ADAL for Python, and the one to use in its place in MSAL for Python: + +| ADAL for Python API | MSAL for Python API | +| ------------------- | ------------------- | +| [AuthenticationContext](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext) | or | +| N/A | | +| N/A | | +| N/A | | +| [acquire_token_with_authorization_code()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token_with_authorization_code) | | +| [acquire_token()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token) | | +| [acquire_token_with_refresh_token()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token_with_refresh_token) | These two helpers are intended to be used during [migration](#migrate-existing-refresh-tokens-for-msal-python) only: | +| [acquire_user_code()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_user_code) | | +| [acquire_token_with_device_code()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token_with_device_code) and [cancel_request_to_get_token_with_device_code()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.cancel_request_to_get_token_with_device_code) | | +| [acquire_token_with_username_password()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token_with_username_password) | | +| [acquire_token_with_client_credentials()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token_with_client_credentials) and [acquire_token_with_client_certificate()](https://adal-python.readthedocs.io/en/latest/#adal.AuthenticationContext.acquire_token_with_client_certificate) | | +| N/A | | +| [TokenCache()](https://adal-python.readthedocs.io/en/latest/#adal.TokenCache) | | +| N/A | Cache with persistence, available from [MSAL Extensions](https://github.com/marstr/original-microsoft-authentication-extensions-for-python) | + +## Migrate existing refresh tokens for MSAL Python + +MSAL abstracts the concept of refresh tokens. MSAL Python provides an in-memory token cache by default so that you don't need to store, lookup, or update refresh tokens. Users will also see fewer sign-in prompts because refresh tokens can usually be updated without user intervention. For more information about the token cache, see [Custom token cache serialization in MSAL for Python](msal-python-token-cache-serialization.md). + +The following code will help you migrate your refresh tokens managed by another OAuth2 library (including but not limited to ADAL Python) to be managed by MSAL for Python. One reason for migrating those refresh tokens is to prevent existing users from needing to sign in again when you migrate your app to MSAL for Python. + +The method for migrating a refresh token is to use MSAL for Python to acquire a new access token using the previous refresh token. When the new refresh token is returned, MSAL for Python will store it in the cache. +Since MSAL Python 1.3.0, we provide an API inside MSAL for this purpose. +Please refer to the following code snippet, quoted from +[a completed sample of migrating refresh tokens with MSAL Python](https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/1.3.0/sample/migrate_rt.py#L28-L67) + +```python +import msal +def get_preexisting_rt_and_their_scopes_from_elsewhere(): + # Maybe you have an ADAL-powered app like this + # https://github.com/AzureAD/azure-activedirectory-library-for-python/blob/1.2.3/sample/device_code_sample.py#L72 + # which uses a resource rather than a scope, + # you need to convert your v1 resource into v2 scopes + # See https://learn.microsoft.com/azure/active-directory/develop/migrate-python-adal-msal#scopes-not-resources + # You may be able to append "/.default" to your v1 resource to form a scope + # See https://learn.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#the-default-scope + + # Or maybe you have an app already talking to the Microsoft identity platform, + # powered by some 3rd-party auth library, and persist its tokens somehow. + + # Either way, you need to extract RTs from there, and return them like this. + return [ + ("old_rt_1", ["scope1", "scope2"]), + ("old_rt_2", ["scope3", "scope4"]), + ] + + +# We will migrate all the old RTs into a new app powered by MSAL +app = msal.PublicClientApplication( + "client_id", authority="...", + # token_cache=... # Default cache is in memory only. + # You can learn how to use SerializableTokenCache from + # https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache + ) + +# We choose a migration strategy of migrating all RTs in one loop +for old_rt, scopes in get_preexisting_rt_and_their_scopes_from_elsewhere(): + result = app.acquire_token_by_refresh_token(old_rt, scopes) + if "error" in result: + print("Discarding unsuccessful RT. Error: ", json.dumps(result, indent=2)) + +print("Migration completed") +``` \ No newline at end of file diff --git a/msal-python-conceptual/advanced/msal-error-handling-python.md b/msal-python-conceptual/advanced/msal-error-handling-python.md new file mode 100644 index 0000000..18fbaaa --- /dev/null +++ b/msal-python-conceptual/advanced/msal-error-handling-python.md @@ -0,0 +1,79 @@ +--- +title: Handle errors and exceptions in MSAL for Python +description: Learn how to handle errors and exceptions, Conditional Access claims challenges, and retries in MSAL for Python applications. +--- + +# Handle errors and exceptions in MSAL for Python + +In MSAL for Python, most errors are conveyed as a return value from the API call. The error is represented as a dictionary containing the JSON response from the Microsoft identity platform. + +* A successful response contains the `"access_token"` key. The format of the response is defined by the OAuth2 protocol. For more information, see [5.1 Successful Response](https://tools.ietf.org/html/rfc6749#section-5.1) +* An error response contains `"error"` and usually `"error_description"`. The format of the response is defined by the OAuth2 protocol. For more information, see [5.2 Error Response](https://tools.ietf.org/html/rfc6749#section-5.2) + +When an error is returned, the `"error"` key contains a machine-readable code. If the `"error"` is, for example, an `"interaction_required"`, you may prompt the user to provide additional information to complete the authentication process. If the `"error"` is `"invalid_grant"`, you may prompt the user to reenter their credentials. The following snippet is an example of error handling in MSAL for Python. + +```python + +from msal import ConfidentialClientApplication + +authority_url = "https://login.microsoftonline.com/your_tenant_id" +client_id = "your_client_id" +client_secret = "your_client_secret" +scopes = ["https://graph.microsoft.com/.default"] + +app = ConfidentialClientApplication(client_id, authority=authority_url, client_credential=client_secret) + +result = app.acquire_token_silent(scopes=scopes, account=None) + +if not result: + result = app.acquire_token_silent(scopes=scopes) + +if "access_token" in result: + print("Access token: %s" % result["access_token"]) +else: + print("Error: %s" % result.get("error")) + +``` + +When an error is returned, the `"error_description"` key also contains a human-readable message, and there is typically also an `"error_code"` key which contains a machine-readable Microsoft identity platform error code. For more information about the various Microsoft identity platform error codes, see [Authentication and authorization error codes](/azure/active-directory/develop/reference-error-codes). + +In MSAL for Python, exceptions are rare because most errors are handled by returning an error value. The `ValueError` exception is only thrown when there's an issue with how you're attempting to use the library, such as when API parameter(s) are malformed. + +## Conditional Access and claims challenges + +When getting tokens silently, your application may receive errors when a [Conditional Access claims challenge](/azure/active-directory/develop/v2-conditional-access-dev-guide) such as MFA policy is required by an API you're trying to access. + +The pattern for handling this error is to interactively acquire a token using MSAL. This prompts the user and gives them the opportunity to satisfy the required Conditional Access policy. + +In certain cases when calling an API requiring Conditional Access, you can receive a claims challenge in the error from the API. For instance if the Conditional Access policy is to have a managed device (Intune) the error will be something like [AADSTS53000: Your device is required to be managed to access this resource](/azure/active-directory/develop/reference-error-codes) or something similar. In this case, you can pass the claims in the acquire token call so that the user is prompted to satisfy the appropriate policy. + + +## Retrying after errors and exceptions + +MSAL makes HTTP calls to the Azure AD service, and occasionally failures can occur. +For example the network can go down or the server is overloaded. + +MSAL Python 1.11+ automatically performs one retry attempt for you. +You may customize this behavior by following +[the `http_client` customization instructions](xref:msal.application.ConfidentialClientApplication). + +### HTTP 429 + +When the Service Token Server (STS) is overloaded with too many requests, +it returns HTTP error 429 with a hint about how long until you can try again in the `Retry-After` response field. + +Your app was expected to throttle the subsequent requests, and only retry after the specified period. + +MSAL Python 1.16+ makes it easy for you to retry an authentication request on-demand +(for example, whenever the end-user clicks the sign-in button again), +MSAL Python 1.16+ would automatically throttle those retry attempts by returning same error response from an HTTP cache, +and only sending out a real HTTP call when that call is attempted after the specified period. + +By default, this throttle mechanism works by saving throttle information into a built-in in-memory HTTP cache. +You may provide your own `dict`-like object as the HTTP cache, which you can control how to persist its content. +See [MSAL Python API documentation](xref:msal.application.PublicClientApplication) +for more details. + +## Next steps + +- Consider enabling [Logging in MSAL for Python](msal-logging-python.md) to help you diagnose and debug issues. diff --git a/msal-python-conceptual/advanced/msal-logging-python.md b/msal-python-conceptual/advanced/msal-logging-python.md new file mode 100644 index 0000000..b00aee0 --- /dev/null +++ b/msal-python-conceptual/advanced/msal-logging-python.md @@ -0,0 +1,92 @@ +--- +title: Logging errors and exceptions in MSAL for Python +description: Learn how to log errors and exceptions in MSAL for Python +--- + +# Logging in MSAL for Python + +The Microsoft Authentication Library (MSAL) apps generate log messages that can help diagnose issues. An app can configure logging with a few lines of code, and have custom control over the level of detail and whether or not personal and organizational data is logged. We recommend you create an MSAL logging implementation and provide a way for users to submit logs when they have authentication issues. + +Logging in MSAL Python is designed to use the standard Python logging mechanisms, so all your previous knowledge of Python logging applies to MSAL Python. + +* By default, the logging in any Python script is turned off. If you want to enable debug logging for ALL modules in your entire Python script, you use `logging.basicConfig(level=logging.DEBUG)`. +* Most of the MSAL Python logs are already in debug level, which would be turned off by default. But if you want to enable debug logging to debug the OTHER modules in your Python script, therefore want to silence MSAL, you simply turn off the logger used by MSAL Python: `logging.getLogger("msal").setLevel(logging.WARN)`. +* MSAL Python does not log Personal Identifiable Information (PII). So there is not even a turn-on-PII-logging toggle in MSAL Python. App developers could still use standard Python logging to log whatever content. By doing so, the app takes responsibility for safely handling highly sensitive data and following regulatory requirements. + + +## Logging levels + +MSAL provides several levels of logging detail: + +- `LogAlways`: No level filtering is done on this log level. Log messages of all levels will be logged. +- `Critical`: Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires immediate attention. +- `Error`: Indicates something has gone wrong and an error was generated. Used for debugging and identifying problems. +- `Warning`: There hasn't necessarily been an error or failure, but are intended for diagnostics and pinpointing problems. +- `Informational`: MSAL will log events intended for informational purposes not necessarily intended for debugging. +- `Verbose` (Default): MSAL logs the full details of library behavior. + +> [!NOTE] +> Not all log levels are available for all MSAL libraries. + +## Personal and organizational data + +By default, the MSAL logger doesn't capture any highly sensitive personal or organizational data. The library provides the option to enable logging personal and organizational data if you decide to do so. + +The following sections provide more details about MSAL error logging for your application. + +## MSAL for Python logging + +Logging in MSAL for Python leverages the [logging module in the Python standard library](https://docs.python.org/3/library/logging.html). You can configure MSAL logging as follows (and see it in action in the [username_password_sample](https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/1.0.0/sample/username_password_sample.py#L31L32)): + +### Enable debug logging for all modules + +By default, the logging in any Python script is turned off. If you want to enable verbose logging for **all** Python modules in your script, use `logging.basicConfig` with a level of `logging.DEBUG`: + +```python +import logging + +logging.basicConfig(level=logging.DEBUG) +``` + +This will print all log messages given to the logging module to the standard output. + +### Configure MSAL logging level + +You can configure the logging level of the MSAL for Python log provider by using the `logging.getLogger()` method with the logger name `"msal"`: + +```python +import logging + +logging.getLogger("msal").setLevel(logging.WARN) +``` + +### Configure MSAL logging with Azure App Insights + +Python logs are given to a log handler, which by default is the `StreamHandler`. To send MSAL logs to an Application Insights with an Instrumentation Key, use the `AzureLogHandler` provided by the `opencensus-ext-azure` library. + +To install, `opencensus-ext-azure` add the `opencensus-ext-azure` package from PyPI to your dependencies or pip install: + +```console +pip install opencensus-ext-azure +``` + +Then change the default handler of the `"msal"` log provider to an instance of `AzureLogHandler` with an instrumentation key set in the `APP_INSIGHTS_KEY` environment variable: + +```python +import logging +import os + +from opencensus.ext.azure.log_exporter import AzureLogHandler + +APP_INSIGHTS_KEY = os.getenv('APP_INSIGHTS_KEY') + +logging.getLogger("msal").addHandler(AzureLogHandler(connection_string='InstrumentationKey={0}'.format(APP_INSIGHTS_KEY))) +``` + +### Personal and organizational data in Python + +MSAL for Python does not log personal data or organizational data. There is no property to turn personal or organization data logging on or off. + +You can use standard Python logging to log whatever you want, but you are responsible for safely handling sensitive data and following regulatory requirements. + +For more information about logging in Python, please refer to Python's [Logging: how-to](https://docs.python.org/3/howto/logging.html#logging-basic-tutorial). \ No newline at end of file diff --git a/msal-python-conceptual/advanced/msal-python-adfs-support.md b/msal-python-conceptual/advanced/msal-python-adfs-support.md new file mode 100644 index 0000000..87b7c58 --- /dev/null +++ b/msal-python-conceptual/advanced/msal-python-adfs-support.md @@ -0,0 +1,41 @@ +--- +title: Azure AD FS support (MSAL Python) +description: Learn about Active Directory Federation Services (AD FS) support in the Microsoft Authentication Library for Python +--- + +# Active Directory Federation Services support in MSAL for Python + +Active Directory Federation Services (AD FS) in Windows Server enables you to add OpenID Connect and OAuth 2.0 based authentication and authorization to your apps by using the Microsoft Authentication Library (MSAL) for Python. Using the MSAL for Python library, your app can authenticate users directly against AD FS. For more information about scenarios, see [AD FS Scenarios for Developers](/windows-server/identity/ad-fs/ad-fs-development). + +There are usually two ways of authenticating against AD FS: + +- MSAL Python talks to Azure Active Directory, which itself is federated with other identity providers. The federation happens through AD FS. MSAL Python connects to Azure AD, which signs in users that are managed in Azure AD (managed users) or users managed by another identity provider such as AD FS (federated users). MSAL Python doesn't know that a user is federated. It simply talks to Azure AD. The [authority](/azure/active-directory/develop/msal-client-application-configuration#authority) you use in this case is the usual authority (authority host name + tenant, common, or organizations). +- MSAL Python talks directly to an AD FS authority. This is only supported by AD FS 2019 and later. + +## Connect to Active Directory federated with AD FS + +### Acquire a token interactively for a federated user + +The following applies whether you connect directly to Active Directory Federation Services (AD FS) or through Active Directory. + +When you call `acquire_token_by_authorization_code` or `acquire_token_by_device_flow`, the user experience is typically as follows: + +1. The user enters their account ID. +2. Azure AD displays briefly the message "Taking you to your organization's page" and the user is redirected to the sign-in page of the identity provider. The sign-in page is usually customized with the logo of the organization. + +The supported AD FS versions in this federated scenario are: +- Active Directory Federation Services FS v2 +- Active Directory Federation Services v3 (Windows Server 2012 R2) +- Active Directory Federation Services v4 (AD FS 2016) + +### Acquire a token via username and password + +The following applies whether you connect directly to Active Directory Federation Services (AD FS) or through Active Directory. + +When you acquire a token using `acquire_token_by_username_password`, MSAL Python gets the identity provider to contact based on the username. MSAL Python gets a [SAML 1.1 token](/azure/active-directory/develop/reference-saml-tokens) from the identity provider, which it then provides to Azure AD which returns the JSON Web Token (JWT). + +## Connecting directly to AD FS + +When you connect directory to AD FS, the authority you'll want to use to build your application will be something like `https://somesite.contoso.com/adfs/` + +MSAL Python supports ADFS 2019, but does not support a direct connection to ADFS 2016 or ADFS v2. Once you have upgraded your on-premises system to ADFS 2019, you can use MSAL Python. \ No newline at end of file diff --git a/msal-python-conceptual/advanced/msal-python-token-cache-serialization.md b/msal-python-conceptual/advanced/msal-python-token-cache-serialization.md new file mode 100644 index 0000000..82b3e74 --- /dev/null +++ b/msal-python-conceptual/advanced/msal-python-token-cache-serialization.md @@ -0,0 +1,24 @@ +--- +title: Custom token cache serialization (MSAL Python) +description: Learn how to serialize token cache using MSAL for Python +--- + +# Custom token cache serialization in MSAL for Python + +In Microsoft Authentication Library (MSAL) for Python, an in-memory token cache that persists for the duration of the app session, is provided by default when you create an instance of . + +Serialization of the token cache, so that different sessions of your app can access it, isn't provided "out of the box." MSAL for Python can be used in app types that don't have access to the file system--such as Web apps. To have a persistent token cache in an app that uses MSAL for Python, you must provide custom token cache serialization. + +The strategies for serializing the token cache differ depending on whether you're writing a public client application (Desktop), or a confidential client application (web app, web API, or daemon app). + +## Token cache for a public client application + +Public client applications run on a user's device and manage tokens for a single user. In this case, you could serialize the entire cache into a file. Remember to provide file locking if your app, or another app, can access the cache concurrently. For a simple example of how to serialize a token cache to a file without locking, see the example in the class reference documentation. + +## Token cache for a Web app (confidential client application) + +For web apps or web APIs, you might use the session, or a Redis cache, or a database to store the token cache. There should be one token cache per user (per account) so ensure that you serialize the token cache per account. + +## Next steps + +See [ms-identity-python-webapp](https://github.com/Azure-Samples/ms-identity-python-webapp/blob/0.3.0/app.py#L66-L74) for an example of how to use the token cache for a Windows or Linux Web app or web API. The example is for a web app that calls the Microsoft Graph API. \ No newline at end of file