diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj
index 54f37452ac4..a1a4944e102 100644
--- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj
+++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj
@@ -4,7 +4,7 @@
9.0
netstandard2.0;net6.0;net472
Microsoft.Graph.PowerShell.Authentication.Core
- 2.35.1
+ 2.38.0
true
@@ -20,6 +20,8 @@
+
+
diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs
index e4448663d72..7d581c0205e 100644
--- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs
+++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs
@@ -430,6 +430,18 @@ public static async Task LogoutAsync()
{
var authContext = GraphSession.Instance.AuthContext;
GraphSession.Instance.InMemoryTokenCache?.ClearCache();
+ if (authContext?.ContextScope == ContextScope.CurrentUser)
+ {
+ try
+ {
+ await TokenCacheUtilities.ClearPersistedTokenCacheAsync(Constants.CacheName).ConfigureAwait(false);
+ }
+ catch (Exception)
+ {
+ // Non-fatal: persisted cache clearing may fail on some platforms.
+ // The auth record and in-memory state are still cleared below.
+ }
+ }
GraphSession.Instance.AuthContext = null;
GraphSession.Instance.GraphHttpClient = null;
await DeleteAuthRecordAsync().ConfigureAwait(false);
diff --git a/src/Authentication/Authentication.Core/Utilities/TokenCacheUtilities.cs b/src/Authentication/Authentication.Core/Utilities/TokenCacheUtilities.cs
new file mode 100644
index 00000000000..8f6fcc3f023
--- /dev/null
+++ b/src/Authentication/Authentication.Core/Utilities/TokenCacheUtilities.cs
@@ -0,0 +1,67 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
+// ------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.Extensions.Msal;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Microsoft.Graph.PowerShell.Authentication.Core.Utilities
+{
+ ///
+ /// Utilities for managing the MSAL token cache persisted to disk by Azure.Identity.
+ ///
+ internal static class TokenCacheUtilities
+ {
+ // Azure.Identity internal constants for cache storage configuration.
+ // See: Azure/azure-sdk-for-net - sdk/core/Azure.Core/src/Identity/Constants.cs
+ private const string DefaultCacheKeychainService = "Microsoft.Developer.IdentityService";
+ private const string DefaultCacheKeyringSchema = "msal.cache";
+ private const string DefaultCacheKeyringCollection = "default";
+ private static readonly KeyValuePair DefaultCacheKeyringAttribute1 =
+ new KeyValuePair("MsalClientID", "Microsoft.Developer.IdentityService");
+ private static readonly KeyValuePair DefaultCacheKeyringAttribute2 =
+ new KeyValuePair("Microsoft.Developer.IdentityService", "1.0.0.0");
+
+ // Azure.Identity appends CAE suffixes to the cache name internally.
+ private const string CaeEnabledSuffix = ".cae";
+ private const string CaeDisabledSuffix = ".nocae";
+
+ private static readonly string DefaultCacheDirectory =
+ Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ ".IdentityService");
+
+ ///
+ /// Clears the persisted MSAL token cache files created by Azure.Identity
+ /// for the given cache name. Clears both CAE-enabled and CAE-disabled variants.
+ ///
+ /// The cache name (e.g., "mg.msal.cache").
+ public static async Task ClearPersistedTokenCacheAsync(string cacheName)
+ {
+ // Azure.Identity creates separate caches for CAE-enabled and CAE-disabled tokens.
+ await ClearCacheAsync(cacheName + CaeEnabledSuffix).ConfigureAwait(false);
+ await ClearCacheAsync(cacheName + CaeDisabledSuffix).ConfigureAwait(false);
+ }
+
+ private static async Task ClearCacheAsync(string cacheFileName)
+ {
+ var storageProperties = new StorageCreationPropertiesBuilder(cacheFileName, DefaultCacheDirectory)
+ .WithMacKeyChain(DefaultCacheKeychainService, cacheFileName)
+ .WithLinuxKeyring(
+ DefaultCacheKeyringSchema,
+ DefaultCacheKeyringCollection,
+ cacheFileName,
+ DefaultCacheKeyringAttribute1,
+ DefaultCacheKeyringAttribute2)
+ .Build();
+
+ var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties).ConfigureAwait(false);
+#pragma warning disable CS0618 // MsalCacheHelper.Clear is obsolete but is the correct approach for full cache wipe on disconnect
+ cacheHelper.Clear();
+#pragma warning restore CS0618
+ }
+ }
+}
diff --git a/src/Authentication/Authentication.Test/TokenCache/TokenCacheUtilitiesTests.cs b/src/Authentication/Authentication.Test/TokenCache/TokenCacheUtilitiesTests.cs
new file mode 100644
index 00000000000..a1cb20bc303
--- /dev/null
+++ b/src/Authentication/Authentication.Test/TokenCache/TokenCacheUtilitiesTests.cs
@@ -0,0 +1,80 @@
+using Microsoft.Graph.PowerShell.Authentication;
+using Microsoft.Graph.PowerShell.Authentication.Core.TokenCache;
+using Microsoft.Graph.PowerShell.Authentication.Core.Utilities;
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.Graph.Authentication.Test.TokenCache
+{
+ public class TokenCacheUtilitiesTests : IDisposable
+ {
+ public TokenCacheUtilitiesTests()
+ {
+ GraphSession.Initialize(() => new GraphSession());
+ GraphSession.Instance.InMemoryTokenCache = new InMemoryTokenCache();
+ }
+
+ public void Dispose()
+ {
+ GraphSession.Reset();
+ }
+
+ [Fact]
+ public async Task LogoutAsyncShouldClearInMemoryCacheForProcessScope()
+ {
+ // Arrange
+ GraphSession.Instance.InMemoryTokenCache = new InMemoryTokenCache(
+ Encoding.UTF8.GetBytes("mockTokenData"));
+ GraphSession.Instance.AuthContext = new AuthContext
+ {
+ AuthType = AuthenticationType.UserProvidedAccessToken,
+ ContextScope = ContextScope.Process
+ };
+
+ // Act
+ var result = await AuthenticationHelpers.LogoutAsync();
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(AuthenticationType.UserProvidedAccessToken, result.AuthType);
+ Assert.Null(GraphSession.Instance.AuthContext);
+ Assert.Null(GraphSession.Instance.GraphHttpClient);
+ Assert.Empty(GraphSession.Instance.InMemoryTokenCache.ReadTokenData());
+ }
+
+ [Fact]
+ public async Task LogoutAsyncShouldNotThrowWhenAuthContextIsNull()
+ {
+ // Arrange
+ GraphSession.Instance.AuthContext = null;
+
+ // Act - should not throw even though there's no auth context
+ var result = await AuthenticationHelpers.LogoutAsync();
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public async Task LogoutAsyncShouldAttemptCacheClearForCurrentUserScope()
+ {
+ // Arrange
+ GraphSession.Instance.AuthContext = new AuthContext
+ {
+ AuthType = AuthenticationType.UserProvidedAccessToken,
+ ContextScope = ContextScope.CurrentUser
+ };
+
+ // Act - should not throw even if no persisted cache exists on disk
+ var result = await AuthenticationHelpers.LogoutAsync();
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(ContextScope.CurrentUser, result.ContextScope);
+ Assert.Null(GraphSession.Instance.AuthContext);
+ }
+ }
+}
diff --git a/src/Authentication/descriptions/Disconnect-MgGraph.md b/src/Authentication/descriptions/Disconnect-MgGraph.md
index bf3a8afface..5964655e8fe 100644
--- a/src/Authentication/descriptions/Disconnect-MgGraph.md
+++ b/src/Authentication/descriptions/Disconnect-MgGraph.md
@@ -1 +1 @@
-Use Disconnect-MgGraph to sign out.
\ No newline at end of file
+Use Disconnect-MgGraph to sign out. This clears the persisted MSAL token cache from disk when using CurrentUser context scope, as well as removing the in-memory token cache and authentication record.
\ No newline at end of file
diff --git a/src/Authentication/docs/Disconnect-MgGraph.md b/src/Authentication/docs/Disconnect-MgGraph.md
index 0414a17ed25..81acd8cb713 100644
--- a/src/Authentication/docs/Disconnect-MgGraph.md
+++ b/src/Authentication/docs/Disconnect-MgGraph.md
@@ -13,11 +13,11 @@ Once you're signed in, you'll remain signed in until you invoke Disconnect-MgGra
## SYNTAX
```
-Disconnect-MgGraph []
+Disconnect-MgGraph [-ProgressAction ] []
```
## DESCRIPTION
-Use Disconnect-MgGraph to sign out.
+Use Disconnect-MgGraph to sign out. This clears the persisted MSAL token cache from disk when using CurrentUser context scope, as well as removing the in-memory token cache and authentication record.
## EXAMPLES
@@ -30,6 +30,21 @@ Use Disconnect-MgGraph to sign out.
## PARAMETERS
+### -ProgressAction
+{{ Fill ProgressAction Description }}
+
+```yaml
+Type: ActionPreference
+Parameter Sets: (All)
+Aliases: proga
+
+Required: False
+Position: Named
+Default value: None
+Accept pipeline input: False
+Accept wildcard characters: False
+```
+
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).