Return HTTP 403 using Authorize attribute in ASP.Net Core
When using ASP.Net WebAPI, I used to have a custom Authorize
attribute I would use to return either an HTTP 403
or 401
depending on the situation. eg if the user is not authenticated, return a 401
; if the user is authenticated but doesn't have the appropriate permissions, return a 403
. See here for more discussion on that.
It seems now, in the new ASP.Net Core, they don't want you overriding the Authorize
attribute anymore instead favoring a policy-based approach. However, it seems Core MVC suffers from the same "just return 401
for all auth errors" approach its predecessors have.
How do I override the framework to get the behavior I want?
I ended up doing it with middleware:
public class AuthorizeCorrectlyMiddleware
{
readonly RequestDelegate next;
public AuthorizeCorrectlyMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
{
if (context.User.Identity.IsAuthenticated)
{
//the user is authenticated, yet we are returning a 401
//let's return a 403 instead
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}
which should be registered in Startup.Configure
before calling app.UseMvc()
.
After opening an issue here, it appears this actually should work...sort of.
In your Startup.Configure
, if you just call app.UseMvc()
and don't register any other middleware, you will get 401
for any auth-related errors (not authenticated, authenticated but no permission).
If, however, you register one of the authentication middlewares that support it, you will correctly get 401
for unauthenticated and 403
for no permissions. For me, I used the JwtBearerMiddleware
which allows authentication via a JSON Web Token. The key part is to set the AutomaticChallenge
option when creating the middleware:
in Startup.Configure
:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
app.UseMvc();
AutomaticAuthenticate
will set the ClaimsPrincipal
automatically so you can access User
in a controller. AutomaticChallenge
allows the auth middleware to modify the response when auth errors happen (in this case setting 401
or 403
appropriately).
If you have your own authentication scheme to implement, you would inherit from AuthenticationMiddleware
and AuthenticationHandler
similar to how the JWT implementation works.