fix: Clear persisted MSAL token cache on Disconnect-MgGraph#3649
fix: Clear persisted MSAL token cache on Disconnect-MgGraph#3649gavinbarron wants to merge 1 commit into
Conversation
When ContextScope is CurrentUser, Disconnect-MgGraph now clears the disk-persisted MSAL token cache (both .cae and .nocae variants) using MsalCacheHelper.Clear() from Microsoft.Identity.Client.Extensions.Msal. Previously only the in-memory cache and auth record file were cleared, leaving cached tokens on disk that could be reused without re-authentication. Fixes #3648 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jsquire
left a comment
There was a problem hiding this comment.
This looks correct to me, though I don't have a deep understanding in the area. I've asked @g2vinay and @JonathanCrd to also review. I'd consider them to be the authoritative voices from the Azure.Identity perspective.
|
Approving the changes, however, in line with @jsquire 's comments, we should await feedback from the folks he mentioned. In order to reduce the change of needing a patch release later on, lets await their sign-off and before merging and releasing. |
|
Agreed |
| { | ||
| try | ||
| { | ||
| await TokenCacheUtilities.ClearPersistedTokenCacheAsync(Constants.CacheName).ConfigureAwait(false); |
There was a problem hiding this comment.
on Windows, WAM is on by default for interactive sign-in, and WAM keeps the account + refresh token in its own cache. So cleaning this file won't actually sign the user out of WAM.
For the broker we would need to ask WAM to forget the account. Is the Windows Broker in scope for this fix?
Copilot suggested something like:
if (ShouldUseWam(authContext))
{
var pca = PublicClientApplicationBuilder
.Create(authContext.ClientId)
.WithAuthority(GetAuthorityUrl(authContext))
.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows))
.Build();
foreach (var account in await pca.GetAccountsAsync().ConfigureAwait(false))
await pca.RemoveAsync(account).ConfigureAwait(false);
}There was a problem hiding this comment.
@JonathanCrd I think we probably need to clear the WAM token cache if possible to ensure a more complete fix.
There was a problem hiding this comment.
Additionally, would using this approach avoid the need to have the TokenCacheUtilities class?
There was a problem hiding this comment.
I don't think so, the broker only covers Windows, and there are non-broker auth flows on windows too.
There was a problem hiding this comment.
@JonathanCrd Sorry, I wasn't very clear here I was more meaning the general approach of using a PCA instance to clear the cached tokens not that specific implementation. Maybe something like this to provide broad cover and eliminate the additional code that knows about the caching specifics
var builder = PublicClientApplicationBuilder
.Create(authContext.ClientId)
.WithAuthority(GetAuthorityUrl(authContext));
if (ShouldUseWam(authContext))
{
builder = builder
.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows));
}
var pca = builder.Build();
foreach (var account in await pca.GetAccountsAsync().ConfigureAwait(false))
await pca.RemoveAsync(account).ConfigureAwait(false);There was a problem hiding this comment.
TokenCacheUtilities -> ClearPersistedTokenCacheAsync still needs to be retained as it deletes the file based caches. PCA code above is needed on top to clear the WAM caches.
Just heads up
WAM is shared broker storage across Windows Azure tools. Removing accounts would also sign out:
Visual Studio
Azure CLI
Azure PowerShell
VS Code Azure extensions
Any app using Azure.Identity with broker enabled
This can impact UX for other logged in flows.
| #pragma warning disable CS0618 // MsalCacheHelper.Clear is obsolete but is the correct approach for full cache wipe on disconnect | ||
| cacheHelper.Clear(); |
There was a problem hiding this comment.
Why is Clear() the intentional approach here? This would wipe out the entire MSAL Cache, removing all accounts, not only the ones in the current context.
There was a problem hiding this comment.
My thinking here is that it would be roughly equivalent to the approach of iterating over the results from .GetAccounts() to remove each or them, how does this differ given that the cache files are managed per machine user?
There was a problem hiding this comment.
I see. My comment was more about removing only the account being disconnected rather than all accounts, mainly from a UX perspective. That said, if there aren't any issues with clearing all accounts, then this approach works for me.
There was a problem hiding this comment.
The CLI does not offer an account parameter, so any user account selection is done during the browser flow, which requires Disconnect-MgGraph to trigger the account selection again, so this feel like the right option. I suppose that there could be the case where a user has closed a terminal and is running Connect-MgGraph again triggering account selection.
But either way I'm comfortable with fully clearing out any cached tokens,
There was a problem hiding this comment.
Msal cache scope is per window user, so .clear() will only scope to disconnecting user, I believe.
JonathanCrd
left a comment
There was a problem hiding this comment.
Overall it looks good but I left a couple of comments about certain scenarios that might be missing and some questions too.
| await TokenCacheUtilities.ClearPersistedTokenCacheAsync(Constants.CacheName).ConfigureAwait(false); | ||
| } | ||
| catch (Exception) | ||
| { |
There was a problem hiding this comment.
Any actual failure here would be silent, could you add a log here so it's diagnosable?
|
Looks fine to me, left my comments on the ongoing discussions. |
Summary
Fixes #3648
When
Disconnect-MgGraphis called withContextScope == CurrentUser, the persisted MSAL token cache on disk was not being cleared. This PR adds cache clearing usingMsalCacheHelper.Clear()from the MSAL Extensions library.Changes
Microsoft.Identity.Client.Extensions.Msalv4.78.0 package reference toAuthentication.Core.csprojTokenCacheUtilities.cs— buildsStorageCreationPropertiesmatching Azure.Identity's internal cache layout (Windows DPAPI, macOS Keychain, Linux KeyRing) and clears both.caeand.nocaecache variantsAuthenticationHelpers.LogoutAsync()— callsTokenCacheUtilities.ClearPersistedTokenCacheAsync()whenContextScope == CurrentUser, with non-fatal error handling (disconnect still succeeds if cache clear fails)Disconnect-MgGraphdocumentationDesign Decisions
.caeand.nocaecache files; both are clearedStorageCreationPropertiesare configured to match the exact storage layout Azure.Identity uses (directory, keychain service, keyring schema/attributes)Testing