NullReferenceException in DefaultUserSession when loading project with IdentityServer4

12/7/2018

I am running a dotnet core 2.2 app with IdentityServer4 installed using Nuget. When I build a docker container and run, all works fine. When I deploy this container to my Google Kubernetes Engine cluster it fails on startup with the following:

{
 insertId:  "18ykz2ofg4ipm0"  
 labels: {
  compute.googleapis.com/resource_name:  "fluentd-gcp-v3.1.0-nndnb"   
  container.googleapis.com/namespace_name:  "my_namespace"   
  container.googleapis.com/pod_name:  "identity-deployment-5b8bd8745b-qn2v8"   
  container.googleapis.com/stream:  "stdout"   
 }
 logName:  "projects/my_project/logs/app"  
 receiveTimestamp:  "2018-12-07T21:09:25.708527406Z"  
 resource: {
  labels: {
   cluster_name:  "my_cluster"    
   container_name:  "app"    
   instance_id:  "4238697312444637243"    
   namespace_id:  "my_namespace"    
   pod_id:  "identity-deployment-5b8bd8745b-qn2v8"    
   project_id:  "my_project"    
   zone:  "europe-west2-b"    
  }
  type:  "container"   
 }
 severity:  "INFO"  
 textPayload:  "System.NullReferenceException: Object reference not set 
to an instance of an object.
   at 
IdentityServer4.Services.DefaultUserSession.RemoveSessionIdCookieAsync()
   at 
IdentityServer4.Services.DefaultUserSession.EnsureSessionIdCookieAsync()
   at 
IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events) 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
"  
 timestamp:  "2018-12-07T21:09:17Z"  
}

As I mentioned, this works perfectly locally, and when running inside a docker container, only when within Kubernetes do I see these errors.

I'm not sure what I've missed here with kubernetes, but any help very much appreciated.

-- James Woodley
.net-core
c#
docker
identityserver4
kubernetes

1 Answer

5/24/2019

This vexed me in the last few days. I suspect it has something to do with the recent deprecation of the former mechanism in app builder configuration:

   app.UseAuthentication().UseCookieAuthentication();  <-- no longer valid and apparently will not even compile now.

This has been replaced the following in the ConfigureServices section:

  services.AddAuthentication("YourCookieName")
    .AddCookie("YourCookieName", options =>
    {
      options.ExpireTimeSpan = TimeSpan.FromDays(30.0);
    });

While I am not sure what the exact breaking change is for identityserver4, after cloning identityserver4 components and debugging I was able to isolate the the constructor for DefaultUserSession takes an IHttpContextAccessor that was arriving as null:

The constructor in question:

    public DefaultUserSession(
        IHttpContextAccessor httpContextAccessor,
        IAuthenticationSchemeProvider schemes,
        IAuthenticationHandlerProvider handlers,
        IdentityServerOptions options,
        ISystemClock clock,
        ILogger<IUserSession> logger)
    { ...

The following solution gets you past the error, though hopefully identityserver4 will make this moot in a near future release.

You need to add an IHttpContextAccessor service in ConfigureServices:

public override void ConfigureServices(IServiceCollection services)
{
  ... other code omitted ...
  services.AddScoped<IHttpContextAccessor>(provider => new 
       LocalHttpContextAccessor(provider));
  ... other code omitted ...
}

The LocalHttpContextAccessor is just a private class in the configuration class as seen here:

private class LocalHttpContextAccessor : IHttpContextAccessor
{
  public IServiceProvider serviceProvider { get; private set; }
  public HttpContext httpContext { get; set; }

  public LocalHttpContextAccessor(IServiceProvider serviceProvider)
  {
    this.serviceProvider = serviceProvider;
    this.httpContext = null;
  }

  public HttpContext HttpContext
  {
    get
    {
      return this.httpContext;
    }
    set
    {
      this.httpContext = null;
    }
  }
}

The problem is that at the point of configuring services, there is no current context to set, so I set it in a using block in the app builder configuration stage:

public override void Configure(IApplicationBuilder app,
  IHostingEnvironment env)
{
  app.Use(async (context, next) =>
  {
    IHttpContextAccessor httpContextAccessor;

    httpContextAccessor = context.RequestServices.GetRequiredService<IHttpContextAccessor>();
    if (httpContextAccessor is LocalHttpContextAccessor)
    {
      ((LocalHttpContextAccessor)httpContextAccessor).httpContext = context;
    }
    await next();
  });
  ... other code omitted ...
  app.UseIdentityServer();

That will set the http context prior to running identity server code which fixes the bug. The scoped service should be created individually for each request. I've only recently made the full plunge into .net core from .net framework, so if there are scope or DI issues in that code that might lead to leaks or bad life-cycle, I'd appreciate the input. That said, at the very least that code keeps identity server 4 from crashing with the core 2.2+.

-- Victor Thomas Wilcox Jr.
Source: StackOverflow