Skip to content

Commit 642ea65

Browse files
committed
Add test case to show that OBO supports SP
1 parent 85f4f9e commit 642ea65

File tree

1 file changed

+80
-21
lines changed

1 file changed

+80
-21
lines changed

tests/test_e2e.py

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -570,19 +570,27 @@ def _test_acquire_token_obo(self, config_pca, config_cca,
570570
# Here we just test regional apps won't adversely break OBO
571571
http_client=None,
572572
):
573-
# 1. An app obtains a token representing a user, for our mid-tier service
574-
pca = msal.PublicClientApplication(
575-
config_pca["client_id"], authority=config_pca["authority"],
576-
azure_region=azure_region,
577-
http_client=http_client or MinimalHttpClient())
578-
pca_result = pca.acquire_token_by_username_password(
579-
config_pca["username"],
580-
config_pca["password"],
581-
scopes=config_pca["scope"],
582-
)
583-
self.assertIsNotNone(
584-
pca_result.get("access_token"),
585-
"PCA failed to get AT because %s" % json.dumps(pca_result, indent=2))
573+
if "client_secret" not in config_pca:
574+
# 1.a An app obtains a token representing a user, for our mid-tier service
575+
result = msal.PublicClientApplication(
576+
config_pca["client_id"], authority=config_pca["authority"],
577+
azure_region=azure_region,
578+
http_client=http_client or MinimalHttpClient(),
579+
).acquire_token_by_username_password(
580+
config_pca["username"], config_pca["password"],
581+
scopes=config_pca["scope"],
582+
)
583+
else: # We repurpose the config_pca to contain client_secret for cca app 1
584+
# 1.b An app obtains a token representing itself, for our mid-tier service
585+
result = msal.ConfidentialClientApplication(
586+
config_pca["client_id"], authority=config_pca["authority"],
587+
client_credential=config_pca["client_secret"],
588+
azure_region=azure_region,
589+
http_client=http_client or MinimalHttpClient(),
590+
).acquire_token_for_client(scopes=config_pca["scope"])
591+
assertion = result.get("access_token")
592+
self.assertIsNotNone(assertion, "First app failed to get AT. {}".format(
593+
json.dumps(result, indent=2)))
586594

587595
# 2. Our mid-tier service uses OBO to obtain a token for downstream service
588596
cca = msal.ConfidentialClientApplication(
@@ -595,9 +603,9 @@ def _test_acquire_token_obo(self, config_pca, config_cca,
595603
# That's fine if OBO app uses short-lived msal instance per session.
596604
# Otherwise, the OBO app need to implement a one-cache-per-user setup.
597605
)
598-
cca_result = cca.acquire_token_on_behalf_of(
599-
pca_result['access_token'], config_cca["scope"])
600-
self.assertNotEqual(None, cca_result.get("access_token"), str(cca_result))
606+
cca_result = cca.acquire_token_on_behalf_of(assertion, config_cca["scope"])
607+
self.assertIsNotNone(cca_result.get("access_token"), "OBO call failed: {}".format(
608+
json.dumps(cca_result, indent=2)))
601609

602610
# 3. Now the OBO app can simply store downstream token(s) in same session.
603611
# Alternatively, if you want to persist the downstream AT, and possibly
@@ -606,13 +614,27 @@ def _test_acquire_token_obo(self, config_pca, config_cca,
606614
# Assuming you already did that (which is not shown in this test case),
607615
# the following part shows one of the ways to obtain an AT from cache.
608616
username = cca_result.get("id_token_claims", {}).get("preferred_username")
609-
if username: # It means CCA have requested an IDT w/ "profile" scope
610-
self.assertEqual(config_cca["username"], username)
611617
accounts = cca.get_accounts(username=username)
612-
assert len(accounts) == 1, "App is expected to partition token cache per user"
613-
account = accounts[0]
618+
if username is not None: # It means CCA have requested an IDT w/ "profile" scope
619+
assert config_cca["username"] == username, "Incorrect test case configuration"
620+
self.assertEqual(1, len(accounts), "App is supposed to partition token cache per user")
621+
account = accounts[0] # Alternatively, cca app could just loop through each account
614622
result = cca.acquire_token_silent(config_cca["scope"], account)
615-
self.assertEqual(cca_result["access_token"], result["access_token"])
623+
self.assertTrue(
624+
result and result.get("access_token") == cca_result["access_token"],
625+
"CCA should hit an access token from cache: {}".format(
626+
json.dumps(cca.token_cache._cache, indent=2)))
627+
if "refresh_token" in cca_result:
628+
result = cca.acquire_token_silent(
629+
config_cca["scope"], account=account, force_refresh=True)
630+
self.assertTrue(
631+
result and "access_token" in result,
632+
"CCA should get an AT silently, but we got this instead: {}".format(result))
633+
self.assertNotEqual(
634+
result["access_token"], cca_result["access_token"],
635+
"CCA should get a new AT")
636+
else:
637+
logger.info("AAD did not issue a RT for OBO flow")
616638

617639
def _test_acquire_token_by_client_secret(
618640
self, client_id=None, client_secret=None, authority=None, scope=None,
@@ -623,6 +645,18 @@ def _test_acquire_token_by_client_secret(
623645
http_client=MinimalHttpClient())
624646
result = app.acquire_token_for_client(scope)
625647
self.assertIsNotNone(result.get("access_token"), "Got %s instead" % result)
648+
result2 = app.acquire_token_silent(scope, account=None)
649+
self.assertEqual(
650+
result2.get("access_token"), result["access_token"],
651+
"CCA should hit an access token from cache: {}".format(
652+
json.dumps(app.token_cache._cache, indent=2))
653+
)
654+
if "refresh_token" in result: # Empirically, RT is unavailable, but just in case...
655+
result3 = app.acquire_token_silent(scope, account=None, force_refresh=True)
656+
error_message = "CCA should get a new AT via RT in cache: {}".format(
657+
json.dumps(app.token_cache._cache, indent=2))
658+
self.assertIsNotNone(result3, error_message)
659+
self.assertNotEqual(result3.get("access_token"), result["access_token"], error_message)
626660

627661

628662
class WorldWideTestCase(LabBasedTestCase):
@@ -732,6 +766,31 @@ def test_acquire_token_obo(self):
732766

733767
self._test_acquire_token_obo(config_pca, config_cca)
734768

769+
@unittest.skipUnless(
770+
os.path.exists("tests/sp_obo.pem"),
771+
"Need a 'tests/sp_obo.pem' private to run OBO for SP test")
772+
def test_acquire_token_obo_for_sp(self):
773+
authority = "https://login.windows-ppe.net/f686d426-8d16-42db-81b7-ab578e110ccd"
774+
with open("tests/sp_obo.pem") as pem:
775+
client_secret = {
776+
"private_key": pem.read(),
777+
"thumbprint": "378938210C976692D7F523B8C4FFBB645D17CE92",
778+
}
779+
midtier_app = {
780+
"authority": authority,
781+
"client_id": "c84e9c32-0bc9-4a73-af05-9efe9982a322",
782+
"client_secret": client_secret,
783+
"scope": ["23d08a1e-1249-4f7c-b5a5-cb11f29b6923/.default"],
784+
#"username": "OBO-Client-PPE", # We do NOT attempt locating initial_app by name
785+
}
786+
initial_app = {
787+
"authority": authority,
788+
"client_id": "9793041b-9078-4942-b1d2-babdc472cc0c",
789+
"client_secret": client_secret,
790+
"scope": [midtier_app["client_id"] + "/.default"],
791+
}
792+
self._test_acquire_token_obo(initial_app, midtier_app)
793+
735794
def test_acquire_token_by_client_secret(self):
736795
# Vastly different than ArlingtonCloudTestCase.test_acquire_token_by_client_secret()
737796
_app = self.get_lab_app_object(

0 commit comments

Comments
 (0)