Skip to content

Commit b388cee

Browse files
committed
Friendly hint covers most flows, tested. TODO: OBO
1 parent b750810 commit b388cee

7 files changed

+49
-11
lines changed

msal/application.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,16 @@ def _build_telemetry_context(
604604
self._telemetry_buffer, self._telemetry_lock, api_id,
605605
correlation_id=correlation_id, refresh_reason=refresh_reason)
606606

607+
def _adjust_response(self, response): # Adjust response inline
608+
# Currently, this is used to provide better error message for CIAM CUD
609+
error_description = response.get("error_description", "")
610+
if ("AADSTS500207" in error_description # Observed in most auth grants
611+
or "AADSTS900144" in error_description # Observed in ROPC
612+
) and self._oidc_authority and not self._oidc_authority.endswith("/v2.0"):
613+
response["error_description"] = (
614+
'Did you forget to append "/v2.0" to your oidc_authority? '
615+
+ response["error_description"])
616+
607617
def _get_regional_authority(self, central_authority):
608618
if not self._region_configured: # User did not opt-in to ESTS-R
609619
return None # Short circuit to completely bypass region detection
@@ -974,11 +984,7 @@ def authorize(): # A controller in a web app
974984
**kwargs))
975985
if "access_token" in response:
976986
response[self._TOKEN_SOURCE] = self._TOKEN_SOURCE_IDP
977-
if ("AADSTS500207" in response.get("error_description", "") and
978-
self._oidc_authority and not self._oidc_authority.endswith("/v2.0")):
979-
response["error_description"] = (
980-
'Did you forget to append "/v2.0" to your oidc_authority? '
981-
+ response["error_description"])
987+
self._adjust_response(response)
982988
telemetry_context.update_telemetry(response)
983989
return response
984990

@@ -1706,6 +1712,7 @@ def acquire_token_by_username_password(
17061712
**kwargs))
17071713
if "access_token" in response:
17081714
response[self._TOKEN_SOURCE] = self._TOKEN_SOURCE_IDP
1715+
self._adjust_response(response)
17091716
telemetry_context.update_telemetry(response)
17101717
return response
17111718

@@ -2008,6 +2015,8 @@ def acquire_token_interactive(
20082015
**kwargs))
20092016
if "access_token" in response:
20102017
response[self._TOKEN_SOURCE] = self._TOKEN_SOURCE_IDP
2018+
self._adjust_response(response) # Note: It won't improve
2019+
# the error rendered in browser, but still better than nothing
20112020
telemetry_context.update_telemetry(response)
20122021
return response
20132022

@@ -2117,6 +2126,7 @@ def initiate_device_flow(self, scopes=None, **kwargs):
21172126
headers={msal.telemetry.CLIENT_REQUEST_ID: correlation_id},
21182127
**kwargs)
21192128
flow[self.DEVICE_FLOW_CORRELATION_ID] = correlation_id
2129+
self._adjust_response(flow) # AADSTS500207 would happen here, not at token endpoint
21202130
return flow
21212131

21222132
def acquire_token_by_device_flow(self, flow, claims_challenge=None, **kwargs):
@@ -2214,6 +2224,7 @@ def _acquire_token_for_client(
22142224
claims=_merge_claims_challenge_and_capabilities(
22152225
self._client_capabilities, claims_challenge)),
22162226
**kwargs)
2227+
self._adjust_response(response)
22172228
telemetry_context.update_telemetry(response)
22182229
return response
22192230

msal/authority.py

+6
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def __init__(
102102

103103
def _initialize_oidc_authority(self, oidc_authority_url):
104104
authority, self.instance, tenant = canonicalize(oidc_authority_url)
105+
self._is_oidc = True
105106
self.is_adfs = tenant.lower() == 'adfs' # As a convention
106107
self._is_b2c = True # Not exactly true, but
107108
# OIDC Authority was designed for CIAM which is the next gen of B2C.
@@ -116,6 +117,7 @@ def _initialize_entra_authority(
116117
# instance discovery endpoint located at ``login.microsoftonline.com``.
117118
# You can customize the endpoint by providing a url as a string.
118119
# Or you can turn this behavior off by passing in a False here.
120+
self._is_oidc = False
119121
if isinstance(authority_url, AuthorityBuilder):
120122
authority_url = str(authority_url)
121123
authority, self.instance, tenant = canonicalize(authority_url)
@@ -162,6 +164,10 @@ def user_realm_discovery(self, username, correlation_id=None, response=None):
162164
# It will typically return a dict containing "ver", "account_type",
163165
# "federation_protocol", "cloud_audience_urn",
164166
# "federation_metadata_url", "federation_active_auth_url", etc.
167+
if self._is_oidc:
168+
# Conceptually, OIDC has no user real discovery.
169+
# Besides, ROPC on CIAM CUD apparently works without the federation anyway
170+
return {} # This can guide the caller to fall back normal ROPC flow
165171
if self.instance not in self.__class__._domains_without_user_realm_discovery:
166172
resp = response or self._http_client.get(
167173
"https://{netloc}/common/userrealm/{username}?api-version=1.0".format(

sample/confidential_client_certificate_sample.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
44
{
55
"authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here",
6+
// Usually you use this one
7+
"oidc_authority": "https://contoso.com/Enter_the_Tenant_Name_Here",
8+
// Alternatively, you use this one when your CIAM tenant has a custom domain
69
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
710
"scope": ["https://graph.microsoft.com/.default"],
811
// Specific to Client Credentials Grant i.e. acquire_token_for_client(),
@@ -50,7 +53,8 @@
5053

5154
# Create a preferably long-lived app instance, to avoid the overhead of app creation
5255
global_app = msal.ConfidentialClientApplication(
53-
config["client_id"], authority=config["authority"],
56+
config["client_id"], authority=config.get("authority"),
57+
oidc_authority=config.get("oidc_authority"), # Use this for a CIAM custom domain
5458
client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()},
5559
token_cache=global_token_cache, # Let this app (re)use an existing token cache.
5660
# If absent, ClientApplication will create its own empty token cache

sample/confidential_client_secret_sample.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
44
{
55
"authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here",
6+
// Usually you use this one
7+
"oidc_authority": "https://contoso.com/Enter_the_Tenant_Name_Here",
8+
// Alternatively, you use this one when your CIAM tenant has a custom domain
69
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
710
"scope": ["https://graph.microsoft.com/.default"],
811
// Specific to Client Credentials Grant i.e. acquire_token_for_client(),
@@ -49,7 +52,8 @@
4952

5053
# Create a preferably long-lived app instance, to avoid the overhead of app creation
5154
global_app = msal.ConfidentialClientApplication(
52-
config["client_id"], authority=config["authority"],
55+
config["client_id"], authority=config.get("authority"),
56+
oidc_authority=config.get("oidc_authority"), # Use this for a CIAM custom domain
5357
client_credential=config["secret"],
5458
token_cache=global_token_cache, # Let this app (re)use an existing token cache.
5559
# If absent, ClientApplication will create its own empty token cache

sample/device_flow_sample.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
44
{
55
"authority": "https://login.microsoftonline.com/common",
6+
// Usually you use this one
7+
"oidc_authority": "https://contoso.com/Enter_the_Tenant_Name_Here",
8+
// Alternatively, you use this one when your CIAM tenant has a custom domain
69
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
710
"scope": ["User.ReadBasic.All"],
811
// You can find the other permission names from this document
@@ -39,7 +42,8 @@
3942

4043
# Create a preferably long-lived app instance, to avoid the overhead of app creation
4144
global_app = msal.PublicClientApplication(
42-
config["client_id"], authority=config["authority"],
45+
config["client_id"], authority=config.get("authority"),
46+
oidc_authority=config.get("oidc_authority"), # Use this for a CIAM custom domain
4347
token_cache=global_token_cache, # Let this app (re)use an existing token cache.
4448
# If absent, ClientApplication will create its own empty token cache
4549
)

sample/interactive_sample.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
77
{
88
"authority": "https://login.microsoftonline.com/organizations",
9+
// Usually you use this one
10+
"oidc_authority": "https://contoso.com/Enter_the_Tenant_Name_Here",
11+
// Alternatively, you use this one when your CIAM tenant has a custom domain
912
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
1013
"scope": ["User.ReadBasic.All"],
1114
// You can find the other permission names from this document
@@ -36,7 +39,8 @@
3639

3740
# Create a preferably long-lived app instance, to avoid the overhead of app creation
3841
global_app = msal.PublicClientApplication(
39-
config["client_id"], authority=config["authority"],
42+
config["client_id"], authority=config.get("authority"),
43+
oidc_authority=config.get("oidc_authority"), # Use this for a CIAM custom domain
4044
#enable_broker_on_windows=True, # Opted in. You will be guided to meet the prerequisites, if your app hasn't already
4145
# See also: https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-acquire-token-wam#wam-value-proposition
4246
token_cache=global_token_cache, # Let this app (re)use an existing token cache.

sample/username_password_sample.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
44
{
55
"authority": "https://login.microsoftonline.com/organizations",
6+
// Usually you use this one
7+
"oidc_authority": "https://contoso.com/Enter_the_Tenant_Name_Here",
8+
// Alternatively, you use this one when your CIAM tenant has a custom domain
69
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
710
"username": "your_username@your_tenant.com",
811
"scope": ["User.ReadBasic.All"],
@@ -33,7 +36,8 @@
3336
# logging.getLogger("msal").setLevel(logging.INFO) # Optionally disable MSAL DEBUG logs
3437

3538
config = json.load(open(sys.argv[1]))
36-
config["password"] = getpass.getpass()
39+
config["password"] = getpass.getpass(
40+
prompt="Enter password for {}: ".format(config["username"]))
3741

3842
# If for whatever reason you plan to recreate same ClientApplication periodically,
3943
# you shall create one global token cache and reuse it by each ClientApplication
@@ -44,7 +48,8 @@
4448
global_app = msal.ClientApplication(
4549
config["client_id"],
4650
client_credential=config.get("client_secret"),
47-
authority=config["authority"],
51+
authority=config.get("authority"),
52+
oidc_authority=config.get("oidc_authority"), # Use this for a CIAM custom domain
4853
token_cache=global_token_cache, # Let this app (re)use an existing token cache.
4954
# If absent, ClientApplication will create its own empty token cache
5055
)

0 commit comments

Comments
 (0)