When it was first developed, System.Web.Mvc.AuthorizeAttribute was doing the right thing -
older revisions of the HTTP specification used status code 401 for both "unauthorized" and "unauthenticated".
From the original specification:
If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.
In fact, you can see the confusion right there - it uses the word "authorization" when it means "authentication". In everyday practice, however, it makes more sense to return a 403 Forbidden when the user is authenticated but not authorized. It's unlikely the user would have a second set of credentials that would give them access - bad user experience all around.
Consider most operating systems - when you attempt to read a file you don't have permission to access, you aren't shown a login screen!
Thankfully, the HTTP specifications were updated (June 2014) to remove the ambiguity.
From "Hyper Text Transport Protocol (HTTP/1.1): Authentication" (RFC 7235):
The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
From "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content" (RFC 7231):
The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.
Interestingly enough, at the time ASP.NET MVC 1 was released the behavior of AuthorizeAttribute was correct. Now, the behavior is incorrect - the HTTP/1.1 specification was fixed.
Rather than attempt to change ASP.NET's login page redirects, it's easier just to fix the problem at the source. You can create a new attribute with the same name (AuthorizeAttribute
) in your website's default namespace (this is very important) then the compiler will automatically pick it up instead of MVC's standard one. Of course, you could always give the attribute a new name if you'd rather take that approach.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
From the readme word doc for RC1 (not indexed by google)
ASP.NET Compiler Post-Build Step
Currently, errors within a view file are not detected until run time. To let you detect these errors at compile time, ASP.NET MVC projects now include an MvcBuildViews property, which is disabled by default. To enable this property, open the project file and set the MvcBuildViews property to true, as shown in the following example:
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
Note Enabling this feature adds some overhead to the build time.
You can update projects that were created with previous releases of MVC to include build-time validation of views by performing the following steps:
- Open the project file in a text editor.
- Add the following element under the top-most
<PropertyGroup>
element:
<MvcBuildViews>true</MvcBuildViews>
- At the end of the project file, uncomment the
<Target Name="AfterBuild">
element and modify it to match the following:
<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
</Target>
Best Answer
Here is an attribute that I've created that can be used to direct to an unauthorized security action. it also allows you to specify a Reason which will be passed to the Unauthorized action on the Security controller, which you can then use for the view.
You can create any number of properties to customize this to fit your particular application, just make sure to add it to the RouteValueDictionary.
Here is the security controller
Here is the attribute declaration on a controller you wish to secure
Here is how PermissionsManager does the check to see if the user has the permissions