Skip to content

Commit 3c40b7a

Browse files
committed
Refactor: user_realm_discovery() memorizes domains not supporting URD
This would become handy when we meet B2C authority with customized domain.
1 parent e613b25 commit 3c40b7a

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

msal/authority.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class Authority(object):
2323
Once constructed, it contains members named "*_endpoint" for this instance.
2424
TODO: It will also cache the previously-validated authority instances.
2525
"""
26+
_domains_without_user_realm_discovery = set([])
27+
2628
def __init__(self, authority_url, validate_authority=True,
2729
verify=True, proxies=None, timeout=None,
2830
):
@@ -67,17 +69,21 @@ def __init__(self, authority_url, validate_authority=True,
6769
_, _, self.tenant = canonicalize(self.token_endpoint) # Usually a GUID
6870
self.is_adfs = self.tenant.lower() == 'adfs'
6971

70-
def user_realm_discovery(self, username):
71-
resp = requests.get(
72-
"https://{netloc}/common/userrealm/{username}?api-version=1.0".format(
73-
netloc=self.instance, username=username),
74-
headers={'Accept':'application/json'},
75-
verify=self.verify, proxies=self.proxies, timeout=self.timeout)
76-
resp.raise_for_status()
77-
return resp.json()
78-
# It will typically contain "ver", "account_type",
72+
def user_realm_discovery(self, username, response=None):
73+
# It will typically return a dict containing "ver", "account_type",
7974
# "federation_protocol", "cloud_audience_urn",
8075
# "federation_metadata_url", "federation_active_auth_url", etc.
76+
if self.instance not in self.__class__._domains_without_user_realm_discovery:
77+
resp = response or requests.get(
78+
"https://{netloc}/common/userrealm/{username}?api-version=1.0".format(
79+
netloc=self.instance, username=username),
80+
headers={'Accept':'application/json'},
81+
verify=self.verify, proxies=self.proxies, timeout=self.timeout)
82+
if resp.status_code != 404:
83+
resp.raise_for_status()
84+
return resp.json()
85+
self.__class__._domains_without_user_realm_discovery.add(self.instance)
86+
return {} # This can guide the caller to fall back normal ROPC flow
8187

8288
def canonicalize(url):
8389
# Returns (canonicalized_url, netloc, tenant). Raises ValueError on errors.

tests/test_authority.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,23 @@ def test_canonicalize_rejects_tenantless_host_with_trailing_slash(self):
6666
with self.assertRaises(ValueError):
6767
canonicalize("https://no.tenant.example.com/")
6868

69+
70+
@unittest.skipIf(os.getenv("TRAVIS_TAG"), "Skip network io during tagged release")
71+
class TestAuthorityInternalHelperUserRealmDiscovery(unittest.TestCase):
72+
def test_memorize(self):
73+
# We use a real authority so the constructor can finish tenant discovery
74+
authority = "https://login.microsoftonline.com/common"
75+
self.assertNotIn(authority, Authority._domains_without_user_realm_discovery)
76+
a = Authority(authority, validate_authority=False)
77+
78+
# We now pretend this authority supports no User Realm Discovery
79+
class MockResponse(object):
80+
status_code = 404
81+
a.user_realm_discovery("[email protected]", response=MockResponse())
82+
self.assertIn(
83+
"login.microsoftonline.com",
84+
Authority._domains_without_user_realm_discovery,
85+
"user_realm_discovery() should memorize domains not supporting URD")
86+
a.user_realm_discovery("[email protected]",
87+
response="This would cause exception if memorization did not work")
88+

0 commit comments

Comments
 (0)