diff --git a/msal/application.py b/msal/application.py index 98050535..1769352b 100644 --- a/msal/application.py +++ b/msal/application.py @@ -21,7 +21,7 @@ # The __init__.py will import this. Not the other way around. -__version__ = "1.5.0" +__version__ = "1.5.1" logger = logging.getLogger(__name__) @@ -193,6 +193,18 @@ def __init__( Default value is None, means it will not be passed to Microsoft. :param list[str] client_capabilities: (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 can then issue CAE access tokens to resources + knowing when the resource emits *claims challenge* + the client will be capable to handle. + + Implementation details: + Client capability is implemented using "claims" parameter on the wire, + for now. MSAL will combine them into `claims parameter `_. + :param str prompt: By default, no prompt value will be sent, not even "none". You will have to specify a value explicitly. @@ -735,6 +752,11 @@ def _acquire_token_silent_by_finding_specific_refresh_token( response = client.obtain_token_by_refresh_token( entry, rt_getter=lambda token_item: token_item["secret"], on_removing_rt=rt_remover or self.token_cache.remove_rt, + on_obtaining_tokens=lambda event: self.token_cache.add(dict( + event, + environment=authority.instance, + skip_account_creation=True, # To honor a concurrent remove_account() + )), scope=scopes, headers={ CLIENT_REQUEST_ID: correlation_id or _get_new_correlation_id(), @@ -936,7 +958,8 @@ def _acquire_token_by_username_password_federated( "https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication") logger.debug("wstrust_endpoint = %s", wstrust_endpoint) wstrust_result = wst_send_request( - username, password, user_realm_result.get("cloud_audience_urn"), + username, password, + user_realm_result.get("cloud_audience_urn", "urn:federation:MicrosoftOnline"), wstrust_endpoint.get("address", # Fallback to an AAD supplied endpoint user_realm_result.get("federation_active_auth_url")), diff --git a/msal/oauth2cli/oauth2.py b/msal/oauth2cli/oauth2.py index 1d9c21d5..90c1d31b 100644 --- a/msal/oauth2cli/oauth2.py +++ b/msal/oauth2cli/oauth2.py @@ -462,6 +462,7 @@ def __init__(self, def _obtain_token( self, grant_type, params=None, data=None, also_save_rt=False, + on_obtaining_tokens=None, *args, **kwargs): _data = data.copy() # to prevent side effect resp = super(Client, self)._obtain_token( @@ -481,7 +482,7 @@ def _obtain_token( # but our obtain_token_by_authorization_code(...) encourages # app developer to still explicitly provide a scope here. scope = _data.get("scope") - self.on_obtaining_tokens({ + (on_obtaining_tokens or self.on_obtaining_tokens)({ "client_id": self.client_id, "scope": scope, "token_endpoint": self.configuration["token_endpoint"], @@ -495,6 +496,7 @@ def obtain_token_by_refresh_token(self, token_item, scope=None, rt_getter=lambda token_item: token_item["refresh_token"], on_removing_rt=None, on_updating_rt=None, + on_obtaining_tokens=None, **kwargs): # type: (Union[str, dict], Union[str, list, set, tuple], Callable) -> dict """This is an overload which will trigger token storage callbacks. diff --git a/msal/token_cache.py b/msal/token_cache.py index 6884075d..b7ebbb99 100644 --- a/msal/token_cache.py +++ b/msal/token_cache.py @@ -126,6 +126,8 @@ def __add(self, event, now=None): environment = realm = None if "token_endpoint" in event: _, environment, realm = canonicalize(event["token_endpoint"]) + if "environment" in event: # Always available unless in legacy test cases + environment = event["environment"] # Set by application.py response = event.get("response", {}) data = event.get("data", {}) access_token = response.get("access_token") @@ -170,7 +172,7 @@ def __add(self, event, now=None): at["key_id"] = data.get("key_id") self.modify(self.CredentialType.ACCESS_TOKEN, at, at) - if client_info: + if client_info and not event.get("skip_account_creation"): account = { "home_account_id": home_account_id, "environment": environment,