Skip to content

Commit 1e09105

Browse files
committed
Support sovereign cloud.
Previous implementation was largely based on some hallway communication, which happened to not work in sovereign scenario. Neither did we test sovereign scenario for MSAL Python, until now.
1 parent 4c52458 commit 1e09105

File tree

2 files changed

+17
-26
lines changed

2 files changed

+17
-26
lines changed

msal/authority.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import re
2+
import logging
23

34
import requests
45

56
from .exceptions import MsalServiceError
67

78

9+
logger = logging.getLogger(__name__)
810
WORLD_WIDE = 'login.microsoftonline.com' # There was an alias login.windows.net
911
WELL_KNOWN_AUTHORITY_HOSTS = set([
1012
WORLD_WIDE,
@@ -38,14 +40,15 @@ def __init__(self, authority_url, validate_authority=True,
3840
canonicalized, self.instance, tenant = canonicalize(authority_url)
3941
tenant_discovery_endpoint = ( # Hard code a V2 pattern as default value
4042
'https://{}/{}/v2.0/.well-known/openid-configuration'
41-
.format(WORLD_WIDE, tenant))
43+
.format(self.instance, tenant))
4244
if validate_authority and self.instance not in WELL_KNOWN_AUTHORITY_HOSTS:
4345
tenant_discovery_endpoint = instance_discovery(
4446
canonicalized + "/oauth2/v2.0/authorize",
4547
verify=verify, proxies=proxies, timeout=timeout)
4648
openid_config = tenant_discovery(
4749
tenant_discovery_endpoint,
4850
verify=verify, proxies=proxies, timeout=timeout)
51+
logger.debug("openid_config = %s", openid_config)
4952
self.authorization_endpoint = openid_config['authorization_endpoint']
5053
self.token_endpoint = openid_config['token_endpoint']
5154
_, _, self.tenant = canonicalize(self.token_endpoint) # Usually a GUID
@@ -76,7 +79,11 @@ def canonicalize(url):
7679
def instance_discovery(url, response=None, **kwargs):
7780
# Returns tenant discovery endpoint
7881
resp = requests.get( # Note: This URL seemingly returns V1 endpoint only
79-
'https://{}/common/discovery/instance'.format(WORLD_WIDE),
82+
'https://{}/common/discovery/instance'.format(
83+
WORLD_WIDE # Historically using WORLD_WIDE. Could use self.instance too
84+
# See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/4.0.0/src/Microsoft.Identity.Client/Instance/AadInstanceDiscovery.cs#L101-L103
85+
# and https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/4.0.0/src/Microsoft.Identity.Client/Instance/AadAuthority.cs#L19-L33
86+
),
8087
params={'authorization_endpoint': url, 'api-version': '1.0'},
8188
**kwargs)
8289
payload = response or resp.json()

tests/test_authority.py

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,16 @@
44

55

66
class TestAuthority(unittest.TestCase):
7-
COMMON_AUTH_ENDPOINT = \
8-
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
9-
COMMON_TOKEN_ENDPOINT = \
10-
'https://login.microsoftonline.com/common/oauth2/v2.0/token'
117

128
def test_wellknown_host_and_tenant(self):
13-
# Test one specific sample in straightforward way, for readability
14-
a = Authority('https://login.microsoftonline.com/common')
15-
self.assertEqual(a.authorization_endpoint, self.COMMON_AUTH_ENDPOINT)
16-
self.assertEqual(a.token_endpoint, self.COMMON_TOKEN_ENDPOINT)
17-
18-
# Test all well known authority hosts, using same real "common" tenant
9+
# Assert all well known authority hosts are using their own "common" tenant
1910
for host in WELL_KNOWN_AUTHORITY_HOSTS:
2011
a = Authority('https://{}/common'.format(host))
21-
# Note: this "common" tenant endpoints always point to its real host
2212
self.assertEqual(
23-
a.authorization_endpoint, self.COMMON_AUTH_ENDPOINT)
24-
self.assertEqual(a.token_endpoint, self.COMMON_TOKEN_ENDPOINT)
13+
a.authorization_endpoint,
14+
'https://%s/common/oauth2/v2.0/authorize' % host)
15+
self.assertEqual(
16+
a.token_endpoint, 'https://%s/common/oauth2/v2.0/token' % host)
2517

2618
@unittest.skip("As of Jan 2017, the server no longer returns V1 endpoint")
2719
def test_lessknown_host_will_return_a_set_of_v1_endpoints(self):
@@ -33,20 +25,12 @@ def test_lessknown_host_will_return_a_set_of_v1_endpoints(self):
3325
self.assertEqual(a.token_endpoint, v1_token_endpoint)
3426
self.assertNotIn('v2.0', a.token_endpoint)
3527

36-
def test_unknown_host(self):
28+
def test_unknown_host_wont_pass_instance_discovery(self):
3729
with self.assertRaisesRegexp(MsalServiceError, "invalid_instance"):
3830
Authority('https://unknown.host/tenant_doesnt_matter_in_this_case')
3931

40-
def test_unknown_host_valid_tenant_and_skip_host_validation(self):
41-
# When skipping host (a.k.a. instance) validation,
42-
# the Tenant Discovery will always use WORLD_WIDE service as instance,
43-
# so, if the tenant happens to exist there, it will find some endpoints.
44-
a = Authority('https://incorrect.host/common', validate_authority=False)
45-
self.assertEqual(a.authorization_endpoint, self.COMMON_AUTH_ENDPOINT)
46-
self.assertEqual(a.token_endpoint, self.COMMON_TOKEN_ENDPOINT)
47-
48-
def test_unknown_host_unknown_tenant_and_skip_host_validation(self):
49-
with self.assertRaisesRegexp(MsalServiceError, "invalid_tenant"):
32+
def test_invalid_host_skipping_validation_meets_connection_error_down_the_road(self):
33+
with self.assertRaises(requests.exceptions.RequestException):
5034
Authority('https://unknown.host/invalid', validate_authority=False)
5135

5236

0 commit comments

Comments
 (0)