Search code examples
asp.netasp.net-coreauthorizationasp.net-core-mvc-2.0

.NET Core 2.0 Authorization filter causes an internal error


An authorization filter will cause an internal error on a .NET Core 2.0 web application. Steps to reproduce the problem are very simple: create a new .NET Core 2.0 MVC project with "no authentication", put an authorization filter on a controller method, access the corresponding URL and a "500 Internal Server Error" will be returned.

[Authorize]
public IActionResult Contact()
{
    ViewData["Message"] = "Your contact page.";

    return View();
}

A similar code in .NET Core 1.1 will correctly return the response "401 Unauthorized".

I had a posted a similar question on the problem: Custom cookie authentication not working after migration from ASP.NET Core 1.1 MVC to 2.0 while I still hadn't understood that the problem is much wider than it was in my specific case.

The exception:

System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
   at Microsoft.AspNetCore.Authentication.AuthenticationService.<ChallengeAsync>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.ChallengeResult.<ExecuteResultAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeResultAsync>d__19.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeFilterPipelineAsync>d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeAsync>d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__7.MoveNext()

Solution

  • HTTP 401 Unauthorized response should contain WWW-Authenticate header that defines authentication method for accessing the resource.

    In your project created without authentication, there is no any default authentication scheme set. That's why authentication middleware can not build valid 401 response and chooses to send 500 error instead.

    The fix is pretty simple. Add any authentication handler and set default authentication scheme, e.g.:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
    }
    

    With such configuration unauthorized request will result to 401 response with correctly set WWW-Authenticate header:

    enter image description here