Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

changes to the cachekey to use tokenNotificationArgs #279

Merged
merged 10 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microsoft.Identity.Web/Microsoft.Identity.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client" Version="4.15.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.16.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
44 changes: 8 additions & 36 deletions src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 22 additions & 17 deletions src/Microsoft.Identity.Web/TokenAcquisition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,29 +283,34 @@ public async Task<string> GetAccessTokenForAppAsync(IEnumerable<string> scopes)
/// <returns>A <see cref="Task"/> that represents a completed account removal operation.</returns>
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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;

Expand All @@ -28,16 +27,11 @@ public class MsalDistributedTokenCacheAdapter : MsalAbstractTokenCacheProvider
/// <summary>
/// Initializes a new instance of the <see cref="MsalDistributedTokenCacheAdapter"/> class.
/// </summary>
/// <param name="microsoftIdentityOptions"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="memoryCache"></param>
/// <param name="cacheOptions"></param>
public MsalDistributedTokenCacheAdapter(
IOptions<MicrosoftIdentityOptions> microsoftIdentityOptions,
IHttpContextAccessor httpContextAccessor,
IDistributedCache memoryCache,
IOptions<MsalDistributedTokenCacheAdapterOptions> cacheOptions)
: base(microsoftIdentityOptions, httpContextAccessor)
{
if (cacheOptions == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ public interface IMsalTokenCacheProvider
Task InitializeAsync(ITokenCache tokenCache);

/// <summary>
/// Clear the cache.
/// Clear the user token cache.
/// </summary>
/// <param name="homeAccountId">HomeAccountId for a user account in the cache.</param>
/// <returns>A <see cref="Task"/> that represents a completed clear operation.</returns>
jmprieur marked this conversation as resolved.
Show resolved Hide resolved
Task ClearAsync();
Task ClearAsync(string homeAccountId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,11 @@ public class MsalMemoryTokenCacheProvider : MsalAbstractTokenCacheProvider
/// <summary>
/// Constructor.
/// </summary>
/// <param name="microsoftIdentityOptions">Configuration options.</param>
/// <param name="httpContextAccessor">Accessor to the HttpContext.</param>
/// <param name="memoryCache">serialization cache.</param>
/// <param name="cacheOptions">Memory cache options.</param>
public MsalMemoryTokenCacheProvider(
IOptions<MicrosoftIdentityOptions> microsoftIdentityOptions,
IHttpContextAccessor httpContextAccessor,
IMemoryCache memoryCache,
IOptions<MsalMemoryTokenCacheOptions> cacheOptions)
: base(microsoftIdentityOptions, httpContextAccessor)
{
if (cacheOptions == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,27 +13,6 @@ namespace Microsoft.Identity.Web.TokenCacheProviders
/// <seealso cref="Microsoft.Identity.Web.TokenCacheProviders.IMsalTokenCacheProvider" />
public abstract class MsalAbstractTokenCacheProvider : IMsalTokenCacheProvider
{
/// <summary>
/// Azure AD options.
/// </summary>
protected readonly IOptions<MicrosoftIdentityOptions> _microsoftIdentityOptions;

/// <summary>
/// HTTP accessor.
/// </summary>
protected readonly IHttpContextAccessor _httpContextAccessor;

/// <summary>
/// Constructor of the abstract token cache provider.
/// </summary>
/// <param name="microsoftIdentityOptions">Configuration options.</param>
/// <param name="httpContextAccessor">Accessor for the HttpContext.</param>
protected MsalAbstractTokenCacheProvider(IOptions<MicrosoftIdentityOptions> microsoftIdentityOptions, IHttpContextAccessor httpContextAccessor)
{
_microsoftIdentityOptions = microsoftIdentityOptions;
_httpContextAccessor = httpContextAccessor;
}

/// <summary>
/// Initializes the token cache serialization.
/// </summary>
Expand All @@ -56,27 +32,6 @@ public Task InitializeAsync(ITokenCache tokenCache)
return Task.CompletedTask;
}

/// <summary>
/// Cache key.
/// </summary>
/// <returns>The cache key.</returns>
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();
}
}

/// <summary>
/// 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:
Expand All @@ -86,24 +41,26 @@ public Task InitializeAsync(ITokenCache tokenCache)
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
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);
}
}
Expand All @@ -121,16 +78,11 @@ protected virtual Task OnBeforeWriteAsync(TokenCacheNotificationArgs args)
/// <summary>
/// Clear the cache.
/// </summary>
/// <returns>A <see cref="Task"/> that represents a completed clear operation.</returns>
jmprieur marked this conversation as resolved.
Show resolved Hide resolved
public async Task ClearAsync()
/// <param name="homeAccountId">HomeAccountId for a user account in the cache.</param>
public async Task ClearAsync(string homeAccountId)
pmaytak marked this conversation as resolved.
Show resolved Hide resolved
{
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
Expand Down
Loading