diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj
index 622dff1e1..76ff6f7b9 100644
--- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj
+++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj
@@ -88,7 +88,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml
index f8b51763a..10bf18386 100644
--- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml
+++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml
@@ -1455,12 +1455,10 @@
MSAL memory token cache options.
-
+
Initializes a new instance of the class.
-
-
@@ -1506,11 +1504,11 @@
Token cache for which to initialize the serialization.
A that represents a completed initialization operation.
-
+
Clear the cache.
- A that represents a completed clear operation.
+ HomeAccountId for a user account in the cache.
@@ -1557,12 +1555,10 @@
MSAL memory token cache options.
-
+
Constructor.
- Configuration options.
- Accessor to the HttpContext.
serialization cache.
Memory cache options.
@@ -1593,23 +1589,6 @@
-
-
- Azure AD options.
-
-
-
-
- HTTP accessor.
-
-
-
-
- Constructor of the abstract token cache provider.
-
- Configuration options.
- Accessor for the HttpContext.
-
Initializes the token cache serialization.
@@ -1617,12 +1596,6 @@
Token cache to serialize/deserialize.
A that represents a completed initialization operation.
-
-
- Cache key.
-
- The cache key.
-
Raised AFTER MSAL added the new token in its in-memory copy of the cache.
@@ -1639,11 +1612,11 @@
Token cache notification arguments.
A that represents a completed operation.
-
+
Clear the cache.
- A that represents a completed clear operation.
+ HomeAccountId for a user account in the cache.
@@ -1689,12 +1662,11 @@
https://aka.ms/msal-net-token-cache-serialization
-
+
MSAL Token cache provider constructor.
- Configuration options.
- Accessor for an HttpContext.
+ Session for the current user.
Logger.
diff --git a/src/Microsoft.Identity.Web/TokenAcquisition.cs b/src/Microsoft.Identity.Web/TokenAcquisition.cs
index d2063ea3e..19a0329d6 100644
--- a/src/Microsoft.Identity.Web/TokenAcquisition.cs
+++ b/src/Microsoft.Identity.Web/TokenAcquisition.cs
@@ -283,29 +283,34 @@ public async Task GetAccessTokenForAppAsync(IEnumerable scopes)
/// A that represents a completed account removal operation.
public async Task RemoveAccountAsync(RedirectContext context)
{
- IConfidentialClientApplication app = await GetOrBuildConfidentialClientApplicationAsync().ConfigureAwait(false);
-
- // For B2C, we should remove all accounts of the user regardless the user flow
- if (_microsoftIdentityOptions.IsB2C)
+ ClaimsPrincipal user = context.HttpContext.User;
+ string? userId = user.GetMsalAccountId();
+ if (!string.IsNullOrEmpty(userId))
{
- var b2cAccounts = await app.GetAccountsAsync().ConfigureAwait(false);
+ IConfidentialClientApplication app = await GetOrBuildConfidentialClientApplicationAsync().ConfigureAwait(false);
- foreach (var b2cAccount in b2cAccounts)
+ // For B2C, we should remove all accounts of the user regardless the user flow
+ if (_microsoftIdentityOptions.IsB2C)
{
- await app.RemoveAsync(b2cAccount).ConfigureAwait(false);
- }
+ var b2cAccounts = await app.GetAccountsAsync().ConfigureAwait(false);
- _tokenCacheProvider?.ClearAsync().ConfigureAwait(false);
- }
- else
- {
- string? identifier = context.HttpContext.User.GetMsalAccountId();
- IAccount account = await app.GetAccountAsync(identifier).ConfigureAwait(false);
+ foreach (var b2cAccount in b2cAccounts)
+ {
+ await app.RemoveAsync(b2cAccount).ConfigureAwait(false);
+ }
- if (account != null)
+ await _tokenCacheProvider.ClearAsync(userId).ConfigureAwait(false);
+ }
+ else
{
- await app.RemoveAsync(account).ConfigureAwait(false);
- _tokenCacheProvider?.ClearAsync().ConfigureAwait(false);
+ string? identifier = context.HttpContext.User.GetMsalAccountId();
+ IAccount account = await app.GetAccountAsync(identifier).ConfigureAwait(false);
+
+ if (account != null)
+ {
+ await app.RemoveAsync(account).ConfigureAwait(false);
+ await _tokenCacheProvider.ClearAsync(userId).ConfigureAwait(false);
+ }
}
}
}
diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs
index 520847c24..866c46cad 100644
--- a/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs
+++ b/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs
@@ -3,7 +3,6 @@
using System;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
@@ -28,16 +27,11 @@ public class MsalDistributedTokenCacheAdapter : MsalAbstractTokenCacheProvider
///
/// Initializes a new instance of the class.
///
- ///
- ///
///
///
public MsalDistributedTokenCacheAdapter(
- IOptions microsoftIdentityOptions,
- IHttpContextAccessor httpContextAccessor,
IDistributedCache memoryCache,
IOptions cacheOptions)
- : base(microsoftIdentityOptions, httpContextAccessor)
{
if (cacheOptions == null)
{
diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/IMsalTokenCacheProvider.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/IMsalTokenCacheProvider.cs
index d78208b46..bf85a894a 100644
--- a/src/Microsoft.Identity.Web/TokenCacheProviders/IMsalTokenCacheProvider.cs
+++ b/src/Microsoft.Identity.Web/TokenCacheProviders/IMsalTokenCacheProvider.cs
@@ -19,9 +19,10 @@ public interface IMsalTokenCacheProvider
Task InitializeAsync(ITokenCache tokenCache);
///
- /// Clear the cache.
+ /// Clear the user token cache.
///
+ /// HomeAccountId for a user account in the cache.
/// A that represents a completed clear operation.
- Task ClearAsync();
+ Task ClearAsync(string homeAccountId);
}
}
diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/InMemory/MsalMemoryTokenCacheProvider.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/InMemory/MsalMemoryTokenCacheProvider.cs
index 061a19654..1f4f7c7aa 100644
--- a/src/Microsoft.Identity.Web/TokenCacheProviders/InMemory/MsalMemoryTokenCacheProvider.cs
+++ b/src/Microsoft.Identity.Web/TokenCacheProviders/InMemory/MsalMemoryTokenCacheProvider.cs
@@ -28,16 +28,11 @@ public class MsalMemoryTokenCacheProvider : MsalAbstractTokenCacheProvider
///
/// Constructor.
///
- /// Configuration options.
- /// Accessor to the HttpContext.
/// serialization cache.
/// Memory cache options.
public MsalMemoryTokenCacheProvider(
- IOptions microsoftIdentityOptions,
- IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
IOptions cacheOptions)
- : base(microsoftIdentityOptions, httpContextAccessor)
{
if (cacheOptions == null)
{
diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs
index 24c094aef..69bb28e6b 100644
--- a/src/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs
+++ b/src/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs
@@ -2,10 +2,7 @@
// Licensed under the MIT License.
using System;
-using System.IdentityModel.Tokens.Jwt;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Options;
using Microsoft.Identity.Client;
namespace Microsoft.Identity.Web.TokenCacheProviders
@@ -16,27 +13,6 @@ namespace Microsoft.Identity.Web.TokenCacheProviders
///
public abstract class MsalAbstractTokenCacheProvider : IMsalTokenCacheProvider
{
- ///
- /// Azure AD options.
- ///
- protected readonly IOptions _microsoftIdentityOptions;
-
- ///
- /// HTTP accessor.
- ///
- protected readonly IHttpContextAccessor _httpContextAccessor;
-
- ///
- /// Constructor of the abstract token cache provider.
- ///
- /// Configuration options.
- /// Accessor for the HttpContext.
- protected MsalAbstractTokenCacheProvider(IOptions microsoftIdentityOptions, IHttpContextAccessor httpContextAccessor)
- {
- _microsoftIdentityOptions = microsoftIdentityOptions;
- _httpContextAccessor = httpContextAccessor;
- }
-
///
/// Initializes the token cache serialization.
///
@@ -56,27 +32,6 @@ public Task InitializeAsync(ITokenCache tokenCache)
return Task.CompletedTask;
}
- ///
- /// Cache key.
- ///
- /// The cache key.
- private string? GetCacheKey(bool isAppTokenCache)
- {
- if (isAppTokenCache)
- {
- return $"{_microsoftIdentityOptions.Value.ClientId}_AppTokenCache";
- }
- else
- {
- // In the case of Web Apps, the cache key is the user account Id, and the expectation is that AcquireTokenSilent
- // should return a token otherwise this might require a challenge.
- // In the case Web APIs, the token cache key is a hash of the access token used to call the Web API
- JwtSecurityToken? jwtSecurityToken = _httpContextAccessor.HttpContext.GetTokenUsedToCallWebAPI();
- return (jwtSecurityToken != null) ? jwtSecurityToken.RawSignature
- : _httpContextAccessor.HttpContext.User.GetMsalAccountId();
- }
- }
-
///
/// Raised AFTER MSAL added the new token in its in-memory copy of the cache.
/// This notification is called every time MSAL accesses the cache, not just when a write takes place:
@@ -86,24 +41,26 @@ public Task InitializeAsync(ITokenCache tokenCache)
/// Contains parameters used by the MSAL call accessing the cache.
private async Task OnAfterAccessAsync(TokenCacheNotificationArgs args)
{
- // if the access operation resulted in a cache update
+ // The access operation resulted in a cache update.
if (args.HasStateChanged)
{
- string? cacheKey = GetCacheKey(args.IsApplicationCache);
- if (!string.IsNullOrWhiteSpace(cacheKey))
+ if (args.HasTokens)
{
- await WriteCacheBytesAsync(cacheKey, args.TokenCache.SerializeMsalV3()).ConfigureAwait(false);
+ await WriteCacheBytesAsync(args.SuggestedCacheKey, args.TokenCache.SerializeMsalV3()).ConfigureAwait(false);
+ }
+ else
+ {
+ // No token in the cache. we can remove the cache entry
+ await RemoveKeyAsync(args.SuggestedCacheKey).ConfigureAwait(false);
}
}
}
private async Task OnBeforeAccessAsync(TokenCacheNotificationArgs args)
{
- string? cacheKey = GetCacheKey(args.IsApplicationCache);
-
- if (!string.IsNullOrEmpty(cacheKey))
+ if (!string.IsNullOrEmpty(args.SuggestedCacheKey))
{
- byte[] tokenCacheBytes = await ReadCacheBytesAsync(cacheKey).ConfigureAwait(false);
+ byte[] tokenCacheBytes = await ReadCacheBytesAsync(args.SuggestedCacheKey).ConfigureAwait(false);
args.TokenCache.DeserializeMsalV3(tokenCacheBytes, shouldClearExistingCache: true);
}
}
@@ -121,16 +78,11 @@ protected virtual Task OnBeforeWriteAsync(TokenCacheNotificationArgs args)
///
/// Clear the cache.
///
- /// A that represents a completed clear operation.
- public async Task ClearAsync()
+ /// HomeAccountId for a user account in the cache.
+ public async Task ClearAsync(string homeAccountId)
{
- string? cacheKey = GetCacheKey(false);
-
- if (!string.IsNullOrEmpty(cacheKey))
- {
- // This is a user token cache
- await RemoveKeyAsync(cacheKey).ConfigureAwait(false);
- }
+ // This is a user token cache
+ await RemoveKeyAsync(homeAccountId).ConfigureAwait(false);
// TODO: Clear the cookie session if any. Get inspiration from
// https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/240
diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/Session/MsalSessionTokenCacheProvider.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/Session/MsalSessionTokenCacheProvider.cs
index 0120ec746..c0fee3b0f 100644
--- a/src/Microsoft.Identity.Web/TokenCacheProviders/Session/MsalSessionTokenCacheProvider.cs
+++ b/src/Microsoft.Identity.Web/TokenCacheProviders/Session/MsalSessionTokenCacheProvider.cs
@@ -5,7 +5,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
namespace Microsoft.Identity.Web.TokenCacheProviders.Session
{
@@ -31,21 +30,19 @@ namespace Microsoft.Identity.Web.TokenCacheProviders.Session
/// https://aka.ms/msal-net-token-cache-serialization
public class MsalSessionTokenCacheProvider : MsalAbstractTokenCacheProvider, IMsalTokenCacheProvider
{
- private HttpContext CurrentHttpContext => _httpContextAccessor.HttpContext;
- private readonly ILogger _logger;
+ private ILogger _logger;
+ private ISession _session;
///
/// MSAL Token cache provider constructor.
///
- /// Configuration options.
- /// Accessor for an HttpContext.
+ /// Session for the current user.
/// Logger.
public MsalSessionTokenCacheProvider(
- IOptions microsoftIdentityOptions,
- IHttpContextAccessor httpContextAccessor,
+ ISession session,
ILogger logger)
- : base(microsoftIdentityOptions, httpContextAccessor)
{
+ _session = session;
_logger = logger;
}
@@ -57,18 +54,18 @@ public MsalSessionTokenCacheProvider(
/// Read blob.
protected override async Task ReadCacheBytesAsync(string cacheKey)
{
- await CurrentHttpContext.Session.LoadAsync().ConfigureAwait(false);
+ await _session.LoadAsync().ConfigureAwait(false);
_sessionLock.EnterReadLock();
try
{
- if (CurrentHttpContext.Session.TryGetValue(cacheKey, out byte[] blob))
+ if (_session.TryGetValue(cacheKey, out byte[] blob))
{
- _logger.LogInformation($"Deserializing session {CurrentHttpContext.Session.Id}, cacheId {cacheKey}");
+ _logger.LogInformation($"Deserializing session {_session.Id}, cacheId {cacheKey}");
}
else
{
- _logger.LogInformation($"CacheId {cacheKey} not found in session {CurrentHttpContext.Session.Id}");
+ _logger.LogInformation($"CacheId {cacheKey} not found in session {_session.Id}");
}
return blob;
@@ -89,10 +86,10 @@ protected override async Task WriteCacheBytesAsync(string cacheKey, byte[] bytes
_sessionLock.EnterWriteLock();
try
{
- _logger.LogInformation($"Serializing session {CurrentHttpContext.Session.Id}, cacheId {cacheKey}");
+ _logger.LogInformation($"Serializing session {_session.Id}, cacheId {cacheKey}");
// Reflect changes in the persistent store
- CurrentHttpContext.Session.Set(cacheKey, bytes);
+ _session.Set(cacheKey, bytes);
await Task.CompletedTask.ConfigureAwait(false);
}
finally
@@ -110,10 +107,10 @@ protected override async Task RemoveKeyAsync(string cacheKey)
_sessionLock.EnterWriteLock();
try
{
- _logger.LogInformation($"Clearing session {CurrentHttpContext.Session.Id}, cacheId {cacheKey}");
+ _logger.LogInformation($"Clearing session {_session.Id}, cacheId {cacheKey}");
// Reflect changes in the persistent store
- CurrentHttpContext.Session.Remove(cacheKey);
+ _session.Remove(cacheKey);
await Task.CompletedTask.ConfigureAwait(false);
}
finally
diff --git a/tests/B2CWebAppCallsWebApi/Client/appsettings.json b/tests/B2CWebAppCallsWebApi/Client/appsettings.json
index 093940003..12e5052a8 100644
--- a/tests/B2CWebAppCallsWebApi/Client/appsettings.json
+++ b/tests/B2CWebAppCallsWebApi/Client/appsettings.json
@@ -1,15 +1,15 @@
{
- "AzureAdB2C": {
- "Instance": "https://fabrikamb2c.b2clogin.com",
- "ClientId": "fdb91ff5-5ce6-41f3-bdbd-8267c817015d",
- "Domain": "fabrikamb2c.onmicrosoft.com",
- "SignedOutCallbackPath": "/signout/B2C_1_susi",
- "SignUpSignInPolicyId": "b2c_1_susi",
- "ResetPasswordPolicyId": "b2c_1_reset",
- "EditProfilePolicyId": "b2c_1_edit_profile", // Optional profile editing policy
- "ClientSecret": "[Copy the client secret added to the app from the Azure portal]"
- //"CallbackPath": "/signin/B2C_1_sign_up_in" // defaults to /signin-oidc
- },
+ "AzureAdB2C": {
+ "Instance": "https://fabrikamb2c.b2clogin.com",
+ "ClientId": "fdb91ff5-5ce6-41f3-bdbd-8267c817015d",
+ "Domain": "fabrikamb2c.onmicrosoft.com",
+ "SignedOutCallbackPath": "/signout/B2C_1_susi",
+ "SignUpSignInPolicyId": "b2c_1_susi",
+ "ResetPasswordPolicyId": "b2c_1_reset",
+ "EditProfilePolicyId": "b2c_1_edit_profile", // Optional profile editing policy
+ "ClientSecret": "[Copy the client secret added to the app from the Azure portal]"
+ //"CallbackPath": "/signin/B2C_1_sign_up_in" // defaults to /signin-oidc
+ },
"TodoList": {
/*
TodoListScope is the scope of the Web API you want to call. This can be: "api://8f085429-c424-45c4-beb3-75f6f0a7924f/user_impersonation",
diff --git a/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs b/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs
index babe901db..aed2e2e47 100644
--- a/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs
+++ b/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs
@@ -13,11 +13,8 @@ namespace Microsoft.Identity.Web.Test.Common.TestHelpers
public class MsalTestTokenCacheProvider : MsalAbstractTokenCacheProvider
{
public MsalTestTokenCacheProvider(
- IOptions microsoftIdentityOptions,
- IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
IOptions cacheOptions)
- : base(microsoftIdentityOptions, httpContextAccessor)
{
MemoryCache = memoryCache;
_cacheOptions = cacheOptions?.Value;
diff --git a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs
index 83ecb22b7..024a18c88 100644
--- a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs
+++ b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs
@@ -98,8 +98,6 @@ private void InitializeTokenAcquisitionObjects()
IHttpContextAccessor httpContextAccessor = CreateMockHttpContextAccessor();
_msalTestTokenCacheProvider = new MsalTestTokenCacheProvider(
- microsoftIdentityOptions,
- httpContextAccessor,
_provider.GetService(),
tokenOptions);
diff --git a/tests/Microsoft.Identity.Web.Test.LabInfrastructure/Microsoft.Identity.Web.Test.LabInfrastructure.csproj b/tests/Microsoft.Identity.Web.Test.LabInfrastructure/Microsoft.Identity.Web.Test.LabInfrastructure.csproj
index aa1d077b8..9bc8af74d 100644
--- a/tests/Microsoft.Identity.Web.Test.LabInfrastructure/Microsoft.Identity.Web.Test.LabInfrastructure.csproj
+++ b/tests/Microsoft.Identity.Web.Test.LabInfrastructure/Microsoft.Identity.Web.Test.LabInfrastructure.csproj
@@ -17,7 +17,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive