How to make Authorize attribute return custom 403 error page instead of redirecting to the Logon page
What I would do is subclass AuthorizeAttribute and override its HandleUnauthorizedRequest to return HTTP status code 403 if user is authenticated. I would then add a system.webServer\httpErrors section to my Web.Config to replace the default 403 with my custom page (this last part requires IIS 7+). Here's how:
public class MyAuthorizeAttribute : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
filterContext.Result = new HttpStatusCodeResult(403);
else
filterContext.Result = new HttpUnauthorizedResult();
}
}
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="403" />
<error statusCode="403" responseMode="ExecuteURL" path="/Error/MyCustom403page" />
</httpErrors>
</system.webServer>
</configuration>
You should be able to create your own class that derives from AuthorizeAttribute
and override the AuthorizeCore
method to provide the authorization mechanism that you want, so that you can apply your custom authorization code by using an attribute instead of moving it into the controller.
If you need more fine-grained control over authorization, then I recommend that you create an implementation of the IActionFilter
interface (on an attribute, then apply the attribute to your methods). This will allow you to intercept calls before they go to the controller, and provide alternate actions before your controller method is called.
This is achieved by implementing the OnActionExecuting
method on the IActionFilter
interface. If your logic determines that you should not make the call to the controller at all, and you want to provide an ActionResult
to be processed instead, then you would set the Result
property on the ActionExecutingContext
instance passed into the method. By doing this, that ActionResult
is processed instead of going to the controller method to get an ActionResult
.
If you want to return a 403 error code, then you can't use the ContentResult
class. You will have to create your own class that derives from ActionResult
and override the ExecuteResult
method to set the StatusCode
property on the HttpResponseBase
to 403, like so:
internal class Http403Result : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
// Set the response code to 403.
context.HttpContext.Response.StatusCode = 403;
}
}
public class CustomActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Result = new Http403Result();
}
}
Of course, you can generalize the Http403Result
class to take a constructor which will accept the status code that you want to return, but the concept remains the same.