From 361f90c23b32f02685fad7c3af0769c16171003a Mon Sep 17 00:00:00 2001 From: Ray Luo Date: Thu, 27 Aug 2020 19:11:19 -0700 Subject: [PATCH 1/8] Cache tokens by specified environment, not by OIDC Discovery --- msal/application.py | 3 ++- msal/token_cache.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/msal/application.py b/msal/application.py index 085914c4..dd65ac6c 100644 --- a/msal/application.py +++ b/msal/application.py @@ -246,7 +246,8 @@ def _build_client(self, client_credential, authority): default_body=default_body, client_assertion=client_assertion, client_assertion_type=client_assertion_type, - on_obtaining_tokens=self.token_cache.add, + on_obtaining_tokens=lambda event: self.token_cache.add(dict( + event, environment=authority.instance)), on_removing_rt=self.token_cache.remove_rt, on_updating_rt=self.token_cache.update_rt) diff --git a/msal/token_cache.py b/msal/token_cache.py index 6884075d..83fc1891 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") From e3d5041a459195c81639802304265a093a81cdee Mon Sep 17 00:00:00 2001 From: Ray Luo Date: Thu, 3 Sep 2020 17:39:34 -0700 Subject: [PATCH 2/8] Document some offline info into this code base --- msal/application.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/msal/application.py b/msal/application.py index 98050535..187a95e7 100644 --- a/msal/application.py +++ b/msal/application.py @@ -193,6 +193,16 @@ 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. + + Client capability is implemented using ‘claims’ parameter, for now. MSAL will combine them into `claims parameter Date: Fri, 4 Sep 2020 11:49:57 -0700 Subject: [PATCH 3/8] Document why recommend against using Implicit Grant, to avoid future https://github.com/AzureAD/microsoft-authentication-library-for-python/pull/249 --- msal/application.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/msal/application.py b/msal/application.py index 187a95e7..35820c02 100644 --- a/msal/application.py +++ b/msal/application.py @@ -285,7 +285,7 @@ def get_authorization_request_url( login_hint=None, # type: Optional[str] state=None, # Recommended by OAuth2 for CSRF protection redirect_uri=None, - response_type="code", # Can be "token" if you use Implicit Grant + response_type="code", # Could be "token" if you use Implicit Grant prompt=None, nonce=None, domain_hint=None, # type: Optional[str] @@ -302,7 +302,11 @@ def get_authorization_request_url( Address to return to upon receiving a response from the authority. :param str response_type: Default value is "code" for an OAuth2 Authorization Code grant. - You can use other content such as "id_token". + + You could use other content such as "id_token" or "token", + which would trigger an Implicit Grant, but that is + `not recommended `_. + :param str prompt: By default, no prompt value will be sent, not even "none". You will have to specify a value explicitly. From 7d38d7ad95629cdb29cd9db3931284dc1d53752d Mon Sep 17 00:00:00 2001 From: Ray Luo Date: Thu, 17 Sep 2020 12:24:47 -0700 Subject: [PATCH 4/8] Remove non-ascii character in prevoius commit --- msal/application.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/msal/application.py b/msal/application.py index 187a95e7..b8318593 100644 --- a/msal/application.py +++ b/msal/application.py @@ -202,7 +202,9 @@ def __init__( knowing when the resource emits *claims challenge* the client will be capable to handle. - Client capability is implemented using ‘claims’ parameter, for now. + Implementation details: + Client capability is implemented using "claims" parameter on the wire, + for now. MSAL will combine them into `claims parameter Date: Mon, 21 Sep 2020 16:49:32 -0700 Subject: [PATCH 5/8] Backporting change from ADAL PR 240 --- msal/application.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/msal/application.py b/msal/application.py index 2c2959bb..9d22eace 100644 --- a/msal/application.py +++ b/msal/application.py @@ -953,7 +953,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")), From af984e7107300612f4e687f716d949d2c0193ee7 Mon Sep 17 00:00:00 2001 From: Ray Luo Date: Sat, 3 Oct 2020 15:58:25 -0700 Subject: [PATCH 6/8] Experiment 1 --- msal/application.py | 5 +++++ msal/oauth2cli/oauth2.py | 4 +++- msal/token_cache.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/msal/application.py b/msal/application.py index 9d22eace..2b3c947a 100644 --- a/msal/application.py +++ b/msal/application.py @@ -752,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, + add_account=False, # To honor a concurrent remove_account() + )), scope=scopes, headers={ CLIENT_REQUEST_ID: correlation_id or _get_new_correlation_id(), 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 83fc1891..e1d8705b 100644 --- a/msal/token_cache.py +++ b/msal/token_cache.py @@ -172,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 event.get("add_account") is not False: account = { "home_account_id": home_account_id, "environment": environment, From 0d5719d60863ecddd8cf614aa8d69db87e70e6b1 Mon Sep 17 00:00:00 2001 From: Ray Luo Date: Wed, 14 Oct 2020 17:32:00 -0700 Subject: [PATCH 7/8] Skip account creation during acquire_token_silent() --- msal/application.py | 2 +- msal/token_cache.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/msal/application.py b/msal/application.py index 2b3c947a..410086d1 100644 --- a/msal/application.py +++ b/msal/application.py @@ -755,7 +755,7 @@ def _acquire_token_silent_by_finding_specific_refresh_token( on_obtaining_tokens=lambda event: self.token_cache.add(dict( event, environment=authority.instance, - add_account=False, # To honor a concurrent remove_account() + skip_account_creation=True, # To honor a concurrent remove_account() )), scope=scopes, headers={ diff --git a/msal/token_cache.py b/msal/token_cache.py index e1d8705b..b7ebbb99 100644 --- a/msal/token_cache.py +++ b/msal/token_cache.py @@ -172,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 and event.get("add_account") is not False: + if client_info and not event.get("skip_account_creation"): account = { "home_account_id": home_account_id, "environment": environment, From 68e5af474040c754c79706d4b4a07d375398eee1 Mon Sep 17 00:00:00 2001 From: Ray Luo Date: Tue, 20 Oct 2020 16:54:06 -0700 Subject: [PATCH 8/8] MSAL Python 1.5.1 Bumping version number --- msal/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msal/application.py b/msal/application.py index 410086d1..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__)