@@ -54,6 +54,25 @@ def _get_app_and_auth_code(
5454 assert ac is not None
5555 return (app , ac , redirect_uri )
5656
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+
5776@unittest .skipIf (os .getenv ("TRAVIS_TAG" ), "Skip e2e tests during tagged release" )
5877class E2eTestCase (unittest .TestCase ):
5978
@@ -212,25 +231,31 @@ def _test_device_flow(
212231
213232 def _test_acquire_token_interactive (
214233 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
216236 data = None , # Needed by ssh-cert feature
217237 prompt = None ,
238+ enable_msa_passthrough = None ,
218239 ** ignored ):
219240 assert client_id and authority and scope
220241 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 ))
221244 result = self .app .acquire_token_interactive (
222245 scope ,
246+ login_hint = username ,
223247 prompt = prompt ,
224248 timeout = 120 ,
225249 port = port ,
226250 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
227252 welcome_template = # This is an undocumented feature for testing
228253 """<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>
232255 <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 )),
234259 data = data or {},
235260 )
236261 self .assertIn (
@@ -239,6 +264,11 @@ def _test_acquire_token_interactive(
239264 # Note: No interpolation here, cause error won't always present
240265 error = result .get ("error" ),
241266 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" ]))
242272 self .assertCacheWorksForUser (result , scope , username = None , data = data or {})
243273 return result # For further testing
244274
@@ -260,7 +290,7 @@ def test_ssh_cert_for_service_principal(self):
260290 self .assertEqual ("ssh-cert" , result ["token_type" ])
261291
262292 @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 ):
264294 result = self ._test_acquire_token_interactive (
265295 client_id = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" , # Azure CLI is one
266296 # of the only 2 clients that are PreAuthz to use ssh cert feature
@@ -555,7 +585,8 @@ def _test_acquire_token_by_auth_code(
555585
556586 def _test_acquire_token_by_auth_code_flow (
557587 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
559590 ** ignored ):
560591 assert client_id and authority and scope
561592 self .app = msal .ClientApplication (
@@ -568,11 +599,11 @@ def _test_acquire_token_by_auth_code_flow(
568599 auth_response = receiver .get_auth_response (
569600 auth_uri = flow ["auth_uri" ], state = flow ["state" ], timeout = 60 ,
570601 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>
574603 <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 )),
576607 )
577608 if auth_response is None :
578609 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(
592623 # Note: No interpolation here, cause error won't always present
593624 error = result .get ("error" ),
594625 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" ]))
595631 self .assertCacheWorksForUser (result , scope , username = None )
596632
597633 def _test_acquire_token_obo (self , config_pca , config_cca ,
@@ -689,10 +725,23 @@ def test_adfs2019_fed_user(self):
689725
690726 @unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
691727 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+ ))
696745
697746 def test_ropc_adfs2019_onprem (self ):
698747 # 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):
719768 @unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
720769 def test_adfs2019_onprem_acquire_token_by_auth_code_flow (self ):
721770 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+ ) )
728777
729778 @unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
730779 def test_adfs2019_onprem_acquire_token_interactive (self ):
731780 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+ ) )
738787
739788 @unittest .skipUnless (
740789 os .getenv ("LAB_OBO_CLIENT_SECRET" ),
@@ -816,14 +865,12 @@ def test_b2c_acquire_token_by_auth_code(self):
816865
817866 @unittest .skipIf (os .getenv ("TRAVIS" ), "Browser automation is not yet implemented" )
818867 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" ),
821870 authority = self ._build_b2c_authority ("B2C_1_SignInPolicy" ),
822- client_id = config ["appId" ],
823871 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+ ))
827874
828875 def test_b2c_acquire_token_by_ropc (self ):
829876 config = self .get_lab_app_object (azureenvironment = "azureb2ccloud" )
0 commit comments