Repository: AzureAD/microsoft-authentication-library-for-python

Issue:
MSAL python 1.4.2 is no longer able to read from other MSAL caches
**Describe the bug**
MSAL.NET maintains cache consistency tests between libraries. It looks like MSAL.py 1.4.2 update brakes the tests which ensure MSAL .py is able to read the token cache produced by .NET and Java. 

After downgrading to MSAL py 1.4.1 (`pip install msal==1.4.1 --force-reinstall`), the tests start passing again.

**To Reproduce**

1. AcquireTokenInteractive with MSAL.NET or with MSAL.Java and save the cache to a file (plaintext)
2. Configure MSAL py to read the cache from the file
3. AcquireTokenSilent with MSAL.Python (using the same scope, client_id etc.)

**Expected behavior**
AcquireTokenSilent should work (i.e. it should fetch the AT, there is no need for RT refresh)

**What you see instead**
GetAccounts returns 1 acconunt, however **AcquireTokenSilent return None.**

**The MSAL Python version you are using**
1.4.2

**Additional context**
Works with 1.4.1

Impact: please do not ship this version to AzCLI or any other partners with whom we do cache sharing!

Note: python test code is [here](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/d9f182257fceb34d7510502f9f42d91afde5abbe/tests/CacheCompat/CommonCache.Test.MsalPython/TestMsalPython.py#L33)


Discussion:

Patch:
diff --git a/msal/application.py b/msal/application.py
index 0d38a1a..f5c8d01 100644
--- a/msal/application.py
+++ b/msal/application.py
@@ -21,7 +21,7 @@ from .token_cache import TokenCache
 
 
 # The __init__.py will import this. Not the other way around.
-__version__ = "1.4.2"
+__version__ = "1.4.1"
 
 logger = logging.getLogger(__name__)
 
@@ -554,7 +554,9 @@ class ClientApplication(object):
         for alias in self._get_authority_aliases(self.authority.instance):
             if not self.token_cache.find(
                     self.token_cache.CredentialType.REFRESH_TOKEN,
-                    target=scopes,
+                    # 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
diff --git a/msal/oauth2cli/oauth2.py b/msal/oauth2cli/oauth2.py
index 1d9c21d..55fa054 100644
--- a/msal/oauth2cli/oauth2.py
+++ b/msal/oauth2cli/oauth2.py
@@ -3,10 +3,10 @@
 
 import json
 try:
-    from urllib.parse import urlencode, parse_qs, quote_plus
+    from urllib.parse import urlencode, parse_qs
 except ImportError:
     from urlparse import parse_qs
-    from urllib import urlencode, quote_plus
+    from urllib import urlencode
 import logging
 import warnings
 import time
@@ -205,14 +205,9 @@ class BaseClient(object):
         # 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(
-                # 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")
+            _headers["Authorization"] = "Basic " + base64.b64encode(
+                "{}:{}".format(self.client_id, self.client_secret)
+                .encode("ascii")).decode("ascii")
 
         if "token_endpoint" not in self.configuration:
             raise ValueError("token_endpoint not found in configuration")


Test patch:
diff --git a/tests/test_application.py b/tests/test_application.py
index 65b36b3..1716470 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -240,21 +240,30 @@ class TestClientApplicationForAuthorityMigration(unittest.TestCase):
                 uid=uid, utid=utid,
                 access_token=self.access_token, refresh_token="some refresh token"),
         })  # The add(...) helper populates correct home_account_id for future searching
-
-    def test_get_accounts(self):
-        app = ClientApplication(
+        self.app = ClientApplication(
             self.client_id,
             authority=self.authority_url_in_app, token_cache=self.cache)
-        accounts = app.get_accounts()
+
+    def test_get_accounts_should_find_accounts_under_different_alias(self):
+        accounts = self.app.get_accounts()
         self.assertNotEqual([], accounts)
         self.assertEqual(self.environment_in_cache, accounts[0].get("environment"),
             "We should be able to find an account under an authority alias")
 
-    def test_acquire_token_silent(self):
-        app = ClientApplication(
-            self.client_id,
-            authority=self.authority_url_in_app, token_cache=self.cache)
-        at = app.acquire_token_silent(self.scopes, self.account)
-        self.assertNotEqual(None, at)
-        self.assertEqual(self.access_token, at.get('access_token'))
+    def test_acquire_token_silent_should_find_at_under_different_alias(self):
+        result = self.app.acquire_token_silent(self.scopes, self.account)
+        self.assertNotEqual(None, result)
+        self.assertEqual(self.access_token, result.get('access_token'))
+
+    def test_acquire_token_silent_should_find_rt_under_different_alias(self):
+        self.cache._cache["AccessToken"] = {}  # A hacky way to clear ATs
+        class ExpectedBehavior(Exception):
+            pass
+        def helper(scopes, account, authority, *args, **kwargs):
+            if authority.instance == self.environment_in_cache:
+                raise ExpectedBehavior("RT of different alias being attempted")
+        self.app._acquire_token_silent_from_cache_and_possibly_refresh_it = helper
+
+        with self.assertRaises(ExpectedBehavior):
+            self.app.acquire_token_silent(["different scope"], self.account)