I have achieved it using ExceptionHandler. I wanted to keep 403 status code, but to customize error message. So I have created custom Exception:
public class ForbiddenException : BaseException
{
public ForbiddenException(string message) : base(HttpStatusCode.Forbidden, message)
{
}
public ForbiddenException(string message, object info) : base(HttpStatusCode.Forbidden, message, info)
{
}
}
public abstract class BaseException : Exception
{
protected BaseException(HttpStatusCode httpErrorCode, string message, Exception innerException = null) : base(message,
innerException)
{
HttpErrorCode = httpErrorCode;
}
protected BaseException(HttpStatusCode httpErrorCode, string message, object info, Exception innerException = null) :
this(httpErrorCode,
message, innerException)
{
Info = info;
}
public HttpStatusCode HttpErrorCode { get; set; }
public object Info { get; set; }
}
Then inside AuthorizationHandler I threw this exception:
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
List<Claim> userPermissionsClaims =
context.User.Claims.Where(c => c.Type == PermissionConstants.PermissionClaimType).ToList();
if (userPermissionsClaims.Any(pc => requirement.Permissions.Any(p => (Permissions)Convert.ToInt16(pc.Value) == p)))
{
context.Succeed(requirement);
}
else
{
throw new ForbiddenException(string.Format(ErrorMessages.PermissionsRequired, string.Join(", ", requirement.Permissions)));
}
return Task.CompletedTask;
}
}
Here is a global exception handler:
app.UseExceptionHandler(exceptionHandler =>
{
exceptionHandler.Run(async context =>
{
await HandleException(context.Features.Get<IExceptionHandlerFeature>().Error, context, env);
});
});
public async Task HandleException(Exception exception, HttpContext context)
{
var message = exception?.Message;
if (exception is BaseException)
{
context.Response.StatusCode = (int)(exception as BaseException)?.HttpErrorCode;
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
await context.Response.WriteAsync(message);
await context.Response.CompleteAsync();
}