@@ -60,6 +60,17 @@ def _select_options(
60
60
if raw_data and accept_nonempty_string :
61
61
return raw_data
62
62
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
+
63
74
def _input_scopes ():
64
75
scopes = _select_options ([
65
76
"https://graph.microsoft.com/.default" ,
@@ -100,6 +111,7 @@ def _acquire_token_silent(app):
100
111
101
112
def _acquire_token_interactive (app , scopes = None , data = None ):
102
113
"""acquire_token_interactive() - User will be prompted if app opts to do select_account."""
114
+ assert isinstance (app , msal .PublicClientApplication )
103
115
scopes = scopes or _input_scopes () # Let user input scope param before less important prompt and login_hint
104
116
prompt = _select_options ([
105
117
{"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):
143
155
144
156
def _acquire_token_by_device_flow (app ):
145
157
"""acquire_token_by_device_flow() - Note that this one does not go through broker"""
158
+ assert isinstance (app , msal .PublicClientApplication )
146
159
flow = app .initiate_device_flow (scopes = _input_scopes ())
147
160
print (flow ["message" ])
148
161
sys .stdout .flush () # Some terminal needs this to ensure the message is shown
@@ -156,6 +169,7 @@ def _acquire_token_by_device_flow(app):
156
169
157
170
def _acquire_ssh_cert_silently (app ):
158
171
"""Acquire an SSH Cert silently- This typically only works with Azure CLI"""
172
+ assert isinstance (app , msal .PublicClientApplication )
159
173
account = _select_account (app )
160
174
if account :
161
175
result = app .acquire_token_silent (
@@ -170,6 +184,7 @@ def _acquire_ssh_cert_silently(app):
170
184
171
185
def _acquire_ssh_cert_interactive (app ):
172
186
"""Acquire an SSH Cert interactively - This typically only works with Azure CLI"""
187
+ assert isinstance (app , msal .PublicClientApplication )
173
188
result = _acquire_token_interactive (app , scopes = _SSH_CERT_SCOPE , data = _SSH_CERT_DATA )
174
189
if result .get ("token_type" ) != "ssh-cert" :
175
190
logging .error ("Unable to acquire an ssh-cert" )
@@ -185,6 +200,7 @@ def _acquire_ssh_cert_interactive(app):
185
200
186
201
def _acquire_pop_token_interactive (app ):
187
202
"""Acquire a POP token interactively - This typically only works with Azure CLI"""
203
+ assert isinstance (app , msal .PublicClientApplication )
188
204
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
189
205
result = _acquire_token_interactive (app , scopes = POP_SCOPE , data = _POP_DATA )
190
206
print_json (result )
@@ -198,6 +214,16 @@ def _remove_account(app):
198
214
app .remove_account (account )
199
215
print ('Account "{}" and/or its token(s) are signed out from MSAL Python' .format (account ["username" ]))
200
216
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
+
201
227
def _exit (app ):
202
228
"""Exit"""
203
229
bug_link = (
@@ -235,12 +261,23 @@ def _main():
235
261
{"client_id" : _AZURE_CLI , "name" : "Azure CLI (Correctly configured for MSA-PT)" },
236
262
{"client_id" : _VISUAL_STUDIO , "name" : "Visual Studio (Correctly configured for MSA-PT)" },
237
263
{"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
+ },
238
270
],
239
271
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)" ,
241
274
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
+ )
244
281
enable_pii_log = _input_boolean ("Enable PII in broker's log?" ) if enable_broker and enable_debug_log else False
245
282
authority = _select_options ([
246
283
"https://login.microsoftonline.com/common" ,
@@ -255,29 +292,44 @@ def _main():
255
292
instance_discovery = _input_boolean (
256
293
"You input an unusual authority which might fail the Instance Discovery. "
257
294
"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
259
297
app = msal .PublicClientApplication (
260
298
chosen_app ["client_id" ] if isinstance (chosen_app , dict ) else chosen_app ,
261
299
authority = authority ,
262
300
instance_discovery = instance_discovery ,
263
301
enable_broker_on_windows = enable_broker ,
264
302
enable_pii_log = enable_pii_log ,
265
303
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 ,
266
311
)
267
- if enable_debug_log :
268
- logging .basicConfig (level = logging .DEBUG )
269
- while True :
270
- func = _select_options ([
312
+ methods_to_be_tested = [
271
313
_acquire_token_silent ,
314
+ ] + ([
272
315
_acquire_token_interactive ,
273
- _acquire_token_by_username_password ,
274
316
_acquire_token_by_device_flow ,
275
317
_acquire_ssh_cert_silently ,
276
318
_acquire_ssh_cert_interactive ,
277
319
_acquire_pop_token_interactive ,
320
+ ] if isinstance (app , msal .PublicClientApplication ) else []
321
+ ) + [
322
+ _acquire_token_by_username_password ,
278
323
_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:" )
281
333
try :
282
334
func (app )
283
335
except ValueError as e :
0 commit comments