Skip to content

Commit 9850cf4

Browse files
committed
Support https://contoso.ciamlogin.com as authority
1 parent c46ff0c commit 9850cf4

File tree

2 files changed

+52
-21
lines changed

2 files changed

+52
-21
lines changed

msal/authority.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
from urlparse import urlparse
66
import logging
77

8-
from .exceptions import MsalServiceError
9-
108

119
logger = logging.getLogger(__name__)
1210

@@ -28,7 +26,9 @@
2826
"b2clogin.cn",
2927
"b2clogin.us",
3028
"b2clogin.de",
29+
"ciamlogin.com",
3130
]
31+
_CIAM_DOMAIN_SUFFIX = ".ciamlogin.com"
3232

3333

3434
class AuthorityBuilder(object):
@@ -74,7 +74,8 @@ def __init__(
7474
if isinstance(authority_url, AuthorityBuilder):
7575
authority_url = str(authority_url)
7676
authority, self.instance, tenant = canonicalize(authority_url)
77-
self.is_adfs = tenant.lower() == 'adfs'
77+
is_ciam = self.instance.endswith(_CIAM_DOMAIN_SUFFIX)
78+
self.is_adfs = tenant.lower() == 'adfs' and not is_ciam
7879
parts = authority.path.split('/')
7980
self._is_b2c = any(
8081
self.instance.endswith("." + d) for d in WELL_KNOWN_B2C_HOSTS
@@ -103,13 +104,13 @@ def __init__(
103104
% authority_url)
104105
tenant_discovery_endpoint = payload['tenant_discovery_endpoint']
105106
else:
106-
tenant_discovery_endpoint = (
107-
'https://{}:{}{}{}/.well-known/openid-configuration'.format(
108-
self.instance,
109-
443 if authority.port is None else authority.port,
110-
authority.path, # In B2C scenario, it is "/tenant/policy"
111-
"" if tenant == "adfs" else "/v2.0" # the AAD v2 endpoint
112-
))
107+
tenant_discovery_endpoint = authority._replace(
108+
path="{prefix}{version}/.well-known/openid-configuration".format(
109+
prefix=tenant if is_ciam and len(authority.path) <= 1 # Path-less CIAM
110+
else authority.path, # In B2C, it is "/tenant/policy"
111+
version="" if self.is_adfs else "/v2.0",
112+
)
113+
).geturl() # Keeping original port and query. Query is useful for test.
113114
try:
114115
openid_config = tenant_discovery(
115116
tenant_discovery_endpoint,
@@ -144,18 +145,28 @@ def user_realm_discovery(self, username, correlation_id=None, response=None):
144145
return {} # This can guide the caller to fall back normal ROPC flow
145146

146147

147-
def canonicalize(authority_url):
148+
def canonicalize(authority_or_auth_endpoint):
148149
# Returns (url_parsed_result, hostname_in_lowercase, tenant)
149-
authority = urlparse(authority_url)
150-
parts = authority.path.split("/")
151-
if authority.scheme != "https" or len(parts) < 2 or not parts[1]:
152-
raise ValueError(
153-
"Your given address (%s) should consist of "
154-
"an https url with a minimum of one segment in a path: e.g. "
155-
"https://login.microsoftonline.com/<tenant> "
156-
"or https://<tenant_name>.b2clogin.com/<tenant_name>.onmicrosoft.com/policy"
157-
% authority_url)
158-
return authority, authority.hostname, parts[1]
150+
authority = urlparse(authority_or_auth_endpoint)
151+
if authority.scheme == "https":
152+
parts = authority.path.split("/")
153+
first_part = parts[1] if len(parts) >= 2 and parts[1] else None
154+
if authority.hostname.endswith(_CIAM_DOMAIN_SUFFIX): # CIAM
155+
# Use path in CIAM authority. It will be validated by OIDC Discovery soon
156+
tenant = first_part if first_part else "{}.onmicrosoft.com".format(
157+
# Fallback to sub domain name. This variation may not be advertised
158+
authority.hostname.rsplit(_CIAM_DOMAIN_SUFFIX, 1)[0])
159+
return authority, authority.hostname, tenant
160+
# AAD
161+
if len(parts) >= 2 and parts[1]:
162+
return authority, authority.hostname, parts[1]
163+
raise ValueError(
164+
"Your given address (%s) should consist of "
165+
"an https url with a minimum of one segment in a path: e.g. "
166+
"https://login.microsoftonline.com/<tenant> "
167+
"or https://<tenant_name>.ciamlogin.com/<tenant> "
168+
"or https://<tenant_name>.b2clogin.com/<tenant_name>.onmicrosoft.com/policy"
169+
% authority_or_auth_endpoint)
159170

160171
def _instance_discovery(url, http_client, instance_discovery_endpoint, **kwargs):
161172
resp = http_client.get(

tests/test_authority.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ def test_invalid_host_skipping_validation_can_be_turned_off(self):
7979
pass # Those are expected for this unittest case
8080

8181

82+
@patch("msal.authority.tenant_discovery", return_value={
83+
"authorization_endpoint": "https://contoso.com/placeholder",
84+
"token_endpoint": "https://contoso.com/placeholder",
85+
})
86+
class TestCiamAuthority(unittest.TestCase):
87+
http_client = MinimalHttpClient()
88+
89+
def test_path_less_authority_should_work(self, oidc_discovery):
90+
Authority('https://contoso.ciamlogin.com', self.http_client)
91+
oidc_discovery.assert_called_once_with(
92+
"https://contoso.ciamlogin.com/contoso.onmicrosoft.com/v2.0/.well-known/openid-configuration",
93+
self.http_client)
94+
95+
def test_authority_with_path_should_be_used_as_is(self, oidc_discovery):
96+
Authority('https://contoso.ciamlogin.com/anything', self.http_client)
97+
oidc_discovery.assert_called_once_with(
98+
"https://contoso.ciamlogin.com/anything/v2.0/.well-known/openid-configuration",
99+
self.http_client)
100+
101+
82102
class TestAuthorityInternalHelperCanonicalize(unittest.TestCase):
83103

84104
def test_canonicalize_tenant_followed_by_extra_paths(self):

0 commit comments

Comments
 (0)