Skip to content

Commit c8effda

Browse files
author
Kalyan Krishna
committed
Merge branch 'master' into kkrishna/updates2019
2 parents 42f59f3 + 5563e2f commit c8effda

13 files changed

+213
-404
lines changed

Microsoft.Identity.Web/Client/TokenAcquisition.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,12 @@ public async Task RemoveAccount(RedirectContext context)
259259
account = accounts.FirstOrDefault(a => a.Username == user.GetLoginHint());
260260
}
261261

262-
this.UserTokenCacheProvider?.Clear();
262+
if (account!=null)
263+
{
264+
this.UserTokenCacheProvider?.Clear(account.HomeAccountId.Identifier);
263265

264-
await app.RemoveAsync(account);
266+
await app.RemoveAsync(account);
267+
}
265268
}
266269

267270
/// <summary>
@@ -391,24 +394,24 @@ private void AddAccountToCacheFromJwt(IEnumerable<string> scopes, JwtSecurityTok
391394
/// </summary>
392395
/// <param name="httpContext">HttpContext</param>
393396
/// <param name="scopes">Scopes to consent to</param>
394-
/// <param name="msalSeviceException"><see cref="MsalUiRequiredException"/> triggering the challenge</param>
397+
/// <param name="msalServiceException"><see cref="MsalUiRequiredException"/> triggering the challenge</param>
395398

396-
public void ReplyForbiddenWithWwwAuthenticateHeader(HttpContext httpContext, IEnumerable<string> scopes, MsalUiRequiredException msalSeviceException)
399+
public void ReplyForbiddenWithWwwAuthenticateHeader(HttpContext httpContext, IEnumerable<string> scopes, MsalUiRequiredException msalServiceException)
397400
{
398401
// A user interaction is required, but we are in a Web API, and therefore, we need to report back to the client through an wwww-Authenticate header https://tools.ietf.org/html/rfc6750#section-3.1
399402
string proposedAction = "consent";
400-
if (msalSeviceException.ErrorCode == MsalError.InvalidGrantError)
403+
if (msalServiceException.ErrorCode == MsalError.InvalidGrantError)
401404
{
402-
if (AcceptedTokenVersionIsNotTheSameAsTokenVersion(msalSeviceException))
405+
if (AcceptedTokenVersionIsNotTheSameAsTokenVersion(msalServiceException))
403406
{
404-
throw msalSeviceException;
407+
throw msalServiceException;
405408
}
406409
}
407410

408411
IDictionary<string, string> parameters = new Dictionary<string, string>()
409412
{
410413
{ "clientId", azureAdOptions.ClientId },
411-
{ "claims", msalSeviceException.Claims },
414+
{ "claims", msalServiceException.Claims },
412415
{ "scopes", string.Join(",", scopes) },
413416
{ "proposedAction", proposedAction }
414417
};

Microsoft.Identity.Web/Client/TokenCacheProviders/IMSALUserTokenCacheProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ public interface IMSALUserTokenCacheProvider
4242
/// <summary>
4343
/// Clears the token cache for this user
4444
/// </summary>
45-
void Clear();
45+
void Clear(string accountId);
4646
}
4747
}

Microsoft.Identity.Web/Client/TokenCacheProviders/InMemory/MSALAppMemoryTokenCacheProvider.cs

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace Microsoft.Identity.Web.Client.TokenCacheProviders
3636
/// An implementation of token cache for Confidential clients backed by MemoryCache.
3737
/// MemoryCache is useful in Api scenarios where there is no HttpContext to cache data.
3838
/// </summary>
39-
/// <seealso cref="https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/token-cache-serialization"/>
39+
/// <seealso cref="https://aka.ms/msal-net-token-cache-serialization"/>
4040
public class MSALAppMemoryTokenCacheProvider : IMSALAppTokenCacheProvider
4141
{
4242
/// <summary>
@@ -49,17 +49,12 @@ public class MSALAppMemoryTokenCacheProvider : IMSALAppTokenCacheProvider
4949
/// </summary>
5050
internal IMemoryCache memoryCache;
5151

52-
/// <summary>
53-
/// The internal handle to the client's instance of the Cache
54-
/// </summary>
55-
private ITokenCache ApptokenCache;
56-
5752
private readonly MSALMemoryTokenCacheOptions CacheOptions;
5853

5954
/// <summary>
6055
/// The App's whose cache we are maintaining.
6156
/// </summary>
62-
private string AppId;
57+
private readonly string AppId;
6358

6459
public MSALAppMemoryTokenCacheProvider(IMemoryCache cache,
6560
MSALMemoryTokenCacheOptions option,
@@ -90,12 +85,9 @@ public void Initialize(ITokenCache tokenCache, HttpContext httpcontext)
9085
{
9186
this.AppCacheId = this.AppId + "_AppTokenCache";
9287

93-
this.ApptokenCache = tokenCache;
94-
this.ApptokenCache.SetBeforeAccess(this.AppTokenCacheBeforeAccessNotification);
95-
this.ApptokenCache.SetAfterAccess(this.AppTokenCacheAfterAccessNotification);
96-
this.ApptokenCache.SetBeforeWrite(this.AppTokenCacheBeforeWriteNotification);
97-
98-
this.LoadAppTokenCacheFromMemory();
88+
tokenCache.SetBeforeAccess(this.AppTokenCacheBeforeAccessNotification);
89+
tokenCache.SetAfterAccess(this.AppTokenCacheAfterAccessNotification);
90+
tokenCache.SetBeforeWrite(this.AppTokenCacheBeforeWriteNotification);
9991
}
10092

10193
/// <summary>
@@ -107,33 +99,12 @@ private void AppTokenCacheBeforeWriteNotification(TokenCacheNotificationArgs arg
10799
// Since we are using a MemoryCache ,whose methods are threads safe, we need not to do anything in this handler.
108100
}
109101

110-
/// <summary>
111-
/// Loads the application's token from memory cache.
112-
/// </summary>
113-
private void LoadAppTokenCacheFromMemory()
114-
{
115-
byte[] tokenCacheBytes = (byte[])this.memoryCache.Get(this.AppCacheId);
116-
this.ApptokenCache.DeserializeMsalV3(tokenCacheBytes);
117-
}
118-
119-
/// <summary>
120-
/// Persists the application's token to the cache.
121-
/// </summary>
122-
private void PersistAppTokenCache()
123-
{
124-
// Reflect changes in the persistence store
125-
this.memoryCache.Set(this.AppCacheId, this.ApptokenCache.SerializeMsalV3(), CacheOptions.AbsoluteExpiration);
126-
}
127-
128102
/// <summary>
129103
/// Clears the token cache for this app
130104
/// </summary>
131105
public void Clear()
132106
{
133107
this.memoryCache.Remove(this.AppCacheId);
134-
135-
// Nulls the currently deserialized instance
136-
this.LoadAppTokenCacheFromMemory();
137108
}
138109

139110
/// <summary>
@@ -142,7 +113,9 @@ public void Clear()
142113
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
143114
private void AppTokenCacheBeforeAccessNotification(TokenCacheNotificationArgs args)
144115
{
145-
this.LoadAppTokenCacheFromMemory();
116+
// Load the token cache from memory
117+
byte[] tokenCacheBytes = (byte[])this.memoryCache.Get(this.AppCacheId);
118+
args.TokenCache.DeserializeMsalV3(tokenCacheBytes, shouldClearExistingCache: true);
146119
}
147120

148121
/// <summary>
@@ -154,7 +127,8 @@ private void AppTokenCacheAfterAccessNotification(TokenCacheNotificationArgs arg
154127
// if the access operation resulted in a cache update
155128
if (args.HasStateChanged)
156129
{
157-
this.PersistAppTokenCache();
130+
// Reflect changes in the persistence store
131+
this.memoryCache.Set(this.AppCacheId, args.TokenCache.SerializeMsalV3(), CacheOptions.AbsoluteExpiration);
158132
}
159133
}
160134
}

Microsoft.Identity.Web/Client/TokenCacheProviders/InMemory/MSALAppMemoryTokenCacheProviderExtension.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323
*/
2424

2525
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
26+
using Microsoft.AspNetCore.Http;
2627
using Microsoft.Extensions.Caching.Memory;
2728
using Microsoft.Extensions.DependencyInjection;
2829
using Microsoft.Extensions.Options;
@@ -72,11 +73,12 @@ public static IServiceCollection AddInMemoryAppTokenCache(this IServiceCollectio
7273
public static IServiceCollection AddInMemoryPerUserTokenCache(this IServiceCollection services, MSALMemoryTokenCacheOptions cacheOptions)
7374
{
7475
services.AddMemoryCache();
75-
76+
services.AddHttpContextAccessor();
7677
services.AddSingleton<IMSALUserTokenCacheProvider>(factory =>
7778
{
7879
var memoryCache = factory.GetRequiredService<IMemoryCache>();
79-
return new MSALPerUserMemoryTokenCacheProvider(memoryCache, cacheOptions);
80+
IHttpContextAccessor httpContextAccessor = factory.GetRequiredService<IHttpContextAccessor>();
81+
return new MSALPerUserMemoryTokenCacheProvider(memoryCache, cacheOptions, httpContextAccessor);
8082
});
8183

8284
return services;

Microsoft.Identity.Web/Client/TokenCacheProviders/InMemory/MSALPerUserMemoryTokenCacheProvider.cs

Lines changed: 34 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,27 @@ namespace Microsoft.Identity.Web.Client.TokenCacheProviders
3434
/// An implementation of token cache for both Confidential and Public clients backed by MemoryCache.
3535
/// MemoryCache is useful in Api scenarios where there is no HttpContext.Session to cache data.
3636
/// </summary>
37-
/// <seealso cref="https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/token-cache-serialization"/>
37+
/// <seealso cref="https://aka.ms/msal-net-token-cache-serialization"/>
3838
public class MSALPerUserMemoryTokenCacheProvider : IMSALUserTokenCacheProvider
3939
{
4040
/// <summary>
4141
/// The backing MemoryCache instance
4242
/// </summary>
4343
internal IMemoryCache memoryCache;
4444

45-
/// <summary>
46-
/// The internal handle to the client's instance of the Cache
47-
/// </summary>
48-
private ITokenCache UserTokenCache;
45+
private readonly MSALMemoryTokenCacheOptions CacheOptions;
4946

5047
/// <summary>
51-
/// Once the user signes in, this will not be null and can be ontained via a call to Thread.CurrentPrincipal
48+
/// Enables the singleton object to access the right HttpContext
5249
/// </summary>
53-
internal ClaimsPrincipal SignedInUser;
54-
55-
private readonly MSALMemoryTokenCacheOptions CacheOptions;
50+
private IHttpContextAccessor httpContextAccessor;
5651

5752
/// <summary>Initializes a new instance of the <see cref="MSALPerUserMemoryTokenCache"/> class.</summary>
5853
/// <param name="cache">The memory cache instance</param>
59-
public MSALPerUserMemoryTokenCacheProvider(IMemoryCache cache, MSALMemoryTokenCacheOptions option)
54+
public MSALPerUserMemoryTokenCacheProvider(IMemoryCache cache, MSALMemoryTokenCacheOptions option, IHttpContextAccessor httpContextAccessor)
6055
{
6156
this.memoryCache = cache;
57+
this.httpContextAccessor = httpContextAccessor;
6258

6359
if (option != null)
6460
{
@@ -76,70 +72,17 @@ public MSALPerUserMemoryTokenCacheProvider(IMemoryCache cache, MSALMemoryTokenCa
7672
/// <param name="user">The signed-in user for whom the cache needs to be established. Not needed by all providers.</param>
7773
public void Initialize(ITokenCache tokenCache, HttpContext httpcontext, ClaimsPrincipal user)
7874
{
79-
this.SignedInUser = user;
80-
81-
this.UserTokenCache = tokenCache;
82-
this.UserTokenCache.SetBeforeAccess(this.UserTokenCacheBeforeAccessNotification);
83-
this.UserTokenCache.SetAfterAccess(this.UserTokenCacheAfterAccessNotification);
84-
this.UserTokenCache.SetBeforeWrite(this.UserTokenCacheBeforeWriteNotification);
85-
86-
if (this.SignedInUser == null)
87-
{
88-
// No users signed in yet, so we return
89-
return;
90-
}
91-
92-
this.LoadUserTokenCacheFromMemory();
93-
}
94-
95-
/// <summary>
96-
/// Explores the Claims of a signed-in user (if available) to populate the unique Id of this cache's instance.
97-
/// </summary>
98-
/// <returns>The signed in user's object.tenant Id , if available in the ClaimsPrincipal.Current instance</returns>
99-
internal string GetMsalAccountId()
100-
{
101-
if (this.SignedInUser != null)
102-
{
103-
return this.SignedInUser.GetMsalAccountId();
104-
}
105-
return null;
106-
}
107-
108-
/// <summary>Loads the user token cache from memory.</summary>
109-
private void LoadUserTokenCacheFromMemory()
110-
{
111-
string cacheKey = this.GetMsalAccountId();
112-
113-
if (string.IsNullOrWhiteSpace(cacheKey))
114-
return;
115-
116-
byte[] tokenCacheBytes = (byte[])this.memoryCache.Get(this.GetMsalAccountId());
117-
this.UserTokenCache.DeserializeMsalV3(tokenCacheBytes);
118-
}
119-
120-
/// <summary>
121-
/// Persists the user token blob to the memoryCache.
122-
/// </summary>
123-
private void PersistUserTokenCache()
124-
{
125-
string cacheKey = this.GetMsalAccountId();
126-
127-
if (string.IsNullOrWhiteSpace(cacheKey))
128-
return;
129-
130-
// Ideally, methods that load and persist should be thread safe.MemoryCache.Get() is thread safe.
131-
this.memoryCache.Set(this.GetMsalAccountId(), this.UserTokenCache.SerializeMsalV3(), this.CacheOptions.AbsoluteExpiration);
75+
tokenCache.SetBeforeAccess(this.UserTokenCacheBeforeAccessNotification);
76+
tokenCache.SetAfterAccess(this.UserTokenCacheAfterAccessNotification);
77+
tokenCache.SetBeforeWrite(this.UserTokenCacheBeforeWriteNotification);
13278
}
13379

13480
/// <summary>
13581
/// Clears the TokenCache's copy of this user's cache.
13682
/// </summary>
137-
public void Clear()
83+
public void Clear(string accountId)
13884
{
139-
this.memoryCache.Remove(this.GetMsalAccountId());
140-
141-
// Nulls the currently deserialized instance
142-
this.LoadUserTokenCacheFromMemory();
85+
this.memoryCache.Remove(accountId);
14386
}
14487

14588
/// <summary>
@@ -148,12 +91,21 @@ public void Clear()
14891
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
14992
private void UserTokenCacheAfterAccessNotification(TokenCacheNotificationArgs args)
15093
{
151-
this.SetSignedInUserFromNotificationArgs(args);
152-
15394
// if the access operation resulted in a cache update
15495
if (args.HasStateChanged)
15596
{
156-
this.PersistUserTokenCache();
97+
string cacheKey = args.Account?.HomeAccountId?.Identifier;
98+
if (string.IsNullOrEmpty(cacheKey))
99+
{
100+
cacheKey = httpContextAccessor.HttpContext.User.GetMsalAccountId();
101+
}
102+
103+
if (string.IsNullOrWhiteSpace(cacheKey))
104+
return;
105+
106+
// Ideally, methods that load and persist should be thread safe.MemoryCache.Get() is thread safe.
107+
this.memoryCache.Set(cacheKey, args.TokenCache.SerializeMsalV3(), this.CacheOptions.AbsoluteExpiration);
108+
157109
}
158110
}
159111

@@ -164,7 +116,17 @@ private void UserTokenCacheAfterAccessNotification(TokenCacheNotificationArgs ar
164116
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
165117
private void UserTokenCacheBeforeAccessNotification(TokenCacheNotificationArgs args)
166118
{
167-
this.LoadUserTokenCacheFromMemory();
119+
string cacheKey = args.Account?.HomeAccountId?.Identifier;
120+
if (string.IsNullOrEmpty(cacheKey))
121+
{
122+
cacheKey = httpContextAccessor.HttpContext.User.GetMsalAccountId();
123+
}
124+
125+
if (string.IsNullOrWhiteSpace(cacheKey))
126+
return;
127+
128+
byte[] tokenCacheBytes = (byte[])this.memoryCache.Get(cacheKey);
129+
args.TokenCache.DeserializeMsalV3(tokenCacheBytes, shouldClearExistingCache:true);
168130
}
169131

170132
/// <summary>
@@ -174,18 +136,5 @@ private void UserTokenCacheBeforeAccessNotification(TokenCacheNotificationArgs a
174136
private void UserTokenCacheBeforeWriteNotification(TokenCacheNotificationArgs args)
175137
{
176138
}
177-
178-
/// <summary>
179-
/// To keep the cache, ClaimsPrincipal and Sql in sync, we ensure that the user's object Id we obtained by MSAL after
180-
/// successful sign-in is set as the key for the cache.
181-
/// </summary>
182-
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
183-
private void SetSignedInUserFromNotificationArgs(TokenCacheNotificationArgs args)
184-
{
185-
if (this.SignedInUser == null && args.Account != null)
186-
{
187-
this.SignedInUser = args.Account.ToClaimsPrincipal();
188-
}
189-
}
190139
}
191140
}

0 commit comments

Comments
 (0)