Ori Refael
Ori Refael

Reputation: 3018

C# .net core 2.1 Exception handling of Authorization

So in Web API 2 in .net framework 4.7.1 if you have a filter which handles exception, defined as followed:

public sealed class RequestExceptionFilter : ExceptionFilterAttribute..

And in WebApiConfig:

config.Filters.Add(new MyAuthorizationFilter());
config.Filters.Add(new RequestExceptionFilter());

if any exception occured in MyAuthorizationFilter it would've been caught in the RequestExceptionFilter.

In .net core 2.1 I have the following:

services.AddMvc(options =>
{
    options.Filters.Add(new MyExceptionFilter());

}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAuthentication("Basic").AddScheme<AuthenticationSchemeOptions, UserAuthenticator>("Basic", null)

// configure DI for application services
services.AddScoped<IUserAuthenticator, UserAuthenticatorHandler>();

And I have the following handler which is:

public sealed class UserAuthenticator: AuthenticationHandler<AuthenticationSchemeOptions>

Now, If I throw an exception in protected override async Task<AuthenticateResult> HandleAuthenticateAsync() which is a method of UserAuthenticator, the server returns Internal Server Error and skips the exception handling.

Can I make it propagate to the exception filter?

Upvotes: 2

Views: 6116

Answers (3)

Terje
Terje

Reputation: 387

According to https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1 it seems authorization filters run before exception filters.

Maybe if you moved the exception handling from a filter to be added as middleware

In the public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) method: app.UseMiddleware<MyErrorHandling>();

public class MyErrorHandling
{
    private readonly RequestDelegate _next;

    public MyErrorHandling(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next.Invoke(context);
        }
        catch (Exception e)
        {
            // Do stuff?
            await context.Response.WriteAsync("it broke. :(");
        }
    }
}

I think this method would be a bit more flexible than using filters.

Upvotes: 3

ste-fu
ste-fu

Reputation: 7434

In .net core a lot of the functions of filters (especially global filters) have been replaced by Middleware.

The execution order of filters in MVC was fixed by the framework - MSDN Link

In .net core Middleware is executed in the order that is is configured in the

Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) method in Startup.cs

This means that you authorization filter will not work if there is an exception in your middleware. The best way round this is to move your exception handling into Middleware, and to ensure that it is added first or nearly first in that method.

One other option could be to enable the developer exception page for testing.

I've answered the how to in more detail in this SO answer: How to catch an exception and respond with a status code in .NET Core

Upvotes: 0

Syed Abdul Aala
Syed Abdul Aala

Reputation: 167

Create an extension method like this

public static void UseGlobalExceptionHandler(this IApplicationBuilder appBuilder, ILogger logger)
        {
            appBuilder.UseExceptionHandler(app =>
            {
                app.Run(async context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";

                    var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
                    //AuthException is your custom exception class
                    if (ex != null && ex.GetType() == typeof(AuthException))
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsync("Unautherized");
                    }
                });
            });
        }

Use it in startup.cs file under Configure method

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   //Use it like this
   app.UseGlobalExceptionHandler();
}

Upvotes: 1

Related Questions