Skip to content

Commit 48911cf

Browse files
authored
In-memory/production distributed caches (#35504)
1 parent ce2f4d5 commit 48911cf

File tree

4 files changed

+181
-10
lines changed

4 files changed

+181
-10
lines changed

aspnetcore/blazor/call-web-api.md

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,97 @@ In the app's `Program` file, call:
6161

6262
* <xref:Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilder.EnableTokenAcquisitionToCallDownstreamApi%2A>: Enables token acquisition to call web APIs.
6363
* `AddDownstreamApi`: Adds a named downstream web service related to a specific configuration section.
64-
* <xref:Microsoft.Identity.Web.TokenCacheProviders.InMemory.InMemoryTokenCacheProviderExtension.AddInMemoryTokenCaches%2A>: Adds both the app and per-user in-memory token caches.
64+
* <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.DistributedTokenCacheAdapterExtension.AddDistributedTokenCaches%2A>: Adds the .NET Core distributed token caches to the service collection.
65+
* <xref:Microsoft.Extensions.DependencyInjection.MemoryCacheServiceCollectionExtensions.AddDistributedMemoryCache%2A>: Adds a default implementation of <xref:Microsoft.Extensions.Caching.Distributed.IDistributedCache> that stores cache items in memory.
66+
* Configure the distributed token cache options (<xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions>):
67+
* In development for debugging purposes, you can disable the L1 cache by setting <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions.DisableL1Cache%2A> to `true`. ***Be sure to reset it back to `false` for production.***
68+
* Set the maximum size of your L1 cache with [`L1CacheOptions.SizeLimit`](xref:Microsoft.Extensions.Caching.Memory.MemoryCacheOptions.SizeLimit%2A) to prevent the cache from overrunning the server's memory. The default value is 500 MB.
69+
* In development for debugging purposes, you can disable token encryption at rest by setting <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions.Encrypt%2A> to `false`, which is the default value. ***Be sure to reset it back to `true` for production.***
70+
* Set token eviction from the cache with <xref:Microsoft.Extensions.Caching.Distributed.DistributedCacheEntryOptions.SlidingExpiration%2A>. The default value is 1 hour.
71+
* For more information, including guidance on the callback for L2 cache failures (<xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions.OnL2CacheFailure%2A>) and asynchronous L2 cache writes (<xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions.EnableAsyncL2Write%2A>), see <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions> and [Token cache serialization: Distributed token caches](/entra/msal/dotnet/how-to/token-cache-serialization#distributed-token-caches).
72+
73+
You can choose to encrypt the cache and should always do so in production.
6574

6675
```csharp
6776
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
6877
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
6978
.EnableTokenAcquisitionToCallDownstreamApi()
70-
.AddDownstreamApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
71-
.AddInMemoryTokenCaches();
79+
.AddDownstreamApi("DownstreamApi",
80+
builder.Configuration.GetSection("DownstreamApi"))
81+
.AddDistributedTokenCaches();
82+
83+
// Requires the 'Microsoft.Extensions.Caching.Memory' NuGet package
84+
builder.Services.AddDistributedMemoryCache();
85+
86+
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
87+
options =>
88+
{
89+
// The following lines that are commented out reflect
90+
// default values. We recommend overriding the default
91+
// value of Encrypt to encrypt tokens at rest.
92+
93+
//options.DisableL1Cache = false;
94+
//options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024;
95+
options.Encrypt = true;
96+
//options.SlidingExpiration = TimeSpan.FromHours(1);
97+
});
98+
```
99+
100+
In-memory distributed token caches are created when calling <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.DistributedTokenCacheAdapterExtension.AddDistributedTokenCaches%2A> to ensure that there's a base implementation available for distributed token caching.
101+
102+
Production web apps and web APIs should use a production distributed token cache (for example: [Redis](https://redis.io/), [Microsoft SQL Server](https://www.microsoft.com/sql-server), [Microsoft Azure Cosmos DB](https://azure.microsoft.com/products/cosmos-db)).
103+
104+
> [!NOTE]
105+
> For local development and testing on a single machine, you can use in-memory token caches instead of distributed token caches:
106+
>
107+
> ```csharp
108+
> builder.Services.AddInMemoryTokenCaches();
109+
> ```
110+
>
111+
> Later in the development and testing period, adopt a production distributed token cache provider.
112+
113+
<xref:Microsoft.Extensions.DependencyInjection.MemoryCacheServiceCollectionExtensions.AddDistributedMemoryCache%2A> adds a default implementation of <xref:Microsoft.Extensions.Caching.Distributed.IDistributedCache> that stores cache items in memory, which is used by Microsoft Identity Web for token caching.
114+
115+
> [!NOTE]
116+
> <xref:Microsoft.Extensions.DependencyInjection.MemoryCacheServiceCollectionExtensions.AddDistributedMemoryCache%2A> requires a package reference to the [`Microsoft.Extensions.Caching.Memory` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory).
117+
>
118+
> [!INCLUDE[](~/includes/package-reference.md)]
119+
120+
To configure a production distributed cache provider, see <xref:performance/caching/distributed>.
121+
122+
> [!WARNING]
123+
> Always replace the in-memory distributed token caches with a real token cache provider when deploying the app to a production environment. If you fail to adopt a production distributed token cache provider, the app may suffer significantly degraded performance.
124+
125+
For more information, see [Token cache serialization: Distributed caches](/entra/msal/dotnet/how-to/token-cache-serialization?tabs=msal#distributed-caches). However, the code examples shown don't apply to ASP.NET Core apps, which configure distributed caches via <xref:Microsoft.Extensions.DependencyInjection.MemoryCacheServiceCollectionExtensions.AddDistributedMemoryCache%2A>, not <xref:Microsoft.Identity.Web.TokenCacheExtensions.AddDistributedTokenCache%2A>.
126+
127+
Use a shared Data Protection key ring in production so that instances of the app across servers in a web farm can decrypt tokens when <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions.Encrypt%2A?displayProperty=nameWithType> is set to `true`.
128+
129+
> [!NOTE]
130+
> For early development and local testing on a single machine, you can set <xref:Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedTokenCacheAdapterOptions.Encrypt%2A> to `false` and configure a shared Data Protection key ring later:
131+
>
132+
> ```csharp
133+
> options.Encrypt = false;
134+
> ```
135+
>
136+
> Later in the development and testing period, enable token encryption and adopt a shared Data Protection key ring.
137+
138+
The following example shows how to use [Azure Blob Storage and Azure Key Vault](xref:security/data-protection/configuration/overview#protectkeyswithazurekeyvault) for the shared key ring. Add the following packages to the server project of the Blazor Web App:
139+
140+
* [`Azure.Extensions.AspNetCore.DataProtection.Blobs`](https://www.nuget.org/packages/Azure.Extensions.AspNetCore.DataProtection.Blobs)
141+
* [`Azure.Extensions.AspNetCore.DataProtection.Keys`](https://www.nuget.org/packages/Azure.Extensions.AspNetCore.DataProtection.Keys)
142+
143+
[!INCLUDE[](~/includes/package-reference.md)]
144+
145+
Configure Azure Blob Storage to maintain the encrypted keys and protect them with Azure Key Vault. In the following example, the `{BLOB URI WITH SAS TOKEN}` placeholder is the full URI where the key file should be stored with the SAS token as a query string parameter, and the `{KEY IDENTIFIER}` placeholder is the key vault key identifier used for key encryption:
146+
147+
```csharp
148+
builder.Services.AddDataProtection()
149+
.PersistKeysToAzureBlobStorage(new Uri("{BLOB URI WITH SAS TOKEN}"))
150+
.ProtectKeysWithAzureKeyVault(new Uri("{KEY IDENTIFIER}"), new DefaultAzureCredential());
72151
```
73152
153+
For more information on using a shared Data Protection key ring, see <xref:host-and-deploy/web-farm#data-protection> and <xref:security/data-protection/configuration/overview>.
154+
74155
Inject <xref:Microsoft.Identity.Abstractions.IDownstreamApi> and call <xref:Microsoft.Identity.Abstractions.IDownstreamApi.CallApiForUserAsync%2A> when calling on behalf of a user:
75156
76157
```csharp

aspnetcore/blazor/fundamentals/signalr.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,11 @@ To resolve the problem, use ***either*** of the following approaches:
177177
* <xref:signalr/configuration>
178178
* [Blazor samples GitHub repository (`dotnet/blazor-samples`)](https://github.com/dotnet/blazor-samples) ([how to download](xref:blazor/fundamentals/index#sample-apps))
179179

180-
## Use session affinity (sticky sessions) for server-side webfarm hosting
180+
## Use session affinity (sticky sessions) for server-side web farm hosting
181181

182182
When more than one backend server is in use, the app must implement session affinity, also called *sticky sessions*. Session affinity ensures that a client's circuit reconnects to the same server if the connection is dropped, which is important because client state is only held in the memory of the server that first established the client's circuit.
183183

184-
The following error is thrown by an app that hasn't enabled session affinity in a webfarm:
184+
The following error is thrown by an app that hasn't enabled session affinity in a web farm:
185185

186186
> :::no-loc text="Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed.":::
187187

0 commit comments

Comments
 (0)