[ACCEPTED]-ASP.NET MVC - How to show unauthorized error on login page?-authorization
UPDATE (Jun 2015): @daniel-lidström has correctly pointed 17 out that you should not use Response.Redirect 16 in an ASP.NET MVC application. For more 15 information about why, please see this link: Response.Redirect and ASP.NET MVC – Do Not Mix.
UPDATE (Sep 2014): I'm 14 not sure when HandleUnauthorizedRequest 13 was added to the AuthorizeAttribute, but 12 either way I've been able to refine the 11 AuthorizeRedirect code into something smaller 10 and simpler.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
public string RedirectUrl = "~/Error/Unauthorized";
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(RedirectUrl);
}
}
}
Original Answer Below (still fully functional)
I've left this answer here as 9 it still gives you an insight as to how 8 the Authorization pipeline works.
For anyone 7 still landing here, I've edited Ben Scheirman's 6 answer to automatically redirect to an unauthorized 5 page when the user is logged in but not 4 authorized. You can change the redirect 3 path using the name parameter RedirectUrl.
EDIT: I've 2 made the solution thread-safe thanks to 1 the advice of Tarynn and MSDN
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
private const string IS_AUTHORIZED = "isAuthorized";
public string RedirectUrl = "~/error/unauthorized";
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
httpContext.Items.Add(IS_AUTHORIZED, isAuthorized);
return isAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null
? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED])
: false;
if (!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl);
}
}
}
You can look for the ?ReturnUrl=
querystring value, or 5 you can create your own authorization filter 4 & set a field in TempData
indicating the reason.
Here 3 is a simple custom filter that will do the 2 trick:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
// NOTE: This is not thread safe, it is much better to store this
// value in HttpContext.Items. See Ben Cull's answer below for an example.
private bool _isAuthorized;
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
_isAuthorized = base.AuthorizeCore(httpContext);
return _isAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if(!_isAuthorized)
{
filterContext.Controller.TempData.Add("RedirectReason", "Unauthorized");
}
}
}
Then in your view, you can do something 1 like this:
@if(TempData["RedirectReason"] == "Unauthorized")
{
<b>You don't have permission to access that area</b>
}
(Though I'd recommend a better approach than these magic strings, but you get the point)
Ben Cull's method works well, but remember 11 there are two AuthorizeAttribute classes 10 - one in System.Web.HTTP (used by Web API), and 9 the other in System.Web.Mvc. Ben's method 8 uses the System.Web.Mvc class. For clarity, I 7 suggest using the fully qualified path.
If 6 you're using Web API alongside MVC, you 5 will need to implement two filters:
public class AuthorizeRedirectMVCAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("~/Account/AccessDenied");
}
}
}
public class AuthorizeRedirectAPIAttribute : System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
base.HandleUnauthorizedRequest(actionContext);
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
}
}
}
Note 4 that asp.net will let you decorate your 3 MVC controller with an API filter - it just 2 won't work the way you expect, so keep your 1 attribute names explicit.
If you have a controller and don't want 12 to have a url in you code you can redirect 11 this way as well. It will not change the 10 url in the address bar of the browser so 9 the user will never see the url for the 8 unauthorized page. This was written in 7 MVC 3. This method will also work if you 6 want to redirect them to a login page or 5 if you want to redirect them to a page to 4 just tell them they aren't authorized. I 3 had section in the program that some user 2 didn't have rights to but they were logged 1 in so this is what I used.
public class AuthorizedRedirect : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
return isAuthorized;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.RequestContext.RouteData.Values["controller"] = "error";
filterContext.Result = new ViewResult { ViewName = "unauthorized" };
}
And an even simpler version that utilizes 4 FormsAuthentication settings. For those 3 not familiar with Contract, Contract.Requires 2 is a .NET 4 addition. Pros and cons to using 1 Code Contracts.
public class RequiresAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
Contract.Requires(filterContext != null);
HttpContextBase context = filterContext.RequestContext.HttpContext;
if (context.User.Identity.IsAuthenticated)
{
// user does not possess the required role permission
string url = context.GetCustomErrorUrl(401);
context.Response.Redirect(url);
}
else
{
// redirect the user to the login page
string extraQueryString = context.Request.RawUrl;
FormsAuthentication.RedirectToLoginPage(extraQueryString);
}
}
}
Going further from divide_byzero's answer 6 even if you don't have a controller you 5 can still use the HandleUnauthorizedRequest 4 to change the redirect.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthoriseRedirect : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.RequestContext.HttpContext.Response.Redirect("UrlToRedirectTo");
}
}
Comes in handy if 3 you have a legacy webforms site that you 2 will be converting to MVC over a longer 1 period of time.....!
I like what Brian Vander Plaats posted, just 1 added few improvements:
/// <summary>
/// Authorize or redirect to an unauthorized MVC action if the user does not have the required roles
/// (an unauthenticated user will be redirected to the defualt sign in action)
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeOrRedirectAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
var routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Unauthorized");
filterContext.Result = new RedirectToRouteResult(routeData.Values);
}
}
}
/// <summary>
/// Authorize or redirect to an unauthorized API action if the user does not have the required roles
/// (an unauthenticated user will be redirected to the defualt sign in action)
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeOrRedirectApiFilterAttribute : System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
base.HandleUnauthorizedRequest(actionContext);
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.