1

I have an ASP.NET Core Web API and I'm using an exception middleware in order to catch my custom exceptions and return appropriate responses to the client.

However, in the case of "Unauthorized Access" thrown by the controller itself when using the [Authorize] attribute, how can I catch the error in my middleware?

Exception middleware

public class ExceptionMiddleware
{
    readonly RequestDelegate _request;

    public ExceptionMiddleware(RequestDelegate request)
    {
        _request = request;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _request(context);
        }
        catch (ValidationException ex)
        {
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            var error = ex.ToValidationExceptionDetails();
            await context.Response.WriteAsJsonAsync(error);
        }
        catch (Exception ex) when (ex.GetErrorCode() == (int)HttpStatusCode.Conflict)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Conflict;
            var error = ex.ToConflictExceptionDetails();
            await context.Response.WriteAsJsonAsync(error);
        }
        catch (Exception ex) when (ex.GetErrorCode() == (int)HttpStatusCode.NotFound)
        {
            context.Response.StatusCode = (int)HttpStatusCode.NotFound;
            var error = ex.ToNotFoundExceptionDetails();
            await context.Response.WriteAsJsonAsync(error);
        }
        catch(Exception ex)  when (ex.GetErrorCode() == (int)HttpStatusCode.Unauthorized)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            var error                   = ex.ToUnauthorizedRequestExceptionDetails();
            await context.Response.WriteAsJsonAsync(error);
        }
        catch (Exception)
        {
            throw;
        }
    }
}

Custom exception example:

public class PostNotFoundException : Exception
{
    public int ErrorCode { get; } = (int)System.Net.HttpStatusCode.NotFound;
    
    readonly static string _defaultErrorMessage = "Post Not Found.";

    public PostNotFoundException() : base(_defaultErrorMessage) { }

    public PostNotFoundException(string message) : base(message) { }

    public PostNotFoundException(string message, Exception innerException)
        : base(message, innerException) { }
}

Controller method code:

[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class PostsController : ControllerBase
{
    readonly IPostService _postService;

    public PostsController(IPostService postService)
    {
        _postService = postService;
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<PostQueryDTO>> GetPostById(Guid id)
    {
        return await _postService.GetPostByIdAsync(id);
    }
    
    [HttpGet("user")]
    public async Task<ActionResult<IEnumerable<PostsByUserDTO>>> GetPostsByUserID()
    {
        return Ok(await _postService.GetPostsByUserIDAsync(HttpContext.GetUserID()));
    }

    [HttpGet("user/followings")]
    public async Task<ActionResult<IEnumerable<PostQueryDTO>>>   GetPostsByUserFollowings()
    {
        return Ok(await _postService.GetPostsByUserFollowingsAsync(HttpContext.GetUserID()));
    }

    [HttpPost]
    public async Task<ActionResult<PostPOSTCommandDTO>> PostPost(PostPOSTCommandDTO post)
    {
        PostQueryDTO newpost = await _postService.CreatePostAsync(post, HttpContext.GetUserID());
        return CreatedAtAction("GetPostById", new { id = newpost.Id }, newpost);
    }

    [HttpPut("{postID}")]
    public async Task<IActionResult> PutPost(PostPOSTCommandDTO post, Guid postID)
    {
        await _postService.EditPostAsync(post, postID, HttpContext.GetUserID());
        return NoContent();
    }
    
    [HttpDelete("{postID}")]
    public async Task<IActionResult> DeletePost(Guid postID)
    {
        await _postService.DeletePostAsync(postID, HttpContext.GetUserID());
        return NoContent();
    }
}

So basically if an unauthorized user tries to add a post, the controller returns and 401 response, because of the Authorize attribute.

Is there a way to stop it from doing that and let me return custom response instead?

2
  • Exception middleware comes into picture when there is any exception, unauthorize access in not a exception but a proper response. Commented Jul 7, 2022 at 8:40
  • True, but it returns an empty response. Is there a way to change the response? Commented Jul 7, 2022 at 10:45

1 Answer 1

5

I think you may take the JwtBearerEvents into considratio

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "mytest.com",
            ValidAudience = "mytest.com",
            IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String("base64string"))
        };
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = async (context) =>
            {
                //do something here
            },
            OnChallenge = async (context) =>
            {
                //do something here
            }
        };
    });
Sign up to request clarification or add additional context in comments.

4 Comments

Can it help you? Did you get any update on it?
I tried it, but still could not catch the error
per my test, if I visited the controller which has [Authorize] tag without access token, the OnChallenge event will be triggered. Did you get any error message? Or something else?
It's working now. Probably missed something the first time I tried it. Thanks you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.