@@ -182,6 +182,10 @@ class ClientApplication(object):
182
182
_TOKEN_SOURCE_BROKER = "broker"
183
183
184
184
_enable_broker = False
185
+ _AUTH_SCHEME_UNSUPPORTED = (
186
+ "auth_scheme is currently only available from broker. "
187
+ "You can enable broker by following these instructions. "
188
+ "https://msal-python.readthedocs.io/en/latest/#publicclientapplication" )
185
189
186
190
def __init__ (
187
191
self , client_id ,
@@ -557,6 +561,10 @@ def _decide_broker(self, allow_broker, enable_pii_log):
557
561
"We will fallback to non-broker." )
558
562
logger .debug ("Broker enabled? %s" , self ._enable_broker )
559
563
564
+ def is_pop_supported (self ):
565
+ """Returns True if this client supports Proof-of-Possession Access Token."""
566
+ return self ._enable_broker
567
+
560
568
def _decorate_scope (
561
569
self , scopes ,
562
570
reserved_scope = frozenset (['openid' , 'profile' , 'offline_access' ])):
@@ -1185,6 +1193,7 @@ def acquire_token_silent(
1185
1193
authority = None , # See get_authorization_request_url()
1186
1194
force_refresh = False , # type: Optional[boolean]
1187
1195
claims_challenge = None ,
1196
+ auth_scheme = None ,
1188
1197
** kwargs ):
1189
1198
"""Acquire an access token for given account, without user interaction.
1190
1199
@@ -1205,7 +1214,7 @@ def acquire_token_silent(
1205
1214
return None # A backward-compatible NO-OP to drop the account=None usage
1206
1215
result = _clean_up (self ._acquire_token_silent_with_error (
1207
1216
scopes , account , authority = authority , force_refresh = force_refresh ,
1208
- claims_challenge = claims_challenge , ** kwargs ))
1217
+ claims_challenge = claims_challenge , auth_scheme = auth_scheme , ** kwargs ))
1209
1218
return result if result and "error" not in result else None
1210
1219
1211
1220
def acquire_token_silent_with_error (
@@ -1215,6 +1224,7 @@ def acquire_token_silent_with_error(
1215
1224
authority = None , # See get_authorization_request_url()
1216
1225
force_refresh = False , # type: Optional[boolean]
1217
1226
claims_challenge = None ,
1227
+ auth_scheme = None ,
1218
1228
** kwargs ):
1219
1229
"""Acquire an access token for given account, without user interaction.
1220
1230
@@ -1241,6 +1251,12 @@ def acquire_token_silent_with_error(
1241
1251
in the form of a claims_challenge directive in the www-authenticate header to be
1242
1252
returned from the UserInfo Endpoint and/or in the ID Token and/or Access Token.
1243
1253
It is a string of a JSON object which contains lists of claims being requested from these locations.
1254
+ :param object auth_scheme:
1255
+ You can provide an ``msal.auth_scheme.PopAuthScheme`` object
1256
+ so that MSAL will get a Proof-of-Possession (POP) token for you.
1257
+
1258
+ New in version 1.26.0.
1259
+
1244
1260
:return:
1245
1261
- A dict containing no "error" key,
1246
1262
and typically contains an "access_token" key,
@@ -1252,7 +1268,7 @@ def acquire_token_silent_with_error(
1252
1268
return None # A backward-compatible NO-OP to drop the account=None usage
1253
1269
return _clean_up (self ._acquire_token_silent_with_error (
1254
1270
scopes , account , authority = authority , force_refresh = force_refresh ,
1255
- claims_challenge = claims_challenge , ** kwargs ))
1271
+ claims_challenge = claims_challenge , auth_scheme = auth_scheme , ** kwargs ))
1256
1272
1257
1273
def _acquire_token_silent_with_error (
1258
1274
self ,
@@ -1261,6 +1277,7 @@ def _acquire_token_silent_with_error(
1261
1277
authority = None , # See get_authorization_request_url()
1262
1278
force_refresh = False , # type: Optional[boolean]
1263
1279
claims_challenge = None ,
1280
+ auth_scheme = None ,
1264
1281
** kwargs ):
1265
1282
assert isinstance (scopes , list ), "Invalid parameter type"
1266
1283
self ._validate_ssh_cert_input_data (kwargs .get ("data" , {}))
@@ -1276,6 +1293,7 @@ def _acquire_token_silent_with_error(
1276
1293
scopes , account , self .authority , force_refresh = force_refresh ,
1277
1294
claims_challenge = claims_challenge ,
1278
1295
correlation_id = correlation_id ,
1296
+ auth_scheme = auth_scheme ,
1279
1297
** kwargs )
1280
1298
if result and "error" not in result :
1281
1299
return result
@@ -1298,6 +1316,7 @@ def _acquire_token_silent_with_error(
1298
1316
scopes , account , the_authority , force_refresh = force_refresh ,
1299
1317
claims_challenge = claims_challenge ,
1300
1318
correlation_id = correlation_id ,
1319
+ auth_scheme = auth_scheme ,
1301
1320
** kwargs )
1302
1321
if result :
1303
1322
if "error" not in result :
@@ -1322,12 +1341,13 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1322
1341
claims_challenge = None ,
1323
1342
correlation_id = None ,
1324
1343
http_exceptions = None ,
1344
+ auth_scheme = None ,
1325
1345
** kwargs ):
1326
1346
# This internal method has two calling patterns:
1327
1347
# it accepts a non-empty account to find token for a user,
1328
1348
# and accepts account=None to find a token for the current app.
1329
1349
access_token_from_cache = None
1330
- if not (force_refresh or claims_challenge ): # Bypass AT when desired or using claims
1350
+ if not (force_refresh or claims_challenge or auth_scheme ): # Then attempt AT cache
1331
1351
query = {
1332
1352
"client_id" : self .client_id ,
1333
1353
"environment" : authority .instance ,
@@ -1370,6 +1390,8 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1370
1390
try :
1371
1391
data = kwargs .get ("data" , {})
1372
1392
if account and account .get ("authority_type" ) == _AUTHORITY_TYPE_CLOUDSHELL :
1393
+ if auth_scheme :
1394
+ raise ValueError ("auth_scheme is not supported in Cloud Shell" )
1373
1395
return self ._acquire_token_by_cloud_shell (scopes , data = data )
1374
1396
1375
1397
if self ._enable_broker and account and account .get ("account_source" ) in (
@@ -1385,6 +1407,7 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1385
1407
claims = _merge_claims_challenge_and_capabilities (
1386
1408
self ._client_capabilities , claims_challenge ),
1387
1409
correlation_id = correlation_id ,
1410
+ auth_scheme = auth_scheme ,
1388
1411
** data )
1389
1412
if response : # Broker provides a decisive outcome
1390
1413
account_was_established_by_broker = account .get (
@@ -1393,6 +1416,8 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1393
1416
if account_was_established_by_broker or broker_attempt_succeeded_just_now :
1394
1417
return self ._process_broker_response (response , scopes , data )
1395
1418
1419
+ if auth_scheme :
1420
+ raise ValueError (self ._AUTH_SCHEME_UNSUPPORTED )
1396
1421
if account :
1397
1422
result = self ._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family (
1398
1423
authority , self ._decorate_scope (scopes ), account ,
@@ -1588,7 +1613,11 @@ def acquire_token_by_refresh_token(self, refresh_token, scopes, **kwargs):
1588
1613
return response
1589
1614
1590
1615
def acquire_token_by_username_password (
1591
- self , username , password , scopes , claims_challenge = None , ** kwargs ):
1616
+ self , username , password , scopes , claims_challenge = None ,
1617
+ # Note: We shouldn't need to surface enable_msa_passthrough,
1618
+ # because this ROPC won't work with MSA account anyway.
1619
+ auth_scheme = None ,
1620
+ ** kwargs ):
1592
1621
"""Gets a token for a given resource via user credentials.
1593
1622
1594
1623
See this page for constraints of Username Password Flow.
@@ -1604,6 +1633,12 @@ def acquire_token_by_username_password(
1604
1633
returned from the UserInfo Endpoint and/or in the ID Token and/or Access Token.
1605
1634
It is a string of a JSON object which contains lists of claims being requested from these locations.
1606
1635
1636
+ :param object auth_scheme:
1637
+ You can provide an ``msal.auth_scheme.PopAuthScheme`` object
1638
+ so that MSAL will get a Proof-of-Possession (POP) token for you.
1639
+
1640
+ New in version 1.26.0.
1641
+
1607
1642
:return: A dict representing the json response from AAD:
1608
1643
1609
1644
- A successful response would contain "access_token" key,
@@ -1623,9 +1658,12 @@ def acquire_token_by_username_password(
1623
1658
self .authority ._is_known_to_developer
1624
1659
or self ._instance_discovery is False ) else None ,
1625
1660
claims = claims ,
1661
+ auth_scheme = auth_scheme ,
1626
1662
)
1627
1663
return self ._process_broker_response (response , scopes , kwargs .get ("data" , {}))
1628
1664
1665
+ if auth_scheme :
1666
+ raise ValueError (self ._AUTH_SCHEME_UNSUPPORTED )
1629
1667
scopes = self ._decorate_scope (scopes )
1630
1668
telemetry_context = self ._build_telemetry_context (
1631
1669
self .ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID )
@@ -1768,6 +1806,7 @@ def acquire_token_interactive(
1768
1806
max_age = None ,
1769
1807
parent_window_handle = None ,
1770
1808
on_before_launching_ui = None ,
1809
+ auth_scheme = None ,
1771
1810
** kwargs ):
1772
1811
"""Acquire token interactively i.e. via a local browser.
1773
1812
@@ -1843,6 +1882,12 @@ def acquire_token_interactive(
1843
1882
1844
1883
New in version 1.20.0.
1845
1884
1885
+ :param object auth_scheme:
1886
+ You can provide an ``msal.auth_scheme.PopAuthScheme`` object
1887
+ so that MSAL will get a Proof-of-Possession (POP) token for you.
1888
+
1889
+ New in version 1.26.0.
1890
+
1846
1891
:return:
1847
1892
- A dict containing no "error" key,
1848
1893
and typically contains an "access_token" key.
@@ -1887,12 +1932,15 @@ def acquire_token_interactive(
1887
1932
claims ,
1888
1933
data ,
1889
1934
on_before_launching_ui ,
1935
+ auth_scheme ,
1890
1936
prompt = prompt ,
1891
1937
login_hint = login_hint ,
1892
1938
max_age = max_age ,
1893
1939
)
1894
1940
return self ._process_broker_response (response , scopes , data )
1895
1941
1942
+ if auth_scheme :
1943
+ raise ValueError ("auth_scheme is currently only available from broker" )
1896
1944
on_before_launching_ui (ui = "browser" )
1897
1945
telemetry_context = self ._build_telemetry_context (
1898
1946
self .ACQUIRE_TOKEN_INTERACTIVE )
@@ -1927,6 +1975,7 @@ def _acquire_token_interactive_via_broker(
1927
1975
claims , # type: str
1928
1976
data , # type: dict
1929
1977
on_before_launching_ui , # type: callable
1978
+ auth_scheme , # type: object
1930
1979
prompt = None ,
1931
1980
login_hint = None , # type: Optional[str]
1932
1981
max_age = None ,
@@ -1950,6 +1999,7 @@ def _acquire_token_interactive_via_broker(
1950
1999
accounts [0 ]["local_account_id" ],
1951
2000
scopes ,
1952
2001
claims = claims ,
2002
+ auth_scheme = auth_scheme ,
1953
2003
** data )
1954
2004
if response and "error" not in response :
1955
2005
return response
@@ -1962,6 +2012,7 @@ def _acquire_token_interactive_via_broker(
1962
2012
claims = claims ,
1963
2013
max_age = max_age ,
1964
2014
enable_msa_pt = enable_msa_passthrough ,
2015
+ auth_scheme = auth_scheme ,
1965
2016
** data )
1966
2017
is_wrong_account = bool (
1967
2018
# _signin_silently() only gets tokens for default account,
@@ -2002,6 +2053,7 @@ def _acquire_token_interactive_via_broker(
2002
2053
claims = claims ,
2003
2054
max_age = max_age ,
2004
2055
enable_msa_pt = enable_msa_passthrough ,
2056
+ auth_scheme = auth_scheme ,
2005
2057
** data )
2006
2058
2007
2059
def initiate_device_flow (self , scopes = None , ** kwargs ):
0 commit comments