Skip to content

Commit 214ec74

Browse files
committed
PR feedback
1 parent a5e9eb7 commit 214ec74

4 files changed

Lines changed: 64 additions & 5 deletions

File tree

msal/application.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2617,12 +2617,18 @@ def acquire_token_by_user_federated_identity_credential(
26172617

26182618
telemetry_context = self._build_telemetry_context(
26192619
self.ACQUIRE_TOKEN_BY_USER_FIC_ID)
2620+
headers = telemetry_context.generate_headers()
2621+
if username:
2622+
headers["X-AnchorMailbox"] = "upn:{}".format(username)
2623+
elif user_object_id:
2624+
headers["X-AnchorMailbox"] = "Oid:{}@{}".format(
2625+
user_object_id, self.authority.tenant)
26202626
response = _clean_up(self.client.obtain_token_by_user_fic(
26212627
scope=self._decorate_scope(scopes),
26222628
assertion=assertion,
26232629
username=username,
26242630
user_object_id=user_object_id,
2625-
headers=telemetry_context.generate_headers(),
2631+
headers=headers,
26262632
data=dict(
26272633
kwargs.pop("data", {}),
26282634
claims=_merge_claims_challenge_and_capabilities(

msal/oauth2cli/oauth2.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,13 @@ def __init__(
106106
It can also be a callable (recommended),
107107
so that we will do lazy creation of an assertion.
108108
109-
The callable may accept zero arguments (legacy) or one argument.
110-
When it accepts one argument, it will receive a dict containing
111-
``"client_id"``, ``"token_endpoint"``, and optionally ``"fmi_path"``
109+
The callable may accept zero arguments (legacy) or one
110+
required positional argument. Callables whose positional
111+
parameters all have default values (e.g.
112+
``lambda token=token: token``) are treated as zero-arg.
113+
When the callable declares a required positional parameter,
114+
it will receive a dict containing ``"client_id"``,
115+
``"token_endpoint"``, and optionally ``"fmi_path"``
112116
(when an FMI path is set on the current request).
113117
client_assertion_type (str):
114118
The type of your :attr:`client_assertion` parameter.

msal/token_cache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ def __add(self, event, now=None):
416416
}
417417
grant_types_that_establish_an_account = (
418418
_GRANT_TYPE_BROKER, "authorization_code", "password",
419-
Client.DEVICE_FLOW["GRANT_TYPE"])
419+
Client.DEVICE_FLOW["GRANT_TYPE"], "user_fic")
420420
if event.get("grant_type") in grant_types_that_establish_an_account:
421421
account["account_source"] = event["grant_type"]
422422
self.modify(self.CredentialType.ACCOUNT, account, account)

tests/test_application.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,37 @@ def mock_post(url, headers=None, data=None, *args, **kwargs):
11971197
self.assertNotIn("username", captured_data,
11981198
"username should NOT be in body when user_object_id is provided")
11991199

1200+
def test_ccs_routing_header_with_username(self):
1201+
app = self._make_app()
1202+
captured_headers = {}
1203+
1204+
def mock_post(url, headers=None, data=None, *args, **kwargs):
1205+
captured_headers.update(headers or {})
1206+
return MinimalResponse(status_code=200, text=_build_user_fic_response())
1207+
1208+
app.acquire_token_by_user_federated_identity_credential(
1209+
["scope"], assertion="t2", username="user@contoso.com", post=mock_post)
1210+
self.assertEqual("upn:user@contoso.com",
1211+
captured_headers.get("X-AnchorMailbox"),
1212+
"CCS routing header should use UPN format for username path")
1213+
1214+
def test_ccs_routing_header_with_oid(self):
1215+
app = self._make_app()
1216+
captured_headers = {}
1217+
1218+
def mock_post(url, headers=None, data=None, *args, **kwargs):
1219+
captured_headers.update(headers or {})
1220+
return MinimalResponse(status_code=200, text=_build_user_fic_response())
1221+
1222+
app.acquire_token_by_user_federated_identity_credential(
1223+
["scope"], assertion="t2",
1224+
user_object_id="user_oid_123", post=mock_post)
1225+
self.assertIn("X-AnchorMailbox", captured_headers,
1226+
"CCS routing header should be present for OID path")
1227+
self.assertTrue(
1228+
captured_headers["X-AnchorMailbox"].startswith("Oid:"),
1229+
"CCS routing header should use Oid format for user_object_id path")
1230+
12001231

12011232
@patch(_OIDC_DISCOVERY, new=_OIDC_DISCOVERY_MOCK)
12021233
class TestUserFicCacheBehavior(unittest.TestCase):
@@ -1291,6 +1322,24 @@ def mock_post(url, headers=None, data=None, *args, **kwargs):
12911322
self.assertIn("access_token", silent_result)
12921323
self.assertEqual("oid_fic_at", silent_result["access_token"])
12931324

1325+
def test_account_source_is_set_to_user_fic(self):
1326+
"""Accounts created by user_fic should have account_source set."""
1327+
app = self._make_app()
1328+
1329+
def mock_post(url, headers=None, data=None, *args, **kwargs):
1330+
return MinimalResponse(status_code=200, text=_build_user_fic_response(
1331+
uid="user_oid", utid="tenant_id"))
1332+
1333+
app.acquire_token_by_user_federated_identity_credential(
1334+
["https://graph.microsoft.com/.default"],
1335+
assertion="t2", username="user@contoso.com", post=mock_post)
1336+
1337+
accounts = app.get_accounts()
1338+
self.assertTrue(len(accounts) > 0)
1339+
self.assertEqual("user_fic", accounts[0].get("account_source"),
1340+
"FIC accounts should have account_source='user_fic' to avoid "
1341+
"broker path misrouting")
1342+
12941343

12951344
@patch(_OIDC_DISCOVERY, new=_OIDC_DISCOVERY_MOCK)
12961345
class TestUserFicInputValidation(unittest.TestCase):

0 commit comments

Comments
 (0)