When scaling your ASP.NET Core Razor app to multiple instances (pods, containers, etc.), authentication can become problematic. This is particularly true for cookie-based authentication, where each instance of your app needs to be able to encrypt and decrypt cookies using the same encryption keys.
By default, ASP.NET Core stores its data protection keys locally within the app (e.g., in /root/.aspnet/DataProtection-Keys
). This approach works well in single-instance scenarios, but when you scale horizontally, each instance of your app has its own set of encryption keys. As a result, cookies encrypted by one instance cannot be decrypted by another, causing authentication failures.
- Users are redirected to the login page when they switch between different instances (pods).
- Invalid or expired authentication cookies when a request hits an unauthenticated pod.
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
To resolve this, we need to configure ASP.NET Core Data Protection to use a centralized storage for encryption keys. Redis is a good choice, especially in distributed systems like Kubernetes.
Here's how you can configure it:
Ensure the following packages are installed:
Microsoft.AspNetCore.DataProtection.StackExchangeRedis
StackExchange.Redis
In your Startup.cs
or Program.cs
file, configure Redis as the persistent storage for Data Protection keys.
public void ConfigureServices(IServiceCollection services)
{
// Redis configuration
ConfigurationOptions redisOptions = new()
{
AbortOnConnectFail = redisCacheConfigs.AbortOnConnectFail,
User = redisCacheConfigs.Username,
Password = redisCacheConfigs.Password,
DefaultDatabase = redisCacheConfigs.Database
};
// Configure Data Protection to use Redis as the persistent key store
services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, // Choose your preferred encryption algorithm
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 // Set validation algorithm
})
.PersistKeysToStackExchangeRedis(
ConnectionMultiplexer.Connect(redisOptions), "DataProtection-Keys") // Connect to Redis
.SetApplicationName(Assembly.GetExecutingAssembly().GetName().ToString()); // Optional: Set app name for identification
// Your Normal UI Authentication
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(opt =>
{
opt.LoginPath = "/admin/account/login";
opt.LogoutPath = "/admin/account/logout";
});
}