Silver Light and Blue Sky

C# and Infrastructure. Code is everything.

Configure IDistributedCache and IDataProtection for session in ASP.NET Core

Background

This blog describes two things.

  • How to store session data out of the ASP.NET Core process
  • How to separate the life cycle of session data from that of ASP.NET Core process and host

In the background, ASP.NET Core app can run as a container (docker) architecture. When ASP.NET Core app runs as a docker, it's easy to scale up and down. In this situation, we would like to store session data out of Docker containers and separate lifecycle. However, you may think if we can store session data out of the ASP.NET Core process, we can separate the lifecycle of session data from that of ASP.NET Core process and host at the same time. However, this is not necessarily so at present ASP.NET Core. I explain it based on the Microsoft's document.

Managing Application State | Microsoft Docs

Configure IDistributedCache to store session data out of memory

At first, we should have to configure a service implementing IDistributedCache. The above document introduces Microsoft.Extensions.Caching.Memory for development purpose. However, we would like to other services for storing session data out of ASP.NET Core process. ASP.NET Core team provides two packages for this purpose. One is using Redis and the other is using SQLServer.

www.nuget.org

www.nuget.org

I'm using Redis at present. After adding Microsoft.Extensions.Caching.Redis in project.json, you may just configure Startup class like below. I use the code samples described in the above Microsoft document to confirm session is working.

gist.github.com

When you run this code with Redis-server, you will find the session is stored in Redis and session is working like below.

f:id:tanaka733:20161216230026p:plain

f:id:tanaka733:20161216230132p:plain

However, a session will be lost in this sample after ASP.NET Core process runs on another machine or container even if the Redis-server keeps running. Why is this?

Configure IDataProtection to separate the life cycle

The key to solving this issue is IDataProtection. ASP.NET Core stores session with encrypted by default. And the encrypted key is generated and stored at each machine without any configuration. Microsoft.AspNetCore.Session packages depends on Microsoft.AspNetCore.DataProtection. Here is the code Microsoft.AspNetCore.DataProtection generates and stored the encryption key in the local filesystem.

DataProtection/FileSystemXmlRepository.cs at rel/1.1.0 · aspnet/DataProtection · GitHub

So if you run the above code sample, you will find the encryption key under ~/.aspnet folder. Because the encryption key is different from the machine, a new machine can't decrypt the session data and the session will be lost even if the HTTP request has the same cookie value which client send.

f:id:tanaka733:20161216235838j:plain

To prevent this issue, we can use other IDataProtection implementations which store encryption key out of the machine. ASP.NET Core team provides two packages at present. One is Redis, the other is AzureStorage.

www.nuget.org

www.nuget.org

As I use Redis to store session, I also want to store encryption key into Redis.

f:id:tanaka733:20161216235853j:plain

It's easy to use Microsoft.AspNetCore.DataProtection.Redis. Like IDistributedCache, simply add Microsoft.AspNetCore.DataProtection.Redis in project.json and configure Startup class like below.

gist.github.com

After running this code sample, you will find the key in Redis.

f:id:tanaka733:20161216233104p:plain

Also, of course, session won't be lost even if the ASP.NET Core process is scaled up with other machines and also scaled down. You can find full code example here.

github.com