26
26
27
27
28
28
# The __init__.py will import this. Not the other way around.
29
- __version__ = "1.15 .0"
29
+ __version__ = "1.16 .0"
30
30
31
31
logger = logging .getLogger (__name__ )
32
32
@@ -170,6 +170,7 @@ def __init__(
170
170
# This way, it holds the same positional param place for PCA,
171
171
# when we would eventually want to add this feature to PCA in future.
172
172
exclude_scopes = None ,
173
+ http_cache = None ,
173
174
):
174
175
"""Create an instance of application.
175
176
@@ -285,7 +286,8 @@ def __init__(
285
286
which you will later provide via one of the acquire-token request.
286
287
287
288
:param str azure_region:
288
- Added since MSAL Python 1.12.0.
289
+ AAD provides regional endpoints for apps to opt in
290
+ to keep their traffic remain inside that region.
289
291
290
292
As of 2021 May, regional service is only available for
291
293
``acquire_token_for_client()`` sent by any of the following scenarios::
@@ -302,9 +304,7 @@ def __init__(
302
304
303
305
4. An app which already onboard to the region's allow-list.
304
306
305
- MSAL's default value is None, which means region behavior remains off.
306
- If enabled, the `acquire_token_for_client()`-relevant traffic
307
- would remain inside that region.
307
+ This parameter defaults to None, which means region behavior remains off.
308
308
309
309
App developer can opt in to a regional endpoint,
310
310
by provide its region name, such as "westus", "eastus2".
@@ -330,12 +330,69 @@ def __init__(
330
330
or provide a custom http_client which has a short timeout.
331
331
That way, the latency would be under your control,
332
332
but still less performant than opting out of region feature.
333
+
334
+ New in version 1.12.0.
335
+
333
336
:param list[str] exclude_scopes: (optional)
334
337
Historically MSAL hardcodes `offline_access` scope,
335
338
which would allow your app to have prolonged access to user's data.
336
339
If that is unnecessary or undesirable for your app,
337
340
now you can use this parameter to supply an exclusion list of scopes,
338
341
such as ``exclude_scopes = ["offline_access"]``.
342
+
343
+ :param dict http_cache:
344
+ MSAL has long been caching tokens in the ``token_cache``.
345
+ Recently, MSAL also introduced a concept of ``http_cache``,
346
+ by automatically caching some finite amount of non-token http responses,
347
+ so that *long-lived*
348
+ ``PublicClientApplication`` and ``ConfidentialClientApplication``
349
+ would be more performant and responsive in some situations.
350
+
351
+ This ``http_cache`` parameter accepts any dict-like object.
352
+ If not provided, MSAL will use an in-memory dict.
353
+
354
+ If your app is a command-line app (CLI),
355
+ you would want to persist your http_cache across different CLI runs.
356
+ The following recipe shows a way to do so::
357
+
358
+ # Just add the following lines at the beginning of your CLI script
359
+ import sys, atexit, pickle
360
+ http_cache_filename = sys.argv[0] + ".http_cache"
361
+ try:
362
+ with open(http_cache_filename, "rb") as f:
363
+ persisted_http_cache = pickle.load(f) # Take a snapshot
364
+ except (
365
+ IOError, # A non-exist http cache file
366
+ pickle.UnpicklingError, # A corrupted http cache file
367
+ EOFError, # An empty http cache file
368
+ AttributeError, ImportError, IndexError, # Other corruption
369
+ ):
370
+ persisted_http_cache = {} # Recover by starting afresh
371
+ atexit.register(lambda: pickle.dump(
372
+ # When exit, flush it back to the file.
373
+ # It may occasionally overwrite another process's concurrent write,
374
+ # but that is fine. Subsequent runs will reach eventual consistency.
375
+ persisted_http_cache, open(http_cache_file, "wb")))
376
+
377
+ # And then you can implement your app as you normally would
378
+ app = msal.PublicClientApplication(
379
+ "your_client_id",
380
+ ...,
381
+ http_cache=persisted_http_cache, # Utilize persisted_http_cache
382
+ ...,
383
+ #token_cache=..., # You may combine the old token_cache trick
384
+ # Please refer to token_cache recipe at
385
+ # https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
386
+ )
387
+ app.acquire_token_interactive(["your", "scope"], ...)
388
+
389
+ Content inside ``http_cache`` are cheap to obtain.
390
+ There is no need to share them among different apps.
391
+
392
+ Content inside ``http_cache`` will contain no tokens nor
393
+ Personally Identifiable Information (PII). Encryption is unnecessary.
394
+
395
+ New in version 1.16.0.
339
396
"""
340
397
self .client_id = client_id
341
398
self .client_credential = client_credential
@@ -370,7 +427,7 @@ def __init__(
370
427
self .http_client .mount ("https://" , a )
371
428
self .http_client = ThrottledHttpClient (
372
429
self .http_client ,
373
- {} # Hard code an in-memory cache, for now
430
+ {} if http_cache is None else http_cache , # Default to an in-memory dict
374
431
)
375
432
376
433
self .app_name = app_name
@@ -437,17 +494,18 @@ def _build_telemetry_context(
437
494
correlation_id = correlation_id , refresh_reason = refresh_reason )
438
495
439
496
def _get_regional_authority (self , central_authority ):
440
- is_region_specified = bool (self ._region_configured
441
- and self ._region_configured != self .ATTEMPT_REGION_DISCOVERY )
442
497
self ._region_detected = self ._region_detected or _detect_region (
443
498
self .http_client if self ._region_configured is not None else None )
444
- if (is_region_specified and self ._region_configured != self ._region_detected ):
499
+ if (self ._region_configured != self .ATTEMPT_REGION_DISCOVERY
500
+ and self ._region_configured != self ._region_detected ):
445
501
logger .warning ('Region configured ({}) != region detected ({})' .format (
446
502
repr (self ._region_configured ), repr (self ._region_detected )))
447
503
region_to_use = (
448
- self ._region_configured if is_region_specified else self ._region_detected )
504
+ self ._region_detected
505
+ if self ._region_configured == self .ATTEMPT_REGION_DISCOVERY
506
+ else self ._region_configured ) # It will retain the None i.e. opted out
507
+ logger .debug ('Region to be used: {}' .format (repr (region_to_use )))
449
508
if region_to_use :
450
- logger .info ('Region to be used: {}' .format (repr (region_to_use )))
451
509
regional_host = ("{}.r.login.microsoftonline.com" .format (region_to_use )
452
510
if central_authority .instance in (
453
511
# The list came from https://github.com/AzureAD/microsoft-authentication-library-for-python/pull/358/files#r629400328
0 commit comments