Hello community,

here is the log from the commit of package python-msal for openSUSE:Factory 
checked in at 2020-10-02 17:27:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-msal (Old)
 and      /work/SRC/openSUSE:Factory/.python-msal.new.4249 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-msal"

Fri Oct  2 17:27:23 2020 rev:3 rq:833121 version:1.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-msal/python-msal.changes  2020-06-17 
14:51:17.369176790 +0200
+++ /work/SRC/openSUSE:Factory/.python-msal.new.4249/python-msal.changes        
2020-10-02 17:28:11.990412164 +0200
@@ -1,0 +2,29 @@
+Tue Sep  8 19:53:50 UTC 2020 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 1.5.0
+  + Added support for setting client capabilities to enable
+    CAE(Continuous Access Evaluation) (#240, #174)
+  + Device code endpoint is now fetched from open-id configuration,
+    if available. (#245, #242)
+  + Fixes in test cases (#239, #211)
+
+-------------------------------------------------------------------
+Fri Aug 28 13:29:30 UTC 2020 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 1.4.3
+  + Bugfix: A side effect in previous release prevented reading some
+    tokens from a different authority alias (#235, #236)
+- from version 1.4.2
+  + Bugfix: Changed case of messageID in WS-Trust Requests (#228 , #230 )
+  + Bugfix: Removed content-type header sent in request to Mex endpoint (#226 
, #227 )
+  + Bugfix: Bypasses cache lookup for authority alias if no refresh token 
found (#223, #225 )
+- from version 1.4.1
+  + Reverts Application Initializer will not send network requests
+    introduced in MSAL Python 1.4.0 (#205, #216, #187)
+- from version 1.4.0
+  + Enhancement: Application initializer will not send network requests. 
(#205, #187)
+  + Enhancement: Improved handling of errors in ADAL to MSAL token migration 
scenario. (#209, #208)
+  + Added changelog in PYPI (#203, #202)
+  + Other readme and reference docs adjustments (#200, #197)
+
+-------------------------------------------------------------------

Old:
----
  msal-1.3.0.tar.gz

New:
----
  msal-1.5.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-msal.spec ++++++
--- /var/tmp/diff_new_pack.X05hpw/_old  2020-10-02 17:28:16.274414729 +0200
+++ /var/tmp/diff_new_pack.X05hpw/_new  2020-10-02 17:28:16.278414731 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-msal
-Version:        1.3.0
+Version:        1.5.0
 Release:        0
 Summary:        Microsoft Authentication Library (MSAL) for Python
 License:        MIT

++++++ msal-1.3.0.tar.gz -> msal-1.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/PKG-INFO new/msal-1.5.0/PKG-INFO
--- old/msal-1.3.0/PKG-INFO     2020-05-15 05:35:49.000000000 +0200
+++ new/msal-1.5.0/PKG-INFO     2020-09-03 23:50:40.000000000 +0200
@@ -1,11 +1,12 @@
 Metadata-Version: 2.1
 Name: msal
-Version: 1.3.0
+Version: 1.5.0
 Summary: The Microsoft Authentication Library (MSAL) for Python library 
enables your app to access the Microsoft Cloud by supporting authentication of 
users with Microsoft Azure Active Directory accounts (AAD) and Microsoft 
Accounts (MSA) using industry standard OAuth2 and OpenID Connect.
 Home-page: 
https://github.com/AzureAD/microsoft-authentication-library-for-python
 Author: Microsoft Corporation
 Author-email: [email protected]
 License: MIT
+Project-URL: Changelog, 
https://github.com/AzureAD/microsoft-authentication-library-for-python/releases
 Description: # Microsoft Authentication Library (MSAL) for Python
         
         
@@ -58,7 +59,7 @@
            from msal import PublicClientApplication
            app = PublicClientApplication(
                "your_client_id",
-               "authority": 
"https://login.microsoftonline.com/Enter_the_Tenant_Name_Here";)
+               
authority="https://login.microsoftonline.com/Enter_the_Tenant_Name_Here";)
            ```
         
            Later, each time you would want an access token, you start by:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/README.md new/msal-1.5.0/README.md
--- old/msal-1.3.0/README.md    2020-05-15 05:35:24.000000000 +0200
+++ new/msal-1.5.0/README.md    2020-09-03 23:50:17.000000000 +0200
@@ -50,7 +50,7 @@
    from msal import PublicClientApplication
    app = PublicClientApplication(
        "your_client_id",
-       "authority": 
"https://login.microsoftonline.com/Enter_the_Tenant_Name_Here";)
+       
authority="https://login.microsoftonline.com/Enter_the_Tenant_Name_Here";)
    ```
 
    Later, each time you would want an access token, you start by:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal/application.py 
new/msal-1.5.0/msal/application.py
--- old/msal-1.3.0/msal/application.py  2020-05-15 05:35:24.000000000 +0200
+++ new/msal-1.5.0/msal/application.py  2020-09-03 23:50:17.000000000 +0200
@@ -21,7 +21,7 @@
 
 
 # The __init__.py will import this. Not the other way around.
-__version__ = "1.3.0"
+__version__ = "1.5.0"
 
 logger = logging.getLogger(__name__)
 
@@ -79,9 +79,21 @@
     return [public_cert_content.strip()]
 
 
+def _merge_claims_challenge_and_capabilities(capabilities, claims_challenge):
+    # Represent capabilities as {"access_token": {"xms_cc": {"values": 
capabilities}}}
+    # and then merge/add it into incoming claims
+    if not capabilities:
+        return claims_challenge
+    claims_dict = json.loads(claims_challenge) if claims_challenge else {}
+    for key in ["access_token"]:  # We could add "id_token" if we'd decide to
+        claims_dict.setdefault(key, {}).update(xms_cc={"values": capabilities})
+    return json.dumps(claims_dict)
+
+
 class ClientApplication(object):
 
     ACQUIRE_TOKEN_SILENT_ID = "84"
+    ACQUIRE_TOKEN_BY_REFRESH_TOKEN = "85"
     ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID = "301"
     ACQUIRE_TOKEN_ON_BEHALF_OF_ID = "523"
     ACQUIRE_TOKEN_BY_DEVICE_FLOW_ID = "622"
@@ -96,7 +108,8 @@
             token_cache=None,
             http_client=None,
             verify=True, proxies=None, timeout=None,
-            client_claims=None, app_name=None, app_version=None):
+            client_claims=None, app_name=None, app_version=None,
+            client_capabilities=None):
         """Create an instance of application.
 
         :param str client_id: Your app has a client_id after you register it 
on AAD.
@@ -178,10 +191,16 @@
         :param app_version: (optional)
             You can provide your application version for Microsoft telemetry 
purposes.
             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"].
+            MSAL will combine them into
+            `claims parameter 
<https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter`_
+            which you will later provide via one of the acquire-token request.
         """
         self.client_id = client_id
         self.client_credential = client_credential
         self.client_claims = client_claims
+        self._client_capabilities = client_capabilities
         if http_client:
             self.http_client = http_client
         else:
@@ -234,6 +253,7 @@
             "authorization_endpoint": authority.authorization_endpoint,
             "token_endpoint": authority.token_endpoint,
             "device_authorization_endpoint":
+                authority.device_authorization_endpoint or
                 urljoin(authority.token_endpoint, "devicecode"),
             }
         return Client(
@@ -259,6 +279,7 @@
             prompt=None,
             nonce=None,
             domain_hint=None,  # type: Optional[str]
+            claims_challenge=None,
             **kwargs):
         """Constructs a URL for you to start a Authorization Code Grant.
 
@@ -284,8 +305,15 @@
             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.
-            
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
-            
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8
+            More information on possible values
+            `here 
<https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_
 and
+            `here 
<https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_.
+        :param claims_challenge:
+             The claims_challenge parameter requests specific claims requested 
by the resource provider
+             in the form of a claims_challenge directive in the 
www-authenticate header to be
+             returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+             It is a string of a JSON object which contains lists of claims 
being requested from these locations.
+
         :return: The authorization url as a string.
         """
         """ # TBD: this would only be meaningful in a new 
acquire_token_interactive()
@@ -318,6 +346,8 @@
             scope=decorate_scope(scopes, self.client_id),
             nonce=nonce,
             domain_hint=domain_hint,
+            claims=_merge_claims_challenge_and_capabilities(
+                self._client_capabilities, claims_challenge),
             )
 
     def acquire_token_by_authorization_code(
@@ -329,6 +359,7 @@
                 # authorization request as described in Section 4.1.1, and 
their
                 # values MUST be identical.
             nonce=None,
+            claims_challenge=None,
             **kwargs):
         """The second half of the Authorization Code Grant.
 
@@ -354,6 +385,12 @@
             same nonce should also be provided here, so that we'll validate it.
             An exception will be raised if the nonce in id token mismatches.
 
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
+
         :return: A dict representing the json response from AAD:
 
             - A successful response would contain "access_token" key,
@@ -374,6 +411,10 @@
                 CLIENT_CURRENT_TELEMETRY: 
_build_current_telemetry_request_header(
                     self.ACQUIRE_TOKEN_BY_AUTHORIZATION_CODE_ID),
                 },
+            data=dict(
+                kwargs.pop("data", {}),
+                claims=_merge_claims_challenge_and_capabilities(
+                    self._client_capabilities, claims_challenge)),
             nonce=nonce,
             **kwargs)
 
@@ -476,6 +517,7 @@
             account,  # type: Optional[Account]
             authority=None,  # See get_authorization_request_url()
             force_refresh=False,  # type: Optional[boolean]
+            claims_challenge=None,
             **kwargs):
         """Acquire an access token for given account, without user interaction.
 
@@ -490,6 +532,12 @@
 
         Internally, this method calls :func:`~acquire_token_silent_with_error`.
 
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
+
         :return:
             - A dict containing no "error" key,
               and typically contains an "access_token" key,
@@ -497,7 +545,8 @@
             - None when cache lookup does not yield a token.
         """
         result = self.acquire_token_silent_with_error(
-            scopes, account, authority, force_refresh, **kwargs)
+            scopes, account, authority, force_refresh,
+            claims_challenge=claims_challenge, **kwargs)
         return result if result and "error" not in result else None
 
     def acquire_token_silent_with_error(
@@ -506,6 +555,7 @@
             account,  # type: Optional[Account]
             authority=None,  # See get_authorization_request_url()
             force_refresh=False,  # type: Optional[boolean]
+            claims_challenge=None,
             **kwargs):
         """Acquire an access token for given account, without user interaction.
 
@@ -526,6 +576,11 @@
         :param force_refresh:
             If True, it will skip Access Token look-up,
             and try to find a Refresh Token to obtain a new Access Token.
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
         :return:
             - A dict containing no "error" key,
               and typically contains an "access_token" key,
@@ -544,18 +599,28 @@
         #     ) if authority else self.authority
         result = self._acquire_token_silent_from_cache_and_possibly_refresh_it(
             scopes, account, self.authority, force_refresh=force_refresh,
+            claims_challenge=claims_challenge,
             correlation_id=correlation_id,
             **kwargs)
         if result and "error" not in result:
             return result
         final_result = result
         for alias in self._get_authority_aliases(self.authority.instance):
+            if not self.token_cache.find(
+                    self.token_cache.CredentialType.REFRESH_TOKEN,
+                    # target=scopes,  # MUST NOT filter by scopes, because:
+                        # 1. AAD RTs are scope-independent;
+                        # 2. therefore target is optional per schema;
+                    query={"environment": alias}):
+                # Skip heavy weight logic when RT for this alias doesn't exist
+                continue
             the_authority = Authority(
                 "https://"; + alias + "/" + self.authority.tenant,
                 self.http_client,
                 validate_authority=False)
             result = 
self._acquire_token_silent_from_cache_and_possibly_refresh_it(
                 scopes, account, the_authority, force_refresh=force_refresh,
+                claims_challenge=claims_challenge,
                 correlation_id=correlation_id,
                 **kwargs)
             if result:
@@ -578,8 +643,9 @@
             account,  # type: Optional[Account]
             authority,  # This can be different than self.authority
             force_refresh=False,  # type: Optional[boolean]
+            claims_challenge=None,
             **kwargs):
-        if not force_refresh:
+        if not (force_refresh or claims_challenge):  # Bypass AT when desired 
or using claims
             query={
                     "client_id": self.client_id,
                     "environment": authority.instance,
@@ -606,7 +672,7 @@
                     }
         return 
self._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family(
                 authority, decorate_scope(scopes, self.client_id), account,
-                force_refresh=force_refresh, **kwargs)
+                force_refresh=force_refresh, 
claims_challenge=claims_challenge, **kwargs)
 
     def _acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family(
             self, authority, scopes, account, **kwargs):
@@ -655,7 +721,7 @@
     def _acquire_token_silent_by_finding_specific_refresh_token(
             self, authority, scopes, query,
             rt_remover=None, break_condition=lambda response: False,
-            force_refresh=False, correlation_id=None, **kwargs):
+            force_refresh=False, correlation_id=None, claims_challenge=None, 
**kwargs):
         matches = self.token_cache.find(
             self.token_cache.CredentialType.REFRESH_TOKEN,
             # target=scopes,  # AAD RTs are scope-independent
@@ -675,6 +741,10 @@
                     CLIENT_CURRENT_TELEMETRY: 
_build_current_telemetry_request_header(
                         self.ACQUIRE_TOKEN_SILENT_ID, 
force_refresh=force_refresh),
                     },
+                data=dict(
+                    kwargs.pop("data", {}),
+                    claims=_merge_claims_challenge_and_capabilities(
+                        self._client_capabilities, claims_challenge)),
                 **kwargs)
             if "error" not in response:
                 return response
@@ -725,9 +795,15 @@
         """
         return self.client.obtain_token_by_refresh_token(
             refresh_token,
-            decorate_scope(scopes, self.client_id),
+            scope=decorate_scope(scopes, self.client_id),
+            headers={
+                CLIENT_REQUEST_ID: _get_new_correlation_id(),
+                CLIENT_CURRENT_TELEMETRY: 
_build_current_telemetry_request_header(
+                    self.ACQUIRE_TOKEN_BY_REFRESH_TOKEN),
+            },
             rt_getter=lambda rt: rt,
             on_updating_rt=False,
+            on_removing_rt=lambda rt_item: None,  # No OP
             )
 
 
@@ -763,7 +839,7 @@
         flow[self.DEVICE_FLOW_CORRELATION_ID] = correlation_id
         return flow
 
-    def acquire_token_by_device_flow(self, flow, **kwargs):
+    def acquire_token_by_device_flow(self, flow, claims_challenge=None, 
**kwargs):
         """Obtain token by a device flow object, with customizable polling 
effect.
 
         :param dict flow:
@@ -771,6 +847,11 @@
             By default, this method's polling effect  will block current 
thread.
             You can abort the polling loop at any time,
             by changing the value of the flow's "expires_at" key to 0.
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
 
         :return: A dict representing the json response from AAD:
 
@@ -779,10 +860,14 @@
         """
         return self.client.obtain_token_by_device_flow(
             flow,
-            data=dict(kwargs.pop("data", {}), code=flow["device_code"]),
-                # 2018-10-4 Hack:
-                # during transition period,
-                # service seemingly need both device_code and code parameter.
+            data=dict(
+                kwargs.pop("data", {}),
+                code=flow["device_code"],  # 2018-10-4 Hack:
+                    # during transition period,
+                    # service seemingly need both device_code and code 
parameter.
+                claims=_merge_claims_challenge_and_capabilities(
+                    self._client_capabilities, claims_challenge),
+                ),
             headers={
                 CLIENT_REQUEST_ID:
                     flow.get(self.DEVICE_FLOW_CORRELATION_ID) or 
_get_new_correlation_id(),
@@ -792,7 +877,7 @@
             **kwargs)
 
     def acquire_token_by_username_password(
-            self, username, password, scopes, **kwargs):
+            self, username, password, scopes, claims_challenge=None, **kwargs):
         """Gets a token for a given resource via user credentials.
 
         See this page for constraints of Username Password Flow.
@@ -802,6 +887,11 @@
         :param str password: The password.
         :param list[str] scopes:
             Scopes requested to access a protected API (a resource).
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
 
         :return: A dict representing the json response from AAD:
 
@@ -814,16 +904,22 @@
             CLIENT_CURRENT_TELEMETRY: _build_current_telemetry_request_header(
                 self.ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID),
             }
+        data = dict(
+            kwargs.pop("data", {}),
+            claims=_merge_claims_challenge_and_capabilities(
+                self._client_capabilities, claims_challenge))
         if not self.authority.is_adfs:
             user_realm_result = self.authority.user_realm_discovery(
                 username, correlation_id=headers[CLIENT_REQUEST_ID])
             if user_realm_result.get("account_type") == "Federated":
                 return self._acquire_token_by_username_password_federated(
                     user_realm_result, username, password, scopes=scopes,
+                    data=data,
                     headers=headers, **kwargs)
         return self.client.obtain_token_by_username_password(
                 username, password, scope=scopes,
                 headers=headers,
+                data=data,
                 **kwargs)
 
     def _acquire_token_by_username_password_federated(
@@ -865,11 +961,16 @@
 
 class ConfidentialClientApplication(ClientApplication):  # server-side web app
 
-    def acquire_token_for_client(self, scopes, **kwargs):
+    def acquire_token_for_client(self, scopes, claims_challenge=None, 
**kwargs):
         """Acquires token for the current confidential client, not for an end 
user.
 
         :param list[str] scopes: (Required)
             Scopes requested to access a protected API (a resource).
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
 
         :return: A dict representing the json response from AAD:
 
@@ -884,9 +985,13 @@
                 CLIENT_CURRENT_TELEMETRY: 
_build_current_telemetry_request_header(
                     self.ACQUIRE_TOKEN_FOR_CLIENT_ID),
                 },
+            data=dict(
+                kwargs.pop("data", {}),
+                claims=_merge_claims_challenge_and_capabilities(
+                    self._client_capabilities, claims_challenge)),
             **kwargs)
 
-    def acquire_token_on_behalf_of(self, user_assertion, scopes, **kwargs):
+    def acquire_token_on_behalf_of(self, user_assertion, scopes, 
claims_challenge=None, **kwargs):
         """Acquires token using on-behalf-of (OBO) flow.
 
         The current app is a middle-tier service which was called with a token
@@ -901,6 +1006,11 @@
 
         :param str user_assertion: The incoming token already received by this 
app
         :param list[str] scopes: Scopes required by downstream API (a 
resource).
+        :param claims_challenge:
+            The claims_challenge parameter requests specific claims requested 
by the resource provider
+            in the form of a claims_challenge directive in the 
www-authenticate header to be
+            returned from the UserInfo Endpoint and/or in the ID Token and/or 
Access Token.
+            It is a string of a JSON object which contains lists of claims 
being requested from these locations.
 
         :return: A dict representing the json response from AAD:
 
@@ -918,7 +1028,11 @@
                 # 2. Requesting an IDT (which would otherwise be unavailable)
                 #    so that the calling app could use id_token_claims to 
implement
                 #    their own cache mapping, which is likely needed in web 
apps.
-            data=dict(kwargs.pop("data", {}), 
requested_token_use="on_behalf_of"),
+            data=dict(
+                kwargs.pop("data", {}),
+                requested_token_use="on_behalf_of",
+                claims=_merge_claims_challenge_and_capabilities(
+                    self._client_capabilities, claims_challenge)),
             headers={
                 CLIENT_REQUEST_ID: _get_new_correlation_id(),
                 CLIENT_CURRENT_TELEMETRY: 
_build_current_telemetry_request_header(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal/authority.py 
new/msal-1.5.0/msal/authority.py
--- old/msal-1.3.0/msal/authority.py    2020-05-15 05:35:24.000000000 +0200
+++ new/msal-1.5.0/msal/authority.py    2020-09-03 23:50:17.000000000 +0200
@@ -92,6 +92,7 @@
         logger.debug("openid_config = %s", openid_config)
         self.authorization_endpoint = openid_config['authorization_endpoint']
         self.token_endpoint = openid_config['token_endpoint']
+        self.device_authorization_endpoint = 
openid_config.get('device_authorization_endpoint')
         _, _, self.tenant = canonicalize(self.token_endpoint)  # Usually a GUID
         self.is_adfs = self.tenant.lower() == 'adfs'
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal/mex.py new/msal-1.5.0/msal/mex.py
--- old/msal-1.3.0/msal/mex.py  2020-05-15 05:35:24.000000000 +0200
+++ new/msal-1.5.0/msal/mex.py  2020-09-03 23:50:17.000000000 +0200
@@ -41,9 +41,7 @@
 
 
 def send_request(mex_endpoint, http_client, **kwargs):
-    mex_document = http_client.get(
-        mex_endpoint, headers={'Content-Type': 'application/soap+xml'},
-        **kwargs).text
+    mex_document = http_client.get(mex_endpoint, **kwargs).text
     return Mex(mex_document).get_wstrust_username_password_endpoint()
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal/oauth2cli/oauth2.py 
new/msal-1.5.0/msal/oauth2cli/oauth2.py
--- old/msal-1.3.0/msal/oauth2cli/oauth2.py     2020-05-15 05:35:24.000000000 
+0200
+++ new/msal-1.5.0/msal/oauth2cli/oauth2.py     2020-09-03 23:50:17.000000000 
+0200
@@ -3,10 +3,10 @@
 
 import json
 try:
-    from urllib.parse import urlencode, parse_qs
+    from urllib.parse import urlencode, parse_qs, quote_plus
 except ImportError:
     from urlparse import parse_qs
-    from urllib import urlencode
+    from urllib import urlencode, quote_plus
 import logging
 import warnings
 import time
@@ -205,9 +205,14 @@
         # client credentials in the request-body using the following
         # parameters: client_id, client_secret.
         if self.client_secret and self.client_id:
-            _headers["Authorization"] = "Basic " + base64.b64encode(
-                "{}:{}".format(self.client_id, self.client_secret)
-                .encode("ascii")).decode("ascii")
+            _headers["Authorization"] = "Basic " + 
base64.b64encode("{}:{}".format(
+                # Per https://tools.ietf.org/html/rfc6749#section-2.3.1
+                # client_id and client_secret needs to be encoded by
+                # "application/x-www-form-urlencoded"
+                # https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
+                # BEFORE they are fed into HTTP Basic Authentication
+                quote_plus(self.client_id), quote_plus(self.client_secret)
+                ).encode("ascii")).decode("ascii")
 
         if "token_endpoint" not in self.configuration:
             raise ValueError("token_endpoint not found in configuration")
@@ -233,7 +238,7 @@
 
         :param refresh_token: The refresh token issued to the client
         :param scope: If omitted, is treated as equal to the scope originally
-            granted by the resource ownser,
+            granted by the resource owner,
             according to https://tools.ietf.org/html/rfc6749#section-6
         """
         assert isinstance(refresh_token, string_types)
@@ -397,7 +402,7 @@
 
     def obtain_token_by_authorization_code(
             self, code, redirect_uri=None, scope=None, **kwargs):
-        """Get a token via auhtorization code. a.k.a. Authorization Code Grant.
+        """Get a token via authorization code. a.k.a. Authorization Code Grant.
 
         This is typically used by a server-side app (Confidential Client),
         but it can also be used by a device-side native app (Public Client).
@@ -503,7 +508,7 @@
             Either way, this token_item will be passed into other callbacks 
as-is.
 
         :param scope: If omitted, is treated as equal to the scope originally
-            granted by the resource ownser,
+            granted by the resource owner,
             according to https://tools.ietf.org/html/rfc6749#section-6
         :param rt_getter: A callable to translate the token_item to a raw RT 
string
         :param on_removing_rt: If absent, fall back to the one defined in 
initialization
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal/oauth2cli/oidc.py 
new/msal-1.5.0/msal/oauth2cli/oidc.py
--- old/msal-1.3.0/msal/oauth2cli/oidc.py       2020-05-15 05:35:24.000000000 
+0200
+++ new/msal-1.5.0/msal/oauth2cli/oidc.py       2020-09-03 23:50:17.000000000 
+0200
@@ -38,6 +38,12 @@
     """
     decoded = json.loads(decode_part(id_token.split('.')[1]))
     err = None  # 
https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
+    _now = now or time.time()
+    skew = 120  # 2 minutes
+    if _now + skew < decoded.get("nbf", _now - 1):  # nbf is optional per JWT 
specs
+        # This is not an ID token validation, but a JWT validation
+        # https://tools.ietf.org/html/rfc7519#section-4.1.5
+        err = "0. The ID token is not yet valid"
     if issuer and issuer != decoded["iss"]:
         # 
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
         err = ('2. The Issuer Identifier for the OpenID Provider, "%s", '
@@ -53,7 +59,7 @@
     # the Client and the Token Endpoint (which it is in this flow),
     # the TLS server validation MAY be used to validate the issuer
     # in place of checking the token signature.
-    if (now or time.time()) > decoded["exp"]:
+    if _now > decoded["exp"]:
         err = "9. The current time MUST be before the time represented by the 
exp Claim."
     if nonce and nonce != decoded.get("nonce"):
         err = ("11. Nonce must be the same value "
@@ -99,7 +105,7 @@
             response_type, nonce=nonce, **kwargs)
 
     def obtain_token_by_authorization_code(self, code, nonce=None, **kwargs):
-        """Get a token via auhtorization code. a.k.a. Authorization Code Grant.
+        """Get a token via authorization code. a.k.a. Authorization Code Grant.
 
         Return value and all other parameters are the same as
         :func:`oauth2.Client.obtain_token_by_authorization_code`,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal/wstrust_request.py 
new/msal-1.5.0/msal/wstrust_request.py
--- old/msal-1.3.0/msal/wstrust_request.py      2020-05-15 05:35:24.000000000 
+0200
+++ new/msal-1.5.0/msal/wstrust_request.py      2020-09-03 23:50:17.000000000 
+0200
@@ -79,7 +79,7 @@
     return """<s:Envelope xmlns:s='{s}' xmlns:wsa='{wsa}' xmlns:wsu='{wsu}'>
         <s:Header>
             <wsa:Action s:mustUnderstand='1'>{soap_action}</wsa:Action>
-            <wsa:messageID>urn:uuid:{message_id}</wsa:messageID>
+            <wsa:MessageID>urn:uuid:{message_id}</wsa:MessageID>
             <wsa:ReplyTo>
             
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
             </wsa:ReplyTo>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/msal.egg-info/PKG-INFO 
new/msal-1.5.0/msal.egg-info/PKG-INFO
--- old/msal-1.3.0/msal.egg-info/PKG-INFO       2020-05-15 05:35:49.000000000 
+0200
+++ new/msal-1.5.0/msal.egg-info/PKG-INFO       2020-09-03 23:50:40.000000000 
+0200
@@ -1,11 +1,12 @@
 Metadata-Version: 2.1
 Name: msal
-Version: 1.3.0
+Version: 1.5.0
 Summary: The Microsoft Authentication Library (MSAL) for Python library 
enables your app to access the Microsoft Cloud by supporting authentication of 
users with Microsoft Azure Active Directory accounts (AAD) and Microsoft 
Accounts (MSA) using industry standard OAuth2 and OpenID Connect.
 Home-page: 
https://github.com/AzureAD/microsoft-authentication-library-for-python
 Author: Microsoft Corporation
 Author-email: [email protected]
 License: MIT
+Project-URL: Changelog, 
https://github.com/AzureAD/microsoft-authentication-library-for-python/releases
 Description: # Microsoft Authentication Library (MSAL) for Python
         
         
@@ -58,7 +59,7 @@
            from msal import PublicClientApplication
            app = PublicClientApplication(
                "your_client_id",
-               "authority": 
"https://login.microsoftonline.com/Enter_the_Tenant_Name_Here";)
+               
authority="https://login.microsoftonline.com/Enter_the_Tenant_Name_Here";)
            ```
         
            Later, each time you would want an access token, you start by:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/msal-1.3.0/setup.cfg new/msal-1.5.0/setup.cfg
--- old/msal-1.3.0/setup.cfg    2020-05-15 05:35:49.000000000 +0200
+++ new/msal-1.5.0/setup.cfg    2020-09-03 23:50:40.000000000 +0200
@@ -1,6 +1,10 @@
 [bdist_wheel]
 universal = 1
 
+[metadata]
+project_urls = 
+       Changelog = 
https://github.com/AzureAD/microsoft-authentication-library-for-python/releases
+
 [egg_info]
 tag_build = 
 tag_date = 0


Reply via email to