Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
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. e.g. 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?
–
–
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.
–
–
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()
.
–
–
–
I followed the guide for Custom Authorization Policy Providers using IAuthorizationPolicyProvider in ASP.NET Core and also wanted to create a custom response.
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-5.0
The guide I followed for that was Customize the behavior of AuthorizationMiddleware
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/customizingauthorizationmiddlewareresponse?view=aspnetcore-5.0
My code finally looked like this:
public class GuidKeyAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
private readonly AuthorizationMiddlewareResultHandler
DefaultHandler = new AuthorizationMiddlewareResultHandler();
public async Task HandleAsync(
RequestDelegate requestDelegate,
HttpContext httpContext,
AuthorizationPolicy authorizationPolicy,
PolicyAuthorizationResult policyAuthorizationResult)
if (policyAuthorizationResult.Challenged && !policyAuthorizationResult.Succeeded && authorizationPolicy.Requirements.Any(requirement => requirement is GuidKeyRequirement))
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
// Fallback to the default implementation.
await DefaultHandler.HandleAsync(requestDelegate, httpContext, authorizationPolicy,
policyAuthorizationResult);
Startup.cs:
services.AddSingleton<IAuthorizationMiddlewareResultHandler,
GuidKeyAuthorizationMiddlewareResultHandler>();
You can also edit your AuthorizationHandler
and access httpContext
via IHttpContextAccessor
. However this feels more like a hack.
internal class GuidKeyAuthorizationHandler : AuthorizationHandler<GuidKeyRequirement>
private readonly ILogger<GuidKeyAuthorizationHandler> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
public GuidKeyAuthorizationHandler(ILogger<GuidKeyAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
_logger = logger;
_httpContextAccessor = httpContextAccessor;
// Check whether a given GuidKeyRequirement is satisfied or not for a particular context
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GuidKeyRequirement requirement)
var httpContext = _httpContextAccessor.HttpContext; // Access context here
var key = System.Web.HttpUtility.ParseQueryString(httpContext.Request.QueryString.Value).Get("key");
if (!string.IsNullOrWhiteSpace(key))
// If the user guid key matches mark the authorization requirement succeeded
if (Guid.TryParse(key, out var guidKey) && guidKey == requirement.Key)
_logger.LogInformation("Guid key is correct");
if (requirement.RequireRefererHeader)
_logger.LogInformation("Require correct referer header");
httpContext.Request.Headers.TryGetValue("Referer", out var refererHeader);
if (requirement.RefererHeader == refererHeader)
_logger.LogInformation("Referer header is correct");
context.Succeed(requirement);
return Task.CompletedTask;
_logger.LogInformation($"Referer header {refererHeader} is not correct");
_logger.LogInformation("Correct referer header is not needed");
context.Succeed(requirement);
return Task.CompletedTask;
_logger.LogInformation($"Guid key {guidKey} is not correct");
_logger.LogInformation("No guid key present");
var msg = "Invalid Guid";
var bytes = Encoding.UTF8.GetBytes(msg);
httpContext.Response.StatusCode = 403;
httpContext.Response.ContentType = "application/json";
httpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length);
return Task.CompletedTask;
Found that solution here:
https://stackoverflow.com/a/61861098/3850405
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.