Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Owin cookie authentication occasionally throws NullReferenceException

I'm using the OWIN cookie authentication middleware in a project that was set up as an ASP.NET MVC and WebApi application (i.e. I added OWIN).

Every now and then, when I made some changes and start the debugging, I get an exception which happens on every request for a good minute or so until the website suddenly works without any issues again. I host the application in my local IIS.

System.NullReferenceException: Object reference not set to an instance of an object.
   at FooWeb.Startup.<>c.<Configuration>b__0_3(CookieExceptionContext context) in C:\ws\Foo\Main\Main\FooWeb\Startup.cs:line 138
   at Microsoft.Owin.Security.Cookies.CookieAuthenticationHandler.<ApplyResponseGrantAsync>d__f.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationHandler.<ApplyResponseCoreAsync>d__b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationHandler.<ApplyResponseAsync>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationHandler.<TeardownAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationMiddleware`1.<Invoke>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContextStage.<RunApp>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.<DoFinalWork>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar)
   at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously);

I set up the middleware like so:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = "Cookies",
    LoginPath = new PathString("/Account/Login"),
    LogoutPath = new PathString("/Account/Logoff"),
    CookieName = "FooWebCookieAuth",
    SlidingExpiration = true,
    ExpireTimeSpan = TimeSpan.FromMinutes(10),
    CookieSecure = CookieSecureOption.Always,

    Provider = new CookieAuthenticationProvider()
    {
        OnValidateIdentity = async context =>
        {
            // Validate access token
            if (context == null)
            {
                return;
            }

            if (context.Identity == null || !context.Identity.IsAuthenticated)
            {
                return;
            }

            if (context.Identity.Claims == null)
            {
                context.RejectIdentity();
            }

            var accessTokenClaim = context.Identity.Claims.FirstOrDefault(x => x.Type == FooClaimTypes.Token);
            var accessToken = (accessTokenClaim == null) ? null : accessTokenClaim.Value;
            if (accessToken == null)
            {
                context.RejectIdentity();
            }
            else
            {
                var client = new IntrospectionClient(
                    SecurityTokenServiceEndpoints.Introspection,
                    "FooScope", 
                    "FooSecret");
                var validationResult = await client.SendAsync(new IntrospectionRequest()
                {
                    Token = accessToken
                });

                if (validationResult.IsError || !validationResult.IsActive)
                {
                    context.RejectIdentity();
                }
            }
        },
        OnException = context =>
        {
            // exception is thrown here (so that debugging stops). Without this it just faults
            throw context.Exception;
        },
    },
});

Update this seems to be related to the cookie or at least the browser - because I have one session in a browser where it will consistently throw that exception while other browsers (that were previously logged in also) can access it just fine.

like image 256
urbanhusky Avatar asked Oct 19 '22 19:10

urbanhusky


1 Answers

Rather than rejecting the identity replace it with an anonymous one.

context.ReplaceIdentity(System.Security.Principal.WindowsIdentity.GetAnonymous());
like image 91
David Brink Avatar answered Oct 27 '22 20:10

David Brink