@@ -60,6 +60,17 @@ def _select_options(
6060 if raw_data and accept_nonempty_string :
6161 return raw_data
6262
63+ enable_debug_log = _input_boolean ("Enable MSAL Python's DEBUG log?" )
64+ logging .basicConfig (level = logging .DEBUG if enable_debug_log else logging .INFO )
65+ try :
66+ from dotenv import load_dotenv
67+ load_dotenv ()
68+ logging .info ("Loaded environment variables from .env file" )
69+ except ImportError :
70+ logging .warning (
71+ "python-dotenv is not installed. "
72+ "You may need to set environment variables manually." )
73+
6374def _input_scopes ():
6475 scopes = _select_options ([
6576 "https://graph.microsoft.com/.default" ,
@@ -100,6 +111,7 @@ def _acquire_token_silent(app):
100111
101112def _acquire_token_interactive (app , scopes = None , data = None ):
102113 """acquire_token_interactive() - User will be prompted if app opts to do select_account."""
114+ assert isinstance (app , msal .PublicClientApplication )
103115 scopes = scopes or _input_scopes () # Let user input scope param before less important prompt and login_hint
104116 prompt = _select_options ([
105117 {"value" : None , "description" : "Unspecified. Proceed silently with a default account (if any), fallback to prompt." },
@@ -143,6 +155,7 @@ def _acquire_token_by_username_password(app):
143155
144156def _acquire_token_by_device_flow (app ):
145157 """acquire_token_by_device_flow() - Note that this one does not go through broker"""
158+ assert isinstance (app , msal .PublicClientApplication )
146159 flow = app .initiate_device_flow (scopes = _input_scopes ())
147160 print (flow ["message" ])
148161 sys .stdout .flush () # Some terminal needs this to ensure the message is shown
@@ -156,6 +169,7 @@ def _acquire_token_by_device_flow(app):
156169
157170def _acquire_ssh_cert_silently (app ):
158171 """Acquire an SSH Cert silently- This typically only works with Azure CLI"""
172+ assert isinstance (app , msal .PublicClientApplication )
159173 account = _select_account (app )
160174 if account :
161175 result = app .acquire_token_silent (
@@ -170,6 +184,7 @@ def _acquire_ssh_cert_silently(app):
170184
171185def _acquire_ssh_cert_interactive (app ):
172186 """Acquire an SSH Cert interactively - This typically only works with Azure CLI"""
187+ assert isinstance (app , msal .PublicClientApplication )
173188 result = _acquire_token_interactive (app , scopes = _SSH_CERT_SCOPE , data = _SSH_CERT_DATA )
174189 if result .get ("token_type" ) != "ssh-cert" :
175190 logging .error ("Unable to acquire an ssh-cert" )
@@ -185,6 +200,7 @@ def _acquire_ssh_cert_interactive(app):
185200
186201def _acquire_pop_token_interactive (app ):
187202 """Acquire a POP token interactively - This typically only works with Azure CLI"""
203+ assert isinstance (app , msal .PublicClientApplication )
188204 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
189205 result = _acquire_token_interactive (app , scopes = POP_SCOPE , data = _POP_DATA )
190206 print_json (result )
@@ -198,6 +214,16 @@ def _remove_account(app):
198214 app .remove_account (account )
199215 print ('Account "{}" and/or its token(s) are signed out from MSAL Python' .format (account ["username" ]))
200216
217+ def _acquire_token_for_client (app ):
218+ """CCA.acquire_token_for_client() - Rerun this will get same token from cache."""
219+ assert isinstance (app , msal .ConfidentialClientApplication )
220+ print_json (app .acquire_token_for_client (scopes = _input_scopes ()))
221+
222+ def _remove_tokens_for_client (app ):
223+ """CCA.remove_tokens_for_client() - Run this to evict tokens from cache."""
224+ assert isinstance (app , msal .ConfidentialClientApplication )
225+ app .remove_tokens_for_client ()
226+
201227def _exit (app ):
202228 """Exit"""
203229 bug_link = (
@@ -235,12 +261,23 @@ def _main():
235261 {"client_id" : _AZURE_CLI , "name" : "Azure CLI (Correctly configured for MSA-PT)" },
236262 {"client_id" : _VISUAL_STUDIO , "name" : "Visual Studio (Correctly configured for MSA-PT)" },
237263 {"client_id" : "95de633a-083e-42f5-b444-a4295d8e9314" , "name" : "Whiteboard Services (Non MSA-PT app. Accepts AAD & MSA accounts.)" },
264+ {
265+ "client_id" : os .getenv ("CLIENT_ID" ),
266+ "client_secret" : os .getenv ("CLIENT_SECRET" ),
267+ "name" : "A confidential client app (CCA) whose settings are defined "
268+ "in environment variables CLIENT_ID and CLIENT_SECRET" ,
269+ },
238270 ],
239271 option_renderer = lambda a : a ["name" ],
240- header = "Impersonate this app (or you can type in the client_id of your own app)" ,
272+ header = "Impersonate this app "
273+ "(or you can type in the client_id of your own public client app)" ,
241274 accept_nonempty_string = True )
242- enable_broker = _input_boolean ("Enable broker? It will error out later if your app has not registered some redirect URI" )
243- enable_debug_log = _input_boolean ("Enable MSAL Python's DEBUG log?" )
275+ is_cca = isinstance (chosen_app , dict ) and "client_secret" in chosen_app
276+ if is_cca and not (chosen_app ["client_id" ] and chosen_app ["client_secret" ]):
277+ raise ValueError ("You need to set environment variables CLIENT_ID and CLIENT_SECRET" )
278+ enable_broker = (not is_cca ) and _input_boolean ("Enable broker? "
279+ "(It will error out later if your app has not registered some redirect URI)"
280+ )
244281 enable_pii_log = _input_boolean ("Enable PII in broker's log?" ) if enable_broker and enable_debug_log else False
245282 authority = _select_options ([
246283 "https://login.microsoftonline.com/common" ,
@@ -255,29 +292,44 @@ def _main():
255292 instance_discovery = _input_boolean (
256293 "You input an unusual authority which might fail the Instance Discovery. "
257294 "Now, do you want to perform Instance Discovery on your input authority?"
258- ) if not authority .startswith ("https://login.microsoftonline.com" ) else None
295+ ) if authority and not authority .startswith (
296+ "https://login.microsoftonline.com" ) else None
259297 app = msal .PublicClientApplication (
260298 chosen_app ["client_id" ] if isinstance (chosen_app , dict ) else chosen_app ,
261299 authority = authority ,
262300 instance_discovery = instance_discovery ,
263301 enable_broker_on_windows = enable_broker ,
264302 enable_pii_log = enable_pii_log ,
265303 token_cache = global_cache ,
304+ ) if not is_cca else msal .ConfidentialClientApplication (
305+ chosen_app ["client_id" ],
306+ client_credential = chosen_app ["client_secret" ],
307+ authority = authority ,
308+ instance_discovery = instance_discovery ,
309+ enable_pii_log = enable_pii_log ,
310+ token_cache = global_cache ,
266311 )
267- if enable_debug_log :
268- logging .basicConfig (level = logging .DEBUG )
269- while True :
270- func = _select_options ([
312+ methods_to_be_tested = [
271313 _acquire_token_silent ,
314+ ] + ([
272315 _acquire_token_interactive ,
273- _acquire_token_by_username_password ,
274316 _acquire_token_by_device_flow ,
275317 _acquire_ssh_cert_silently ,
276318 _acquire_ssh_cert_interactive ,
277319 _acquire_pop_token_interactive ,
320+ ] if isinstance (app , msal .PublicClientApplication ) else []
321+ ) + [
322+ _acquire_token_by_username_password ,
278323 _remove_account ,
279- _exit ,
280- ], option_renderer = lambda f : f .__doc__ , header = "MSAL Python APIs:" )
324+ ] + ([
325+ _acquire_token_for_client ,
326+ _remove_tokens_for_client ,
327+ ] if isinstance (app , msal .ConfidentialClientApplication ) else []
328+ )
329+ while True :
330+ func = _select_options (
331+ methods_to_be_tested + [_exit ],
332+ option_renderer = lambda f : f .__doc__ , header = "MSAL Python APIs:" )
281333 try :
282334 func (app )
283335 except ValueError as e :
0 commit comments