1

I've introduced distributed cache using DB as suggested in MSDN. The aim is to allow our IDP server to work on multiple pods on Kubernetes. In the Startup.cs, I add the service like so.

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = Configuration.GetConnectionString("DbTarget");
    options.ExpiredItemsDeletionInterval = new TimeSpan(10, 0, 0);
    options.SchemaName = "dbo";
    options.TableName = "Pokemons";
});
services.AddAuthorization(options => ... );
services.AddDbContext<AppDbContext>(ContextOptions());
services.AddIdentity<AppUser, AppRole>()
    .AddEntityFrameworkStores<AppDbContext>();

I check the DB and can confirm that there's a table corresponding to our options. However, it's empty, so no distributed cache information is available at the moment. According to this, I'm supposed to request the default storage of IDS4, which I try to do by the following code. (I'm not using AddOpenIdConnect(...) but AddIdentityServerAuthentication(...). However, since IDS4 follows OIDC standard, that's included, I assume.)

services.AddIdentityServer(Ids4Options())
    .AddAspNetIdentity<AppUser>()
    .AddOperationalStore(OperationOptions())
    .AddConfigurationStore(ConfigOptions())
    .AddProfileService<ProfileService>()
    .AddDeveloperSigningCredential();

services.ConfigureApplicationCookie(options =>
{
    options.ExpireTimeSpan = TimeSpan.FromSeconds(55);
    options.SlidingExpiration = false;
});

services.AddHealthChecks();
services.AddCors(...);

services.AddOidcStateDataFormatterCache();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
    .AddIdentityServerAuthentication(options =>
     {
         options.Authority = Configuration["Env:BaseUrl"];
     });

I expected that given the distributed cache to be in effect and the data formatter set up, the table in my DB would get populated upon sign in. That doesn't happen, though. Testing to access the cache using DI in a constructor works and I see the entries in the DB when I execute something like this.

public MyClass (IDistributedCache cache) { Cache = cache; }
...
byte[] data = Encoding.ASCII.GetBytes("roo");
await Cache.SetAsync("kanga", data);

There was an issue on GitHub with suggestion of specifying the schemas explictly as shown below. Regrettably, that didn't change anything in my case.

service.AddOidcStateDataFormatterCache(
  IdentityServerAuthenticationDefaults.AuthenticationScheme)

When I execute the project locally, I get to see two cookies stored in the application tab of my browser as I call HttpContext.SignInAsync() after verification that the user credentials are valid. I was expecting that something corresponding to those would be stored in the DB as well. My colleagues spawned suggestions on switching to SingalR. I can't see, however, how changing the chache provider to Redis or such will resolve anything, since it's not the load that's the issue but non-existence of stored info. I also verified that the table dbo.PersistedGrants is live and kicking, filled with values. Tip of the iceberg with proofs of effort: irrelevant, tumbleweed, theoretical, already implemented. Same theme in other suggestions based on the search keys.

Apparently, additional step to enable the distributed signin is missing. Or, possibly, I'm doing something incorrectly. At the moment, I have no other clues (despite extensive googling) and I'm stuck due to lack of ideas how to trouble-shoot it or investigate further.

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438

1 Answers1

0

I managed to resolved it, with a lot of swearing and sweating. First hint came from this answer suggesting that the certificate I'm using isn't feasible in a multi-pod environment. Note that a previous version of .NET applied a different method (AddTestCredential() or something like that), which is obsolete by now in favor of the below.

services. ...
  //.AddDeveloperSigningCredential();
  .AddSigningCredential(cert);

There are blogs on how to create a certificate file. It will likely involve a lot of black magic like creating and converting files, compounding sub-certs, picking domains etc. It's complex and difficult. My suggestion is - follow a blog and hope for the best. The certificate can eventually be distributed by a simple code below or by using a service to provide the certificate key.

string path = base + "/certificate.pfx";
string pass = Configuration.GetValue<string>(...);
X509Certificate2 cert = new X509Certificate2(path, pass);

Once we've done that (and by we I mean a colleague with me riding shotgun), everything started to behave as expected. The issue was apparently not with the distributed cache but with accessing it due to different certificates being created on the fly with AddDeveloperSigningCredential().

This should be more apparent and well documented, in my opinion. Some resources to check out (with no claim of being complete) come here.

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438