1+ # It is currently shipped inside msal library.
2+ # Pros: It is always available wherever msal is installed.
3+ # Cons: Its 3rd-party dependencies (if any) may become msal's dependency.
4+ """MSAL Python Tester
5+
6+ Usage 1: Run it on the fly.
7+ python -m msal
8+
9+ Usage 2: Build an all-in-one executable file for bug bash.
10+ shiv -e msal.__main__._main -o msaltest-on-os-name.pyz .
11+ Note: We choose to not define a console script to avoid name conflict.
12+ """
113import base64 , getpass , json , logging , sys , msal
214
3-
4- AZURE_CLI = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"
5- VISUAL_STUDIO = "04f0c124-f2bc-4f59-8241-bf6df9866bbd"
15+ _AZURE_CLI = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"
16+ _VISUAL_STUDIO = "04f0c124-f2bc-4f59-8241-bf6df9866bbd"
617
718def print_json (blob ):
819 print (json .dumps (blob , indent = 2 , sort_keys = True ))
@@ -61,7 +72,7 @@ def _select_account(app):
6172 else :
6273 print ("No account available inside MSAL Python. Use other methods to acquire token first." )
6374
64- def acquire_token_silent (app ):
75+ def _acquire_token_silent (app ):
6576 """acquire_token_silent() - with an account already signed into MSAL Python."""
6677 account = _select_account (app )
6778 if account :
@@ -71,7 +82,8 @@ def acquire_token_silent(app):
7182 force_refresh = _input_boolean ("Bypass MSAL Python's token cache?" ),
7283 ))
7384
74- def _acquire_token_interactive (app , scopes , data = None ):
85+ def _acquire_token_interactive (app , scopes = None , data = None ):
86+ """acquire_token_interactive() - User will be prompted if app opts to do select_account."""
7587 prompt = _select_options ([
7688 {"value" : None , "description" : "Unspecified. Proceed silently with a default account (if any), fallback to prompt." },
7789 {"value" : "none" , "description" : "none. Proceed silently with a default account (if any), or error out." },
@@ -88,78 +100,73 @@ def _acquire_token_interactive(app, scopes, data=None):
88100 )
89101 login_hint = raw_login_hint ["username" ] if isinstance (raw_login_hint , dict ) else raw_login_hint
90102 result = app .acquire_token_interactive (
91- scopes ,
103+ scopes or _input_scopes () ,
92104 parent_window_handle = app .CONSOLE_WINDOW_HANDLE , # This test app is a console app
93105 enable_msa_passthrough = app .client_id in [ # Apps are expected to set this right
94- AZURE_CLI , VISUAL_STUDIO ,
106+ _AZURE_CLI , _VISUAL_STUDIO ,
95107 ], # Here this test app mimics the setting for some known MSA-PT apps
96108 prompt = prompt , login_hint = login_hint , data = data or {})
97109 if login_hint and "id_token_claims" in result :
98110 signed_in_user = result .get ("id_token_claims" , {}).get ("preferred_username" )
99111 if signed_in_user != login_hint :
100112 logging .warning ('Signed-in user "%s" does not match login_hint' , signed_in_user )
113+ print_json (result )
101114 return result
102115
103- def acquire_token_interactive (app ):
104- """acquire_token_interactive() - User will be prompted if app opts to do select_account."""
105- print_json (_acquire_token_interactive (app , _input_scopes ()))
106-
107- def acquire_token_by_username_password (app ):
116+ def _acquire_token_by_username_password (app ):
108117 """acquire_token_by_username_password() - See constraints here: https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-authentication-flows#constraints-for-ropc"""
109118 print_json (app .acquire_token_by_username_password (
110119 _input ("username: " ), getpass .getpass ("password: " ), scopes = _input_scopes ()))
111120
112121_JWK1 = """{"kty":"RSA", "n":"2tNr73xwcj6lH7bqRZrFzgSLj7OeLfbn8216uOMDHuaZ6TEUBDN8Uz0ve8jAlKsP9CQFCSVoSNovdE-fs7c15MxEGHjDcNKLWonznximj8pDGZQjVdfK-7mG6P6z-lgVcLuYu5JcWU_PeEqIKg5llOaz-qeQ4LEDS4T1D2qWRGpAra4rJX1-kmrWmX_XIamq30C9EIO0gGuT4rc2hJBWQ-4-FnE1NXmy125wfT3NdotAJGq5lMIfhjfglDbJCwhc8Oe17ORjO3FsB5CLuBRpYmP7Nzn66lRY3Fe11Xz8AEBl3anKFSJcTvlMnFtu3EpD-eiaHfTgRBU7CztGQqVbiQ", "e":"AQAB"}"""
113- SSH_CERT_DATA = {"token_type" : "ssh-cert" , "key_id" : "key1" , "req_cnf" : _JWK1 }
114- SSH_CERT_SCOPE = ["https://pas.windows.net/CheckMyAccess/Linux/.default" ]
122+ _SSH_CERT_DATA = {"token_type" : "ssh-cert" , "key_id" : "key1" , "req_cnf" : _JWK1 }
123+ _SSH_CERT_SCOPE = ["https://pas.windows.net/CheckMyAccess/Linux/.default" ]
115124
116- def acquire_ssh_cert_silently (app ):
125+ def _acquire_ssh_cert_silently (app ):
117126 """Acquire an SSH Cert silently- This typically only works with Azure CLI"""
118127 account = _select_account (app )
119128 if account :
120129 result = app .acquire_token_silent (
121- SSH_CERT_SCOPE ,
130+ _SSH_CERT_SCOPE ,
122131 account ,
123- data = SSH_CERT_DATA ,
132+ data = _SSH_CERT_DATA ,
124133 force_refresh = _input_boolean ("Bypass MSAL Python's token cache?" ),
125134 )
126135 print_json (result )
127136 if result and result .get ("token_type" ) != "ssh-cert" :
128137 logging .error ("Unable to acquire an ssh-cert." )
129138
130- def acquire_ssh_cert_interactive (app ):
139+ def _acquire_ssh_cert_interactive (app ):
131140 """Acquire an SSH Cert interactively - This typically only works with Azure CLI"""
132- result = _acquire_token_interactive (app , SSH_CERT_SCOPE , data = SSH_CERT_DATA )
133- print_json (result )
141+ result = _acquire_token_interactive (app , scopes = _SSH_CERT_SCOPE , data = _SSH_CERT_DATA )
134142 if result .get ("token_type" ) != "ssh-cert" :
135143 logging .error ("Unable to acquire an ssh-cert" )
136144
137- POP_KEY_ID = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-AAAAAAAA' # Fake key with a certain format and length
138- RAW_REQ_CNF = json .dumps ({"kid" : POP_KEY_ID , "xms_ksl" : "sw" })
139- POP_DATA = { # Sampled from Azure CLI's plugin connectedk8s
145+ _POP_KEY_ID = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-AAAAAAAA' # Fake key with a certain format and length
146+ _RAW_REQ_CNF = json .dumps ({"kid" : _POP_KEY_ID , "xms_ksl" : "sw" })
147+ _POP_DATA = { # Sampled from Azure CLI's plugin connectedk8s
140148 'token_type' : 'pop' ,
141- 'key_id' : POP_KEY_ID ,
142- "req_cnf" : base64 .urlsafe_b64encode (RAW_REQ_CNF .encode ('utf-8' )).decode ('utf-8' ).rstrip ('=' ),
143- # Note: Sending RAW_REQ_CNF without base64 encoding would result in an http 500 error
149+ 'key_id' : _POP_KEY_ID ,
150+ "req_cnf" : base64 .urlsafe_b64encode (_RAW_REQ_CNF .encode ('utf-8' )).decode ('utf-8' ).rstrip ('=' ),
151+ # Note: Sending _RAW_REQ_CNF without base64 encoding would result in an http 500 error
144152} # See also https://github.com/Azure/azure-cli-extensions/blob/main/src/connectedk8s/azext_connectedk8s/_clientproxyutils.py#L86-L92
145153
146- def acquire_pop_token_interactive (app ):
154+ def _acquire_pop_token_interactive (app ):
147155 """Acquire a POP token interactively - This typically only works with Azure CLI"""
148156 POP_SCOPE = ['6256c85f-0aad-4d50-b960-e6e9b21efe35/.default' ] # KAP 1P Server App Scope, obtained from https://github.com/Azure/azure-cli-extensions/pull/4468/files#diff-a47efa3186c7eb4f1176e07d0b858ead0bf4a58bfd51e448ee3607a5b4ef47f6R116
149- result = _acquire_token_interactive (app , POP_SCOPE , data = POP_DATA )
157+ result = _acquire_token_interactive (app , scopes = POP_SCOPE , data = _POP_DATA )
150158 print_json (result )
151159 if result .get ("token_type" ) != "pop" :
152160 logging .error ("Unable to acquire a pop token" )
153161
154-
155- def remove_account (app ):
162+ def _remove_account (app ):
156163 """remove_account() - Invalidate account and/or token(s) from cache, so that acquire_token_silent() would be reset"""
157164 account = _select_account (app )
158165 if account :
159166 app .remove_account (account )
160167 print ('Account "{}" and/or its token(s) are signed out from MSAL Python' .format (account ["username" ]))
161168
162- def exit (app ):
169+ def _exit (app ):
163170 """Exit"""
164171 bug_link = (
165172 "https://identitydivision.visualstudio.com/Engineering/_queries/query/79b3a352-a775-406f-87cd-a487c382a8ed/"
@@ -169,11 +176,11 @@ def exit(app):
169176 print ("Bye. If you found a bug, please report it here: {}" .format (bug_link ))
170177 sys .exit ()
171178
172- def main ():
173- print ("Welcome to the Msal Python {} Tester\n " .format (msal .__version__ ))
179+ def _main ():
180+ print ("Welcome to the Msal Python {} Tester (Experimental) \n " .format (msal .__version__ ))
174181 chosen_app = _select_options ([
175- {"client_id" : AZURE_CLI , "name" : "Azure CLI (Correctly configured for MSA-PT)" },
176- {"client_id" : VISUAL_STUDIO , "name" : "Visual Studio (Correctly configured for MSA-PT)" },
182+ {"client_id" : _AZURE_CLI , "name" : "Azure CLI (Correctly configured for MSA-PT)" },
183+ {"client_id" : _VISUAL_STUDIO , "name" : "Visual Studio (Correctly configured for MSA-PT)" },
177184 {"client_id" : "95de633a-083e-42f5-b444-a4295d8e9314" , "name" : "Whiteboard Services (Non MSA-PT app. Accepts AAD & MSA accounts.)" },
178185 ],
179186 option_renderer = lambda a : a ["name" ],
@@ -201,14 +208,14 @@ def main():
201208 logging .basicConfig (level = logging .DEBUG )
202209 while True :
203210 func = _select_options ([
204- acquire_token_silent ,
205- acquire_token_interactive ,
206- acquire_token_by_username_password ,
207- acquire_ssh_cert_silently ,
208- acquire_ssh_cert_interactive ,
209- acquire_pop_token_interactive ,
210- remove_account ,
211- exit ,
211+ _acquire_token_silent ,
212+ _acquire_token_interactive ,
213+ _acquire_token_by_username_password ,
214+ _acquire_ssh_cert_silently ,
215+ _acquire_ssh_cert_interactive ,
216+ _acquire_pop_token_interactive ,
217+ _remove_account ,
218+ _exit ,
212219 ], option_renderer = lambda f : f .__doc__ , header = "MSAL Python APIs:" )
213220 try :
214221 func (app )
@@ -218,5 +225,5 @@ def main():
218225 print ("Aborted" )
219226
220227if __name__ == "__main__" :
221- main ()
228+ _main ()
222229
0 commit comments