diff --git a/sdk/identity/azure-identity/samples/README.md b/sdk/identity/azure-identity/samples/README.md index 40f659e46392..137910e900a2 100644 --- a/sdk/identity/azure-identity/samples/README.md +++ b/sdk/identity/azure-identity/samples/README.md @@ -34,6 +34,6 @@ pip install azure-identity azure-keyvault-certificates azure-keyvault-secrets | File | Description | |-------------|-------------| | control_interactive_prompts.py | demonstrates controlling when interactive credentials prompt for user interaction | -| custom_credentials.py | demonstrates custom credential implementation | +| custom_credentials.py | demonstrates custom credential implementations using existing access tokens and an MSAL client | | key_vault_cert.py | demonstrates authenticating with a Key Vault certificate | -| user_authentication.py | demonstrates user authentication API for applications | +| user_authentication.py | demonstrates user authentication and token cache persistence API for applications | diff --git a/sdk/identity/azure-identity/samples/custom_credentials.py b/sdk/identity/azure-identity/samples/custom_credentials.py index 1b1248c60fcb..b55f843c8068 100644 --- a/sdk/identity/azure-identity/samples/custom_credentials.py +++ b/sdk/identity/azure-identity/samples/custom_credentials.py @@ -2,11 +2,14 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -"""Demonstrates custom credential implementation""" +"""Demonstrates custom credential implementations using existing access tokens and an MSAL client""" +import time from typing import TYPE_CHECKING from azure.core.credentials import AccessToken +from azure.identity import AuthenticationRequiredError, AzureAuthorityHosts +import msal if TYPE_CHECKING: from typing import Any, Union @@ -32,3 +35,25 @@ def get_token(self, *scopes, **kwargs): """get_token is the only method a credential must implement""" return self._token + + +class MsalTokenCredential(object): + """Uses an MSAL client directly to obtain access tokens with an interactive flow.""" + def __init__(self, tenant_id, client_id): + # type: (str, str) -> None + self._app = msal.PublicClientApplication( + client_id=client_id, authority="https://{}/{}".format(AzureAuthorityHosts.AZURE_PUBLIC_CLOUD, tenant_id) + ) + + def get_token(self, *scopes, **kwargs): + # type: (*str, **Any) -> AccessToken + """get_token is the only method a credential must implement""" + + now = int(time.time()) + result = self._app.acquire_token_interactive(list(scopes), **kwargs) + + try: + return AccessToken(result["access_token"], now + int(result["expires_in"])) + except: + print("\nFailed to get a valid access token") + raise AuthenticationRequiredError(scopes) diff --git a/sdk/identity/azure-identity/samples/user_authentication.py b/sdk/identity/azure-identity/samples/user_authentication.py index 9a461c53a439..e74d965b4d87 100644 --- a/sdk/identity/azure-identity/samples/user_authentication.py +++ b/sdk/identity/azure-identity/samples/user_authentication.py @@ -25,6 +25,7 @@ # The 'authenticate' method begins interactive authentication. Call it whenever it's convenient # for your application to authenticate a user. It returns a record of the authentication. record = credential.authenticate() +print("\nAuthenticated first credential") # The record contains no authentication secrets. You can serialize it to JSON for storage. record_json = record.serialize() @@ -33,6 +34,7 @@ # without prompting for authentication again. client = SecretClient(VAULT_URL, credential) secret_names = [s.name for s in client.list_properties_of_secrets()] +print("\nCompleted request with first credential") # An authentication record stored by your application enables other credentials to access data from # past authentications. If the cache contains sufficient data, this eliminates the need for your @@ -45,3 +47,14 @@ # This request should also succeed without prompting for authentication. client = SecretClient(VAULT_URL, new_credential) secret_names = [s.name for s in client.list_properties_of_secrets()] +print("\nCompleted request with credential using shared cache") + +# To isolate the token cache from other applications, you can provide a cache name to TokenCachePersistenceOptions. +separate_cache_credential = InteractiveBrowserCredential( + cache_persistence_options=TokenCachePersistenceOptions(name="my_app"), authentication_record=deserialized_record +) + +# This request should prompt for authentication since the credential is using a separate cache. +client = SecretClient(VAULT_URL, separate_cache_credential) +secret_names = [s.name for s in client.list_properties_of_secrets()] +print("\nCompleted request with credential using separate cache")