I am using the Authorize
attribute like this:
[Authorize (Roles="Admin, User")]
Public ActionResult Index(int id)
{
// blah
}
When a user is not in the specified roles, I get an error page (resource not found). So I put the HandleError
attribute in also.
[Authorize (Roles="Admin, User"), HandleError]
Public ActionResult Index(int id)
{
// blah
}
Now it goes to the Login page, if the user is not in the specified roles.
How do I get it to go to an Unauthorized page instead of the login page, when a user does not meet one of the required roles? And if a different error occurs, how do I distinguish that error from an Unauthorized error and handle it differently?
Add something like this to your web.config:
<customErrors mode="On" defaultRedirect="~/Login">
<error statusCode="401" redirect="~/Unauthorized" />
<error statusCode="404" redirect="~/PageNotFound" />
</customErrors>
You should obviously create the /PageNotFound
and /Unauthorized
routes, actions and views.
EDIT: I'm sorry, I apparently didn't understand the problem thoroughly.
The problem is that when the AuthorizeAttribute
filter is executed, it decides that the user does not fit the requirements (he/she may be logged in, but is not in a correct role). It therefore sets the response status code to 401. This is intercepted by the FormsAuthentication
module which will then perform the redirect.
I see two alternatives:
Disable the defaultRedirect.
Create your own IAuthorizationFilter
. Derive from AuthorizeAttribute
and override HandleUnauthorizedRequest. In this method, if the user is authenticated do a redirect to /Unauthorized
I don't like either: the defaultRedirect functionality is nice and not something you want to implement yourself. The second approach results in the user being served a visually correct "You are not authorized"-page, but the HTTP status codes will not be the desired 401.
I don't know enough about HttpModules to say whether this can be circumvented with a a tolerable hack.
EDIT 2: How about implementing your own IAuthorizationFilter in the following way: download the MVC2 code from CodePlex and "borrow" the code for AuthorizeAttribute. Change the OnAuthorization method to look like
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
// Is user logged in?
else if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// Redirect to custom Unauthorized page
filterContext.Result = new RedirectResult(unauthorizedUrl);
}
else {
// Handle in the usual way
HandleUnauthorizedRequest(filterContext);
}
}
where unauthorizedUrl
is either a property on the filter or read from Web.config.
You could also inherit from AuthorizeAttribute and override OnAuthorization
, but you would end up writing a couple of private methods which are already in AuthorizeAttribute.