31

I'm creating a custom role provider and I set a Authorize attribute specifying a role in my controller and it's working just fine, like this:

[Authorize(Roles="SuperAdmin")]
public class SuperAdminController : Controller
...

But when an user doens't have access to this controller, he's redirected to login page. How can I redirect him to a "AcessDenied.aspx" page?

Yusubov
  • 5,815
  • 9
  • 32
  • 69
André Miranda
  • 6,420
  • 20
  • 70
  • 94

9 Answers9

43
[AccessDeniedAuthorize(Roles="SuperAdmin")]
public class SuperAdminController : Controller

AccessDeniedAuthorizeAttribute.cs:

public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if(filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Result = new RedirectResult("~/AcessDenied.aspx");
        }
    }
}
eu-ge-ne
  • 28,023
  • 6
  • 71
  • 62
  • 7
    If the user is logged in and attempts to access the page, they will get redirected to the AccessDenied page. Good. But, if the user is not logged in, they will get redirected to the AccessDenied page. Bad. In that situation, they should get redirected to the Login page. – Matt Frear Jan 19 '11 at 18:06
  • 3
    If you want the page to redirect normally in the case that the user is no longer in, after the base.OnAuthorization() method call, add an if statement around the rest of the code that checks if the Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated. This way the user is directed to the AccessDenied page unless the user is not authenticated...in which case it will do the default action (redirect to the login page) – Frinavale Nov 01 '13 at 17:47
  • where does this class get put? in the controller? – Jay Dec 05 '13 at 14:43
  • Working fine but I think Frinavale point is good and valid. (y) – Fahad Mahmood May 08 '15 at 07:31
  • MVC: AcessDenied.aspx ? not cshtml ? – Kiquenet Dec 12 '18 at 11:44
26

Here's my solution, based on eu-ge-ne's answer. Mine correctly redirects the user to the Login page if they are not logged in, but to an Access Denied page if they are logged in but are unauthorized to view that page.

[AccessDeniedAuthorize(Roles="SuperAdmin")]
public class SuperAdminController : Controller

AccessDeniedAuthorizeAttribute.cs:

public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new RedirectResult("~/Account/Logon");
            return;
        }

        if (filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Result = new RedirectResult("~/Account/Denied");
        }
    }
}

AccountController.cs:

public ActionResult Denied()
{
    return View();
}

Views/Account/Denied.cshtml: (Razor syntax)

@{
    ViewBag.Title = "Access Denied";
}

<h2>@ViewBag.Title</h2>

Sorry, but you don't have access to that page.
Matt Frear
  • 52,283
  • 12
  • 78
  • 86
8

Take a look at tvanfosson's Answer from this very similar question, This is what I am doing(Thanks to tvanfosson), so now I just have to say:

[MyAuthorize(Roles="SuperAdmin",ViewName="AccessDenied")]
public class SuperAdminController : Controller
...

If the user is not in the role, they will get thew view specified by ViewName.

Community
  • 1
  • 1
KP.
  • 2,138
  • 23
  • 21
6

Redirect is not always the best solution

Use standard http code 403:

return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
gdbdable
  • 4,445
  • 3
  • 30
  • 46
6

A slight improvement to Matt's answer by avoiding the need to hard-code the Logon page and optionally setting the access denied view within the attribute:

public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
    public string AccessDeniedViewName { get; set; }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated &&
            filterContext.Result is HttpUnauthorizedResult)
        {
            if (string.IsNullOrWhiteSpace(AccessDeniedViewName))
                AccessDeniedViewName = "~/Account/AccessDenied";

            filterContext.Result = new RedirectResult(AccessDeniedViewName);
        }
    }
}
Vic Alcazar
  • 98
  • 1
  • 5
1

I had similar issue. No matter what role I had, I was always redirected to LogIn page instead of AccessDenied. The fix was unbelievably easy, but it might not work in all cases. So it turned out, that I had wrong order in Startup.cs of these two lines:

app.UseAuthentication();
app.UseAuthorization();

Make sure if app.UseAuthentication(); is BEFORE app.UseAuthorization();

In other words, ask "Who are you?" first, and then "Are you allowed here?", not the other way.

Cynninge
  • 11
  • 1
0
public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);

            if (filterContext.Result is HttpUnauthorizedResult && WebSecurity.IsAuthenticated)
            {
                filterContext.Result = new RedirectResult("~/Account/AccessDenied");
            }
        }
    }
Yuriy
  • 21
  • 1
0

I've built on Vic's answer to allow me to have a different Access Denied page for each of the application's areas. Did it by returning a RedirectToRouteResult instead, which instead of redirecting to a URL relative to the root of the application it redirects to the current area's controller and action:

public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
    public string AccessDeniedController { get; set; }
    public string AccessDeniedAction { get; set; }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated &&
            filterContext.Result is HttpUnauthorizedResult)
        {
            if (String.IsNullOrWhiteSpace(AccessDeniedController) || String.IsNullOrWhiteSpace(AccessDeniedAction))
            {
                AccessDeniedController = "Home";
                AccessDeniedAction = "AccessDenied";
            }

            filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { Controller = AccessDeniedController, Action = AccessDeniedAction }));
        }
    }
}
Farinha
  • 17,636
  • 21
  • 64
  • 80
0

Just a small update to Vic Alcazar, Added details of the request url in redirect So that can log the details of the access denied and by who if want

public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
    public string AccessDeniedViewName { get; set; }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated &&
            filterContext.Result is HttpUnauthorizedResult)
        {
            if (string.IsNullOrWhiteSpace(AccessDeniedViewName))
                AccessDeniedViewName = "~/Account/AccessDenied";

            var requestUrl = filterContext.HttpContext.Request.Url;

            filterContext.Result = new RedirectResult(String.Format("{0}?RequestUrl={1}", AccessDeniedViewName, requestUrl));
        }
    }
}
True Solutions
  • 137
  • 1
  • 7