Working on a .net core app implementing a custom policy.
Let's say we have a very simple custom policy:
internal class RequireNamePolicy : AuthorizationHandler<RequireNameRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RequireNameRequirement requirement)
{
var nameClaim = context.User.Claims.FirstOrDefault(c => c.Type == Claims.Name);
if (nameClaim != null && nameClaim.Value == "Chimney Spork")
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
internal class RequireNameRequirement : IAuthorizationRequirement
{
}
Now let's say the claim doesn't exist, so we hit context.Fail(). The default response is a 403 with no message body.
My question is, where would we change the status code (to 401) and return a message that states the problem (ie claim not present)?
You can declare a parameter of type Response in your path operation function (as you can do for cookies and headers). And then you can set the status_code in that temporal response object. And then you can return any object you need, as you normally would (a dict , a database model, etc).
The 200 status code is by far the most common returned. It means, simply, that the request was received and understood and is being processed. A 201 status code indicates that a request was successful and as a result, a resource has been created (for example a new page).
DELETE API Response Codes. A successful response of DELETE requests SHOULD be an HTTP response code 200 (OK) if the response includes an entity describing the status. The status should be 202 (Accepted) if the action has been queued.
To set a different HTTP Status Code in the Response, do the following: Go to Manage Dependencies... and add the SetStatusCode action of the HTTPRequestHandler extension. Use the SetStatusCode action in your REST API Method or callback flow right before the end node. Set its "StatusCode" property to the desired status code.
This response code means the returned meta-information is not exactly the same as is available from the origin server, but is collected from a local or a third-party copy. This is mostly used for mirrors or backups of another resource. Except for that specific case, the "200 OK" response is preferred to this status. 204 No Content
Use the SetStatusCode action in your REST API Method or callback flow right before the end node. Set its "StatusCode" property to the desired status code. Was this article useful?
AFAIK, if you disable buffering, the web server starts sending the response immediately. Given an HTTP response starts with a status line containing the status code, you can't change the status code after you've started sending a response. in your controller ?
The documentation for Customize the behavior of AuthorizationMiddleware can be found below:
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>();
context.Resource as AuthorizationFilterContext is null in net core 3.1
Finally I rewrite the method to this:
public class SysUserAuthHandler : AuthorizationHandler<SysUserAuthRequirement> {
private readonly IFetchLoginUser fetchUser;
private readonly IHttpContextAccessor httpContextAccessor;
public SysUserAuthHandler( IFetchLoginUser fetchLoginUser, IHttpContextAccessor httpContextAccessor ) {
fetchUser = fetchLoginUser;
this.httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync( AuthorizationHandlerContext context, SysUserAuthRequirement requirement ) {
var httpContext = httpContextAccessor.HttpContext;
byte[] bytes;
string msg;
if (!string.IsNullOrWhiteSpace( context.User.Identity.Name )) {
var myUser = fetchUser.LoadUser( context.User.Identity.Name, SystemEnum.FooSytem);
if ((myUser.Auth & requirement.Auth) == requirement.Auth) {
context.Succeed( requirement );
return Task.CompletedTask;
}
msg = requirement.Auth switch {
1 => "You don't have Auth of Maker",
2 => "You don't have Auth of Checker",
4 => "You don't have Auth of Admin",
8 => "You don't have Auth of Operator",
_ => "You don't have Auth"
};
}
else {
msg = "User Invalid, Please check your login status or login again";
}
bytes = Encoding.UTF8.GetBytes( msg );
httpContext.Response.StatusCode = 405;
httpContext.Response.ContentType = "application/json";
httpContext.Response.Body.WriteAsync( bytes, 0, bytes.Length );
//context.Succeed( requirement );
return Task.CompletedTask;
}
}
public class SysUserAuthRequirement : IAuthorizationRequirement {
public long Auth { get; private set; }
public SysUserAuthRequirement( long auth ) {
Auth = auth;
}
}
Do not forget add this line in Startup
services.AddHttpContextAccessor();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With