HttpModule to allow a custom error page for 401.2 Access Denied in ASP.NET
As you know, the customErrors section of web.config allows you to define your own pages to display to the user when an error occurs. It allows for a default page to display when any unhandled error occurs, and it also allows you to specify different pages to display depending on the HTTP status code.I like to take advantage of ASP.NET's declarative security model by defining the users and roles that are authorized to execute different parts of my website. For example, I can limit access to a page named Protected.aspx by declaring that all users must be a meber of the VerySpecialUsers group. This can be accomplished by adding the following section to my web.config:
<location path="Protected.aspx">
<system.web>
<authorization>
<allow roles="VerySpecialUsers"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
I like this model as it allows me to easily modify the access restrictions for a page, and since it uses ASP.NET's Role Provider framework, it is not tied to any implementation about how role membership is determined. By default in a Windows Authentication/NTLM scenario, the roles map to the group memberships of your Windows login account (Administrators, Power Users, Users, etc). But you can very easily create your own Role Provider that retrieves group membership information from your own data store.
When a user does not meet the authorization requirements to visit a certain page, ASP.NET will return a 401.2 Access Denied error. The page looks like the standard yellow screen of death (YSOD) that ASP.NET returns for all errors.The customErrors section of web.config is supposed to let you provide a friendlier response to your users so they never have to see the YSOD. You would think that all you need to do is define a page for the 401 status code in customErrors and all would be good. You would be wrong. You can even try changing the custom error page defined in your IIS settings, and it still won't work.
The newsgroups have countless unanswered posts about this issue. Fortunately, my most recent attempt to solve this problem finally found some answers. The first one I found, and the one I use for my solution, was posted by John "iSpeakGeek" on ASPFree forums. I have since discovered a CodeProject article by George Mamaladze, and even a (somewhat related) Microsoft KB article. Hopefully this post will help improve the search results for other people struggling with this issue.
The key to the solution is to intercept the EndRequest event of the page lifecycle, check for a 401 status code, and then execute your custom page. The Microsoft article suggests intercepting the Error event and checking for an UnauthroizedAccessException, but that didn't work for my scenario. It may only apply when using impersonation, as the article describes. I need to do more testing and will possibly modify my solution to cover more scenarios.
Since I need this functionality on pretty much every website I make, I created an HttpModule to encapsulate the logic. The nice thing about my module approach is that you simply register the module in your web.config, and it will automatically respect the settings you define in your customErrors section. There is no hardcoded dependence on a specific error page or need for a custom configuration section.
<httpModules>
<add name="CustomAccessDenied" type="FlimFlan.CustomAccessDenied, FlimFlan.CustomAccessDenied"/>
</httpModules>
<customErrors mode="On" defaultRedirect="Error.htm">
<error statusCode="401" redirect="AccessDenied.aspx"/>
</customErrors>
I've packaged up the full source and a sample website. Use it and change it as you wish. If you don't care about the source, just grab the DLL from the DemoWeb\Bin folder and put it in your own website's Bin folder.
I can't claim it is a general purpose solution yet, as I have not tested it with all possible authentication/authorization settings. If you find a scenario that doesn't work, please let me know and I'll see if I can address it.
Download FlimFlan.CustomAccessDenied
Comments
I want my .Net app to be accessible to a group only.For this I entered the following in web.config
<authorization>
<allow roles="Group name" />
<deny users="*" />
</authorization>
and want all users who are not member of this group redirected to customised page NoAccess.htm
<customErrors mode="On" defaultRedirect="Error.aspx" >
<error statusCode="401" redirect="NoAccess.htm" />
</customErrors>
I am using Windows authetication,have added the group in the local users and groups.
When I run the set up it prompts for login credentials which when entered gives error message 401.2 Access is denied
Please help.
This is great thanks. The only problem I have now is that my error page is in the root, and I have an image in it which is in the images folder. \ I cannot get the image to display.
I have a folder of the root called Dashboards, which is secured.
Root
CustomError.aspx
--Images
--Dashboards
When I look at the path for the image, it has the path Dashboards/images/error.jpg
and so doesnt display?
This is the markup which works fine when you view the page on its own as the page is in the root.
<img src="images/error.jpg">
When your HTTP handler returns my custom error page, the image has the path:
Dshboards/images/error.jpg (the error was raised by a page in the Dashboards folder)
Any thoughts?
to Joshua Flanagan - thank verry much for this solution...works fine for me
web.config
<httpModules>
\ <add name="CustomAccessDenied" type="FlimFlan.CustomAccessDenied, FlimFlan.CustomAccessDenied"/>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>
<authentication mode="Windows"/>
<customErrors mode="RemoteOnly" defaultRedirect="Error.htm">
<error statusCode="401" redirect="~/errorpages/denied.aspx"/>
</customErrors>
<location path="Reporting/default.aspx">
<system.web>
<authorization>
\ <!--<allow roles="BUILTIN\Administrators,DomainName\Manager" />-->
<allow roles="Human_resources" />
<deny users="*" />
</authorization>
</system.web>
\ </location>
when visiting hrapps/reporting/default.aspx then page will only redirect to error.htm
dll file was imported from demoweb vis VS2005
any thoughts on this? from what I can see, the setup is fairly similar.I am running ajax if that matters.
mac
Error
Could not load file or assembly 'FlimFlan.CustomAccessDenied' or one of its dependencies.
I have the following
<httpModules>
<add name="CustomAccessDenied" type="FlimFlan.CustomAccessDenied, FlimFlan.CustomAccessDenied"/>
...other modules
</httpModules>
i registered through and it appears to be loaded in correctly.
I don't think this solution will work as presented when using Forms authentication. The reason, is because the FormsAuthenticationModule already intercepts the 401 and performs a redirect to the "login page".
Many posts out there are looking for a solution to redirect to an unauthorized page instead of the login page. I think this is the best approach, but would require clearing all of the http modules and redeclaring them.
Let me know what you think.
Will