@@ -1142,7 +1142,7 @@ def _find_msal_accounts(self, environment):
1142
1142
"local_account_id" : a .get ("local_account_id" ), # Tenant-specific
1143
1143
"realm" : a .get ("realm" ), # Tenant-specific
1144
1144
}
1145
- for a in self .token_cache .find (
1145
+ for a in self .token_cache .search (
1146
1146
TokenCache .CredentialType .ACCOUNT ,
1147
1147
query = {"environment" : environment })
1148
1148
if a ["authority_type" ] in interested_authority_types
@@ -1188,18 +1188,22 @@ def _sign_out(self, home_account):
1188
1188
"home_account_id" : home_account ["home_account_id" ],} # realm-independent
1189
1189
app_metadata = self ._get_app_metadata (home_account ["environment" ])
1190
1190
# Remove RTs/FRTs, and they are realm-independent
1191
- for rt in [rt for rt in self .token_cache .find (
1191
+ for rt in [ # Remove RTs from a static list (rather than from a dynamic generator),
1192
+ # to avoid changing self.token_cache while it is being iterated
1193
+ rt for rt in self .token_cache .search (
1192
1194
TokenCache .CredentialType .REFRESH_TOKEN , query = owned_by_home_account )
1193
1195
# Do RT's app ownership check as a precaution, in case family apps
1194
1196
# and 3rd-party apps share same token cache, although they should not.
1195
1197
if rt ["client_id" ] == self .client_id or (
1196
1198
app_metadata .get ("family_id" ) # Now let's settle family business
1197
1199
and rt .get ("family_id" ) == app_metadata ["family_id" ])
1198
- ]:
1200
+ ]:
1199
1201
self .token_cache .remove_rt (rt )
1200
- for at in self .token_cache .find ( # Remove ATs
1201
- # Regardless of realm, b/c we've removed realm-independent RTs anyway
1202
- TokenCache .CredentialType .ACCESS_TOKEN , query = owned_by_home_account ):
1202
+ for at in list (self .token_cache .search ( # Remove ATs from a static list,
1203
+ # to avoid changing self.token_cache while it is being iterated
1204
+ TokenCache .CredentialType .ACCESS_TOKEN , query = owned_by_home_account ,
1205
+ # Regardless of realm, b/c we've removed realm-independent RTs anyway
1206
+ )):
1203
1207
# To avoid the complexity of locating sibling family app's AT,
1204
1208
# we skip AT's app ownership check.
1205
1209
# It means ATs for other apps will also be removed, it is OK because:
@@ -1213,11 +1217,15 @@ def _forget_me(self, home_account):
1213
1217
owned_by_home_account = {
1214
1218
"environment" : home_account ["environment" ],
1215
1219
"home_account_id" : home_account ["home_account_id" ],} # realm-independent
1216
- for idt in self .token_cache .find ( # Remove IDTs, regardless of realm
1217
- TokenCache .CredentialType .ID_TOKEN , query = owned_by_home_account ):
1220
+ for idt in list (self .token_cache .search ( # Remove IDTs from a static list,
1221
+ # to avoid changing self.token_cache while it is being iterated
1222
+ TokenCache .CredentialType .ID_TOKEN , query = owned_by_home_account , # regardless of realm
1223
+ )):
1218
1224
self .token_cache .remove_idt (idt )
1219
- for a in self .token_cache .find ( # Remove Accounts, regardless of realm
1220
- TokenCache .CredentialType .ACCOUNT , query = owned_by_home_account ):
1225
+ for a in list (self .token_cache .search ( # Remove Accounts from a static list,
1226
+ # to avoid changing self.token_cache while it is being iterated
1227
+ TokenCache .CredentialType .ACCOUNT , query = owned_by_home_account , # regardless of realm
1228
+ )):
1221
1229
self .token_cache .remove_account (a )
1222
1230
1223
1231
def _acquire_token_by_cloud_shell (self , scopes , data = None ):
@@ -1350,12 +1358,12 @@ def _acquire_token_silent_with_error(
1350
1358
return result
1351
1359
final_result = result
1352
1360
for alias in self ._get_authority_aliases (self .authority .instance ):
1353
- if not self .token_cache .find (
1361
+ if not list ( self .token_cache .search ( # Need a list to test emptiness
1354
1362
self .token_cache .CredentialType .REFRESH_TOKEN ,
1355
1363
# target=scopes, # MUST NOT filter by scopes, because:
1356
1364
# 1. AAD RTs are scope-independent;
1357
1365
# 2. therefore target is optional per schema;
1358
- query = {"environment" : alias }):
1366
+ query = {"environment" : alias })) :
1359
1367
# Skip heavy weight logic when RT for this alias doesn't exist
1360
1368
continue
1361
1369
the_authority = Authority (
@@ -1410,11 +1418,13 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1410
1418
query ["key_id" ] = key_id
1411
1419
now = time .time ()
1412
1420
refresh_reason = msal .telemetry .AT_ABSENT
1413
- for entry in self .token_cache ._find ( # It returns a generator
1421
+ for entry in self .token_cache .search ( # A generator allows us to
1422
+ # break early in cache-hit without finding a full list
1414
1423
self .token_cache .CredentialType .ACCESS_TOKEN ,
1415
1424
target = scopes ,
1416
1425
query = query ,
1417
- ): # Note that _find() holds a lock during this for loop;
1426
+ ): # This loop is about token search, not about token deletion.
1427
+ # Note that search() holds a lock during this loop;
1418
1428
# that is fine because this loop is fast
1419
1429
expires_in = int (entry ["expires_on" ]) - now
1420
1430
if expires_in < 5 * 60 : # Then consider it expired
@@ -1552,10 +1562,10 @@ def _acquire_token_silent_by_finding_specific_refresh_token(
1552
1562
rt_remover = None , break_condition = lambda response : False ,
1553
1563
refresh_reason = None , correlation_id = None , claims_challenge = None ,
1554
1564
** kwargs ):
1555
- matches = self .token_cache .find (
1565
+ matches = list ( self .token_cache .search ( # We want a list to test emptiness
1556
1566
self .token_cache .CredentialType .REFRESH_TOKEN ,
1557
1567
# target=scopes, # AAD RTs are scope-independent
1558
- query = query )
1568
+ query = query ))
1559
1569
logger .debug ("Found %d RTs matching %s" , len (matches ), {
1560
1570
k : _pii_less_home_account_id (v ) if k == "home_account_id" and v else v
1561
1571
for k , v in query .items ()
@@ -2252,11 +2262,12 @@ def remove_tokens_for_client(self):
2252
2262
:func:`~acquire_token_for_client()` for the current client."""
2253
2263
for env in [self .authority .instance ] + self ._get_authority_aliases (
2254
2264
self .authority .instance ):
2255
- for at in self .token_cache .find (TokenCache .CredentialType .ACCESS_TOKEN , query = {
2265
+ for at in list (self .token_cache .search ( # Remove ATs from a snapshot
2266
+ TokenCache .CredentialType .ACCESS_TOKEN , query = {
2256
2267
"client_id" : self .client_id ,
2257
2268
"environment" : env ,
2258
2269
"home_account_id" : None , # These are mostly app-only tokens
2259
- }):
2270
+ })) :
2260
2271
self .token_cache .remove_at (at )
2261
2272
# acquire_token_for_client() obtains no RTs, so we have no RT to remove
2262
2273
0 commit comments