Skip to content

Commit 6362813

Browse files
committed
Pivot to remove only RTs of this app or its family
1 parent 167e954 commit 6362813

File tree

2 files changed

+39
-12
lines changed

2 files changed

+39
-12
lines changed

msal/application.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -280,22 +280,47 @@ def _get_authority_aliases(self, instance):
280280
return [alias for alias in group if alias != instance]
281281
return []
282282

283-
def remove_account(self, home_account):
284-
"""Remove all relevant RTs and ATs from token cache"""
285-
owned_by_account = {
283+
def remove_account(self, account):
284+
"""Sign me out and forget me from token cache"""
285+
self._forget_me(account)
286+
287+
def _sign_out(self, home_account):
288+
# Remove all relevant RTs and ATs from token cache
289+
owned_by_home_account = {
286290
"environment": home_account["environment"],
287291
"home_account_id": home_account["home_account_id"],} # realm-independent
288-
for rt in self.token_cache.find( # Remove RTs, and RTs are realm-independent
289-
TokenCache.CredentialType.REFRESH_TOKEN, query=owned_by_account):
292+
app_metadata = self._get_app_metadata(home_account["environment"])
293+
# Remove RTs/FRTs, and they are realm-independent
294+
for rt in [rt for rt in self.token_cache.find(
295+
TokenCache.CredentialType.REFRESH_TOKEN, query=owned_by_home_account)
296+
# Do RT's app ownership check as a precaution, in case family apps
297+
# and 3rd-party apps share same token cache, although they should not.
298+
if rt["client_id"] == self.client_id or (
299+
app_metadata.get("family_id") # Now let's settle family business
300+
and rt.get("family_id") == app_metadata["family_id"])
301+
]:
290302
self.token_cache.remove_rt(rt)
291-
for at in self.token_cache.find( # Remove ATs, regardless of realm
292-
TokenCache.CredentialType.ACCESS_TOKEN, query=owned_by_account):
303+
for at in self.token_cache.find( # Remove ATs
304+
# Regardless of realm, b/c we've removed realm-independent RTs anyway
305+
TokenCache.CredentialType.ACCESS_TOKEN, query=owned_by_home_account):
306+
# To avoid the complexity of locating sibling family app's AT,
307+
# we skip AT's app ownership check.
308+
# It means ATs for other apps will also be removed, it is OK because:
309+
# * non-family apps are not supposed to share token cache to begin with;
310+
# * Even if it happens, we keep other app's RT already, so SSO still works
293311
self.token_cache.remove_at(at)
312+
313+
def _forget_me(self, home_account):
314+
# It implies signout, and then also remove all relevant accounts and IDTs
315+
self._sign_out(home_account)
316+
owned_by_home_account = {
317+
"environment": home_account["environment"],
318+
"home_account_id": home_account["home_account_id"],} # realm-independent
294319
for idt in self.token_cache.find( # Remove IDTs, regardless of realm
295-
TokenCache.CredentialType.ID_TOKEN, query=owned_by_account):
320+
TokenCache.CredentialType.ID_TOKEN, query=owned_by_home_account):
296321
self.token_cache.remove_idt(idt)
297322
for a in self.token_cache.find( # Remove Accounts, regardless of realm
298-
TokenCache.CredentialType.ACCOUNT, query=owned_by_account):
323+
TokenCache.CredentialType.ACCOUNT, query=owned_by_home_account):
299324
self.token_cache.remove_account(a)
300325

301326
def acquire_token_silent(

tests/test_application.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,9 @@ def setUp(self):
174174
self.account = {"home_account_id": "{}.{}".format(self.uid, self.utid)}
175175
self.frt = "what the frt"
176176
self.cache = msal.SerializableTokenCache()
177+
self.preexisting_family_app_id = "preexisting_family_app"
177178
self.cache.add({ # Pre-populate a FRT
178-
"client_id": "preexisting_family_app",
179+
"client_id": self.preexisting_family_app_id,
179180
"scope": self.scopes,
180181
"token_endpoint": "{}/oauth2/v2.0/token".format(self.authority_url),
181182
"response": TokenCacheTestCase.build_response(
@@ -241,10 +242,11 @@ def tester(url, data=None, **kwargs):
241242

242243
# Will not test scenario of app leaving family. Per specs, it won't happen.
243244

244-
def test_get_remove_account(self):
245+
def test_family_app_remove_account(self):
245246
logger.debug("%s.cache = %s", self.id(), self.cache.serialize())
246247
app = ClientApplication(
247-
"family_app_2", authority=self.authority_url, token_cache=self.cache)
248+
self.preexisting_family_app_id,
249+
authority=self.authority_url, token_cache=self.cache)
248250
account = app.get_accounts()[0]
249251
mine = {"home_account_id": account["home_account_id"]}
250252

0 commit comments

Comments
 (0)