Skip to content

Commit 5f23ddf

Browse files
author
Kalyan Krishna
committed
Merge branch 'master' into kkrishna/updates2019
2 parents a2fa549 + 958bafe commit 5f23ddf

12 files changed

+87
-30
lines changed

2-WebApp-graph-user/2-2-TokenCache/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ Go to the `"2-WebApp-graph-user\2-2-TokenCache"` folder
5151
#### In the appsettings.json file, configure a Sql server database for token caching, if you have not already done so:
5252

5353
1. In the `TokenCacheDbConnStr` key, provide the Sql server connection string to the database you wish to use for token caching.
54+
> Note:
55+
> If you want to test this sample locally with Visual Studio, you might want to use localdb, which is installed with Visual Studio.
56+
> In that case, use the following connection string:
57+
>
58+
> ```XML
59+
> "ConnectionStrings": {
60+
> "TokenCacheDbConnStr": "Data Source=(LocalDb)\\MSSQLLocalDB;Database=MY_TOKEN_CACHE_DATABASE;Trusted_Connection=True;"
61+
> },
62+
> ```
5463
1. If you do not have an existing database and tables needed for token caching, this sample can use [EF Core- code first](https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/new-db?tabs=visual-studio) to create a database and tables for you. to do that, follow the steps below.
5564
1. In the file `Microsoft.Identity.Web\Client\TokenCacheProviders\Sql\MSALAppSqlTokenCacheProviderExtension.cs`, uncomment the code under the **// Uncomment the following lines to create the database.**. This comment exists once in the **AddSqlAppTokenCache** and **AddSqlPerUserTokenCache** methods.
5665
1. Run the solution again, when a user signs-in the very first time, the Entity Framework will create the database and tables `AppTokenCache` and `UserTokenCache` for app and user token caching respectively.

2-WebApp-graph-user/2-2-TokenCache/appsettings.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99

1010
// To call an API
1111
"ClientSecret": "[Enter app key of the first app as obtained from Azure Portal, e.g. rZJJ9bHSi/cYnYwmQFxLYDn/6EfnrnIfKoNzv9NKgbo]"
12-
},
12+
},
13+
14+
/*
15+
To try the sample locally on your development machine, if you have installed Visual Studio you can
16+
use the following connection string
17+
"TokenCacheDbConnStr": "Data Source=(LocalDb)\\MSSQLLocalDB;Database=MY_TOKEN_CACHE_DATABASE;Trusted_Connection=True;"
18+
*/
1319
"ConnectionStrings": {
1420
"TokenCacheDbConnStr": "[Enter the Sql server connection string, e.g. Server=MY_SQL_SERVER;Database=MY_TOKEN_CACHE_DATABASE;Trusted_Connection=True;"
1521
},

Microsoft.Identity.Web/Client/TokenAcquisition.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,20 @@ public async Task<string> GetAccessTokenOnBehalfOfUser(HttpContext context, IEnu
165165

166166
// Use MSAL to get the right token to call the API
167167
var application = BuildConfidentialClientApplication(context, context.User);
168-
return await GetAccessTokenOnBehalfOfUser(application, context.User, scopes, tenant);
168+
169+
// Case of a lazy OBO
170+
Claim jwtClaim = context.User.FindFirst("jwt");
171+
if (jwtClaim != null)
172+
{
173+
(context.User.Identity as ClaimsIdentity).RemoveClaim(jwtClaim);
174+
var result = await application.AcquireTokenOnBehalfOf(scopes.Except(scopesRequestedByMsalNet), new UserAssertion(jwtClaim.Value))
175+
.ExecuteAsync();
176+
return result.AccessToken;
177+
}
178+
else
179+
{
180+
return await GetAccessTokenOnBehalfOfUser(application, context.User, scopes, tenant);
181+
}
169182
}
170183

171184
/// <summary>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ private void AppTokenCacheAfterAccessNotification(TokenCacheNotificationArgs arg
128128
if (args.HasStateChanged)
129129
{
130130
// Reflect changes in the persistence store
131-
this.memoryCache.Set(this.AppCacheId, args.TokenCache.SerializeMsalV3(), CacheOptions.AbsoluteExpiration);
131+
this.memoryCache.Set(this.AppCacheId, args.TokenCache.SerializeMsalV3(), CacheOptions.SlidingExpiration);
132132
}
133133
}
134134
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static class MSALAppMemoryTokenCacheProviderExtension
3939
/// <returns></returns>
4040
public static IServiceCollection AddInMemoryTokenCaches(this IServiceCollection services, MSALMemoryTokenCacheOptions cacheOptions = null)
4141
{
42-
var memoryCacheoptions = (cacheOptions == null) ? new MSALMemoryTokenCacheOptions { AbsoluteExpiration = DateTimeOffset.Now.AddDays(14) }
42+
var memoryCacheoptions = (cacheOptions == null) ? new MSALMemoryTokenCacheOptions { SlidingExpiration = TimeSpan.FromDays(14) }
4343
: cacheOptions;
4444

4545
AddInMemoryAppTokenCache(services, memoryCacheoptions);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ public class MSALMemoryTokenCacheOptions
3838
/// <value>
3939
/// The AbsoluteExpiration value.
4040
/// </value>
41-
public DateTimeOffset AbsoluteExpiration
41+
public TimeSpan SlidingExpiration
4242
{
4343
get;
4444
set;
4545
}
4646

4747
public MSALMemoryTokenCacheOptions()
4848
{
49-
this.AbsoluteExpiration = DateTimeOffset.Now.AddHours(12);
49+
this.SlidingExpiration = TimeSpan.FromHours(12);
5050
}
5151
}
5252
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private void UserTokenCacheAfterAccessNotification(TokenCacheNotificationArgs ar
104104
return;
105105

106106
// 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);
107+
this.memoryCache.Set(cacheKey, args.TokenCache.SerializeMsalV3(), this.CacheOptions.SlidingExpiration);
108108

109109
}
110110
}

Microsoft.Identity.Web/Client/TokenCacheProviders/Session/MSALAppSessionTokenCacheProvider.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2929
using System;
3030
using System.Diagnostics;
3131
using System.Threading;
32+
using System.Threading.Tasks;
3233

3334
namespace Microsoft.Identity.Web.Client.TokenCacheProviders
3435
{
@@ -46,7 +47,12 @@ public class MSALAppSessionTokenCacheProvider : IMSALAppTokenCacheProvider
4647
/// <summary>
4748
/// The HTTP context being used by this app
4849
/// </summary>
49-
internal HttpContext HttpContext = null;
50+
internal HttpContext HttpContext { get { return httpContextAccessor.HttpContext; } }
51+
52+
/// <summary>
53+
/// HTTP context accessor
54+
/// </summary>
55+
internal IHttpContextAccessor httpContextAccessor;
5056

5157
/// <summary>
5258
/// The duration till the tokens are kept in memory cache. In production, a higher value , upto 90 days is recommended.
@@ -63,26 +69,26 @@ public class MSALAppSessionTokenCacheProvider : IMSALAppTokenCacheProvider
6369
/// <summary>Initializes a new instance of the <see cref="MSALAppSessionTokenCacheProvider"/> class.</summary>
6470
/// <param name="azureAdOptionsAccessor">The azure ad options accessor.</param>
6571
/// <exception cref="ArgumentNullException">AzureADOptions - The app token cache needs {nameof(AzureADOptions)}</exception>
66-
public MSALAppSessionTokenCacheProvider(IOptionsMonitor<AzureADOptions> azureAdOptionsAccessor)
72+
public MSALAppSessionTokenCacheProvider(IOptionsMonitor<AzureADOptions> azureAdOptionsAccessor, IHttpContextAccessor httpContextAccessor)
6773
{
74+
this.httpContextAccessor = httpContextAccessor;
6875
if (azureAdOptionsAccessor.CurrentValue == null && string.IsNullOrWhiteSpace(azureAdOptionsAccessor.CurrentValue.ClientId))
6976
{
7077
throw new ArgumentNullException(nameof(AzureADOptions), $"The app token cache needs {nameof(AzureADOptions)}, populated with clientId to initialize.");
7178
}
7279

73-
this.AppId = azureAdOptionsAccessor.CurrentValue.ClientId;
80+
AppId = azureAdOptionsAccessor.CurrentValue.ClientId;
7481
}
7582

7683
/// <summary>Initializes this instance of TokenCacheProvider with essentials to initialize themselves.</summary>
7784
/// <param name="tokenCache">The token cache instance of MSAL application</param>
7885
/// <param name="httpcontext">The Httpcontext whose Session will be used for caching.This is required by some providers.</param>
7986
public void Initialize(ITokenCache tokenCache, HttpContext httpcontext)
8087
{
81-
this.AppCacheId = this.AppId + "_AppTokenCache";
82-
this.HttpContext = httpcontext;
88+
AppCacheId = this.AppId + "_AppTokenCache";
8389

84-
tokenCache.SetBeforeAccess(this.AppTokenCacheBeforeAccessNotification);
85-
tokenCache.SetAfterAccess(this.AppTokenCacheAfterAccessNotification);
90+
tokenCache.SetBeforeAccessAsync(this.AppTokenCacheBeforeAccessNotificationAsync);
91+
tokenCache.SetAfterAccessAsync(this.AppTokenCacheAfterAccessNotificationAsync);
8692
tokenCache.SetBeforeWrite(this.AppTokenCacheBeforeWriteNotification);
8793
}
8894

@@ -119,9 +125,9 @@ public void Clear()
119125
/// Triggered right before MSAL needs to access the cache. Reload the cache from the persistence store in case it changed since the last access.
120126
/// </summary>
121127
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
122-
private void AppTokenCacheBeforeAccessNotification(TokenCacheNotificationArgs args)
128+
private async Task AppTokenCacheBeforeAccessNotificationAsync(TokenCacheNotificationArgs args)
123129
{
124-
this.HttpContext.Session.LoadAsync().Wait();
130+
await this.HttpContext.Session.LoadAsync();
125131

126132
SessionLock.EnterReadLock();
127133
try
@@ -147,7 +153,7 @@ private void AppTokenCacheBeforeAccessNotification(TokenCacheNotificationArgs ar
147153
/// Triggered right after MSAL accessed the cache.
148154
/// </summary>
149155
/// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
150-
private void AppTokenCacheAfterAccessNotification(TokenCacheNotificationArgs args)
156+
private async Task AppTokenCacheAfterAccessNotificationAsync(TokenCacheNotificationArgs args)
151157
{
152158
// if the access operation resulted in a cache update
153159
if (args.HasStateChanged)
@@ -159,8 +165,8 @@ private void AppTokenCacheAfterAccessNotification(TokenCacheNotificationArgs arg
159165

160166
// Reflect changes in the persistent store
161167
byte[] blob = args.TokenCache.SerializeMsalV3();
162-
this.HttpContext.Session.Set(this.AppCacheId, blob);
163-
this.HttpContext.Session.CommitAsync().Wait();
168+
HttpContext.Session.Set(this.AppCacheId, blob);
169+
await HttpContext.Session.CommitAsync();
164170
}
165171
finally
166172
{

Microsoft.Identity.Web/Client/TokenCacheProviders/Session/MSALAppSessionTokenCacheProviderExtension.cs

Lines changed: 4 additions & 1 deletion
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.DependencyInjection;
2728
using Microsoft.Extensions.Options;
2829

@@ -46,9 +47,11 @@ public static IServiceCollection AddSessionTokenCaches(this IServiceCollection s
4647
/// <returns></returns>
4748
public static IServiceCollection AddSessionAppTokenCache(this IServiceCollection services)
4849
{
50+
services.AddHttpContextAccessor();
4951
services.AddScoped<IMSALAppTokenCacheProvider>(factory =>
5052
{
51-
return new MSALAppSessionTokenCacheProvider(factory.GetRequiredService<IOptionsMonitor<AzureADOptions>>());
53+
return new MSALAppSessionTokenCacheProvider(factory.GetRequiredService<IOptionsMonitor<AzureADOptions>>(),
54+
factory.GetRequiredService<IHttpContextAccessor>());
5255
});
5356

5457
return services;

Microsoft.Identity.Web/Client/TokenCacheProviders/Sql/MSALAppSqlTokenCacheProviderExtension.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,13 @@ public static IServiceCollection AddSqlAppTokenCache(this IServiceCollection ser
5252
{
5353
// Uncomment the following lines to create the database. In production scenarios, the database
5454
// will most probably be already present.
55-
//var tokenCacheDbContextBuilder = new DbContextOptionsBuilder<TokenCacheDbContext>();
56-
//tokenCacheDbContextBuilder.UseSqlServer(sqlTokenCacheOptions.SqlConnectionString);
57-
58-
//var tokenCacheDbContext = new TokenCacheDbContext(tokenCacheDbContextBuilder.Options);
59-
//tokenCacheDbContext.Database.EnsureCreated();
55+
/*
56+
var tokenCacheDbContextBuilder = new DbContextOptionsBuilder<TokenCacheDbContext>();
57+
tokenCacheDbContextBuilder.UseSqlServer(sqlTokenCacheOptions.SqlConnectionString);
6058
59+
var tokenCacheDbContextForCreation = new TokenCacheDbContext(tokenCacheDbContextBuilder.Options);
60+
tokenCacheDbContextForCreation.Database.EnsureCreated();
61+
*/
6162
services.AddDataProtection();
6263

6364
services.AddDbContext<TokenCacheDbContext>(options =>

0 commit comments

Comments
 (0)