Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save swagfin/49bd0ded288bce4d0e8b8f4ed051a975 to your computer and use it in GitHub Desktop.
Save swagfin/49bd0ded288bce4d0e8b8f4ed051a975 to your computer and use it in GitHub Desktop.
Horizontally Scaling an ASP.NET Core Razor App (Authentication with Redis)

Horizontally Scaling an ASP.NET Core Razor App (Authentication with Redis)

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.

Problem

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.

Symptoms:

  • 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.

Solution: Using Redis to Persist Data Protection Keys

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:

1. Install Required NuGet Packages

Ensure the following packages are installed:

  • Microsoft.AspNetCore.DataProtection.StackExchangeRedis
  • StackExchange.Redis

2. Configure Redis for Data Protection

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";
        });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment