title: How do I create a custom AuthorizeAttribute that is specific to the area, controller and action?
body: <p>In other words, is this a really stupid idea?</p>
  
  <pre><code>[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
  public class AuthorizeActionAttribute : AuthorizeAttribute
  {
      public override void OnAuthorization(AuthorizationContext filterContext)
      {
          // get the area, controller and action
          var area = filterContext.RouteData.Values["area"];
          var controller = filterContext.RouteData.Values["controller"];
          var action = filterContext.RouteData.Values["action"];
          string verb = filterContext.HttpContext.Request.HttpMethod;
  
          // these values combined are our roleName
          string roleName = String.Format("{0}/{1}/{2}/{3}", area, controller, action, verb);
  
          // set role name to area/controller/action name
          this.Roles = roleName;
  
          base.OnAuthorization(filterContext);
      }
  }
  </code></pre>
  
  <p><strong>UPDATE</strong>
  I'm trying to avoid the following, in a scenario where we have extremely granular role permissions because the roles are setup on a per-client basis and attached to user groups:</p>
  
  <pre><code>public partial class HomeController : Controller
  {
      [Authorize(Roles = "/supplierarea/homecontroller/indexaction/")]
      public virtual ActionResult Index()
      {
          return View();
      }
  
      [Authorize(Roles = "/supplierarea/homecontroller/aboutaction/")]
      public virtual ActionResult About()
      {
          return View();
      }
  }
  </code></pre>
  
  <p>Can anyone enlighten me to a secure way to write this AuthorizeRouteAttribute to access the route information and use this as the role name? As Levi says, the RouteData.Values isn't secure. </p>
  
  <p>Is the use of the executing httpContext.Request.Path any more secure or better practice?</p>
  
  <pre><code>public override void OnAuthorization(AuthorizationContext filterContext)
  {
      if (filterContext == null)
      {
          throw new ArgumentNullException("filterContext");
      }
  
      if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
      {
          // auth failed, redirect to login page
          filterContext.Result = new HttpUnauthorizedResult();
          return;
      }
  
      var path = filterContext.HttpContext.Request.Path;
      var verb = filterContext.HttpContext.Request.HttpMethod;
  
      // these values combined are our roleName
      string roleName = String.Format("{0}/{1}", path, verb);
  
      if (!filterContext.HttpContext.User.IsInRole(roleName))
      {
          // role auth failed, redirect to login page
          filterContext.Result = new HttpUnauthorizedResult();
          // P.S. I want to tell the logged in user they don't 
          // have access, not ask them to login. They are already
          // logged in!
          return;
      }
  
      //
      base.OnAuthorization(filterContext);
  }
  </code></pre>
  
  <p>This maybe illustrates the issue a little further:</p>
  
  <pre><code>enum Version
  {
      PathBasedRole,
      InsecureButWorks,
      SecureButMissingAreaName
  }
  
  string GetRoleName(AuthorizationContext filterContext, Version version)
  {
      //
      var path = filterContext.HttpContext.Request.Path;
      var verb = filterContext.HttpContext.Request.HttpMethod;
  
      // recommended way to access controller and action names
      var controller = 
          filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
      var action = 
          filterContext.ActionDescriptor.ActionName;
      var area = "oh dear...."; // mmmm, where's thearea name???
  
      //
      var insecureArea = filterContext.RouteData.Values["area"];
      var insecureController = filterContext.RouteData.Values["controller"];
      var insecureAction = filterContext.RouteData.Values["action"];
  
      string pathRoleName = 
          String.Format("{0}/{1}", path, verb);
      string insecureRoleName = 
          String.Format("{0}/{1}/{2}/{3}", 
          insecureArea, 
          insecureController, 
          insecureAction, 
          verb);
      string secureRoleName = 
          String.Format("{0}/{1}/{2}/{3}", 
          area, 
          controller, 
          action, 
          verb);
  
      string roleName = String.Empty;
  
      switch (version)
      {
          case Version.InsecureButWorks:
              roleName = insecureRoleName;
              break;
          case Version.PathBasedRole:
              roleName = pathRoleName; 
              break;
          case Version.SecureButMissingAreaName:
              // let's hope they don't choose this, because
              // I have no idea what the area name is
              roleName = secureRoleName;
              break;
          default:
              roleName = String.Empty;
              break;
      }
  
      return roleName;
  }
  </code></pre>