@@ -54,6 +54,25 @@ def _get_app_and_auth_code(
54
54
assert ac is not None
55
55
return (app , ac , redirect_uri )
56
56
57
+ def _render (url , description = None ):
58
+ # Render a url in html if description is available, otherwise return url as-is
59
+ return "<a href='{url}' target=_blank>{description}</a>" .format (
60
+ url = url , description = description ) if description else url
61
+
62
+
63
+ def _get_hint (html_mode = None , username = None , lab_name = None , username_uri = None ):
64
+ return "Sign in with {user} whose password is available from {lab}" .format (
65
+ user = ("<b>{}</b>" .format (username ) if html_mode else username )
66
+ if username
67
+ else "the upn from {}" .format (_render (
68
+ username_uri , description = "here" if html_mode else None )),
69
+ lab = _render (
70
+ "https://aka.ms/GetLabUserSecret?Secret=" + (lab_name or "msidlabXYZ" ),
71
+ description = "this password api" if html_mode else None ,
72
+ ),
73
+ )
74
+
75
+
57
76
@unittest .skipIf (os .getenv ("TRAVIS_TAG" ), "Skip e2e tests during tagged release" )
58
77
class E2eTestCase (unittest .TestCase ):
59
78
@@ -212,25 +231,31 @@ def _test_device_flow(
212
231
213
232
def _test_acquire_token_interactive (
214
233
self , client_id = None , authority = None , scope = None , port = None ,
215
- username_uri = "" , # But you would want to provide one
234
+ username = None , lab_name = None ,
235
+ username_uri = "" , # Unnecessary if you provided username and lab_name
216
236
data = None , # Needed by ssh-cert feature
217
237
prompt = None ,
238
+ enable_msa_passthrough = None ,
218
239
** ignored ):
219
240
assert client_id and authority and scope
220
241
self .app = self ._build_app (client_id , authority = authority )
242
+ logger .info (_get_hint ( # Useful when testing broker which shows no welcome_template
243
+ username = username , lab_name = lab_name , username_uri = username_uri ))
221
244
result = self .app .acquire_token_interactive (
222
245
scope ,
246
+ login_hint = username ,
223
247
prompt = prompt ,
224
248
timeout = 120 ,
225
249
port = port ,
226
250
parent_window_handle = self .app .CONSOLE_WINDOW_HANDLE , # This test app is a console app
251
+ enable_msa_passthrough = enable_msa_passthrough , # Needed when testing MSA-PT app
227
252
welcome_template = # This is an undocumented feature for testing
228
253
"""<html><body><h1>{id}</h1><ol>
229
- <li>Get a username from the upn shown at <a href="{username_uri}">here</a></li>
230
- <li>Get its password from https://aka.ms/GetLabUserSecret?Secret=msidlabXYZ
231
- (replace the lab name with the labName from the link above).</li>
254
+ <li>{hint}</li>
232
255
<li><a href="$auth_uri">Sign In</a> or <a href="$abort_uri">Abort</a></li>
233
- </ol></body></html>""" .format (id = self .id (), username_uri = username_uri ),
256
+ </ol></body></html>""" .format (id = self .id (), hint = _get_hint (
257
+ html_mode = True ,
258
+ username = username , lab_name = lab_name , username_uri = username_uri )),
234
259
data = data or {},
235
260
)
236
261
self .assertIn (
@@ -239,6 +264,11 @@ def _test_acquire_token_interactive(
239
264
# Note: No interpolation here, cause error won't always present
240
265
error = result .get ("error" ),
241
266
error_description = result .get ("error_description" )))
267
+ if username and result .get ("id_token_claims" , {}).get ("preferred_username" ):
268
+ self .assertEqual (
269
+ username , result ["id_token_claims" ]["preferred_username" ],
270
+ "You are expected to sign in as account {}, but tokens returned is for {}" .format (
271
+ username , result ["id_token_claims" ]["preferred_username" ]))
242
272
self .assertCacheWorksForUser (result , scope , username = None , data = data or {})
243
273
return result # For further testing
244
274
@@ -260,7 +290,7 @@ def test_ssh_cert_for_service_principal(self):
260
290
self .assertEqual ("ssh-cert" , result ["token_type" ])
261
291
262
292
@unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
263
- def test_ssh_cert_for_user (self ):
293
+ def test_ssh_cert_for_user_should_work_with_any_account (self ):
264
294
result = self ._test_acquire_token_interactive (
265
295
client_id = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" , # Azure CLI is one
266
296
# of the only 2 clients that are PreAuthz to use ssh cert feature
@@ -555,7 +585,8 @@ def _test_acquire_token_by_auth_code(
555
585
556
586
def _test_acquire_token_by_auth_code_flow (
557
587
self , client_id = None , authority = None , port = None , scope = None ,
558
- username_uri = "" , # But you would want to provide one
588
+ username = None , lab_name = None ,
589
+ username_uri = "" , # Optional if you provided username and lab_name
559
590
** ignored ):
560
591
assert client_id and authority and scope
561
592
self .app = msal .ClientApplication (
@@ -568,11 +599,11 @@ def _test_acquire_token_by_auth_code_flow(
568
599
auth_response = receiver .get_auth_response (
569
600
auth_uri = flow ["auth_uri" ], state = flow ["state" ], timeout = 60 ,
570
601
welcome_template = """<html><body><h1>{id}</h1><ol>
571
- <li>Get a username from the upn shown at <a href="{username_uri}">here</a></li>
572
- <li>Get its password from https://aka.ms/GetLabUserSecret?Secret=msidlabXYZ
573
- (replace the lab name with the labName from the link above).</li>
602
+ <li>{hint}</li>
574
603
<li><a href="$auth_uri">Sign In</a> or <a href="$abort_uri">Abort</a></li>
575
- </ol></body></html>""" .format (id = self .id (), username_uri = username_uri ),
604
+ </ol></body></html>""" .format (id = self .id (), hint = _get_hint (
605
+ html_mode = True ,
606
+ username = username , lab_name = lab_name , username_uri = username_uri )),
576
607
)
577
608
if auth_response is None :
578
609
self .skipTest ("Timed out. Did not have test settings in hand? Prepare and retry." )
@@ -592,6 +623,11 @@ def _test_acquire_token_by_auth_code_flow(
592
623
# Note: No interpolation here, cause error won't always present
593
624
error = result .get ("error" ),
594
625
error_description = result .get ("error_description" )))
626
+ if username and result .get ("id_token_claims" , {}).get ("preferred_username" ):
627
+ self .assertEqual (
628
+ username , result ["id_token_claims" ]["preferred_username" ],
629
+ "You are expected to sign in as account {}, but tokens returned is for {}" .format (
630
+ username , result ["id_token_claims" ]["preferred_username" ]))
595
631
self .assertCacheWorksForUser (result , scope , username = None )
596
632
597
633
def _test_acquire_token_obo (self , config_pca , config_cca ,
@@ -689,10 +725,23 @@ def test_adfs2019_fed_user(self):
689
725
690
726
@unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
691
727
def test_cloud_acquire_token_interactive (self ):
692
- config = self .get_lab_user (usertype = "cloud" )
693
- self ._test_acquire_token_interactive (
694
- username_uri = "https://msidlab.com/api/user?usertype=cloud" ,
695
- ** config )
728
+ self ._test_acquire_token_interactive (** self .get_lab_user (usertype = "cloud" ))
729
+
730
+ @unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
731
+ def test_msa_pt_app_signin_via_organizations_authority_without_login_hint (self ):
732
+ """There is/was an upstream bug. See test case full docstring for the details.
733
+
734
+ When a MSAL-PT flow that account control is launched, user has 2+ AAD accounts in WAM,
735
+ selects an AAD account that is NOT the default AAD account from the OS,
736
+ it will incorrectly get tokens for default AAD account.
737
+ """
738
+ self ._test_acquire_token_interactive (** dict (
739
+ self .get_lab_user (usertype = "cloud" ), # This is generally not the current laptop's default AAD account
740
+ authority = "https://login.microsoftonline.com/organizations" ,
741
+ client_id = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" , # Azure CLI is an MSA-PT app
742
+ enable_msa_passthrough = True ,
743
+ prompt = "select_account" , # In MSAL Python, this resets login_hint
744
+ ))
696
745
697
746
def test_ropc_adfs2019_onprem (self ):
698
747
# Configuration is derived from https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/4.7.0/tests/Microsoft.Identity.Test.Common/TestConstants.cs#L250-L259
@@ -719,22 +768,22 @@ def test_adfs2019_onprem_acquire_token_by_auth_code(self):
719
768
@unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
720
769
def test_adfs2019_onprem_acquire_token_by_auth_code_flow (self ):
721
770
config = self .get_lab_user (usertype = "onprem" , federationProvider = "ADFSv2019" )
722
- config [ "authority" ] = "https://fs.%s.com/adfs" % config [ "lab_name" ]
723
- config [ "scope" ] = self . adfs2019_scopes
724
- config ["port" ] = 8080
725
- self ._test_acquire_token_by_auth_code_flow (
726
- username_uri = "https://msidlab.com/api/user?usertype=onprem&federationprovider=ADFSv2019" ,
727
- ** config )
771
+ self . _test_acquire_token_by_auth_code_flow ( ** dict (
772
+ config ,
773
+ authority = "https://fs.%s.com/adfs" % config ["lab_name" ],
774
+ scope = self .adfs2019_scopes ,
775
+ port = 8080 ,
776
+ ) )
728
777
729
778
@unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
730
779
def test_adfs2019_onprem_acquire_token_interactive (self ):
731
780
config = self .get_lab_user (usertype = "onprem" , federationProvider = "ADFSv2019" )
732
- config [ "authority" ] = "https://fs.%s.com/adfs" % config [ "lab_name" ]
733
- config [ "scope" ] = self . adfs2019_scopes
734
- config ["port" ] = 8080
735
- self ._test_acquire_token_interactive (
736
- username_uri = "https://msidlab.com/api/user?usertype=onprem&federationprovider=ADFSv2019" ,
737
- ** config )
781
+ self . _test_acquire_token_interactive ( ** dict (
782
+ config ,
783
+ authority = "https://fs.%s.com/adfs" % config ["lab_name" ],
784
+ scope = self .adfs2019_scopes ,
785
+ port = 8080 ,
786
+ ) )
738
787
739
788
@unittest .skipUnless (
740
789
os .getenv ("LAB_OBO_CLIENT_SECRET" ),
@@ -816,14 +865,12 @@ def test_b2c_acquire_token_by_auth_code(self):
816
865
817
866
@unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
818
867
def test_b2c_acquire_token_by_auth_code_flow (self ):
819
- config = self .get_lab_app_object ( azureenvironment = "azureb2ccloud" )
820
- self ._test_acquire_token_by_auth_code_flow (
868
+ self ._test_acquire_token_by_auth_code_flow ( ** dict (
869
+ self .get_lab_user ( usertype = "b2c" , b2cprovider = "local" ),
821
870
authority = self ._build_b2c_authority ("B2C_1_SignInPolicy" ),
822
- client_id = config ["appId" ],
823
871
port = 3843 , # Lab defines 4 of them: [3843, 4584, 4843, 60000]
824
- scope = config ["scopes" ],
825
- username_uri = "https://msidlab.com/api/user?usertype=b2c&b2cprovider=local" ,
826
- )
872
+ scope = self .get_lab_app_object (azureenvironment = "azureb2ccloud" )["scopes" ],
873
+ ))
827
874
828
875
def test_b2c_acquire_token_by_ropc (self ):
829
876
config = self .get_lab_app_object (azureenvironment = "azureb2ccloud" )
0 commit comments