Tip/Trick: Integrating ASP.NET Security with Classic ASP and Non-ASP.NET URLs

One of the questions I am often asked is "How can I integrate ASP.NET security with Classic ASP other non-ASP.NET URLs?".  Specifically, people want to know if they can integrate ASP.NET's Forms Authentication, Role Based Security, and URL Authorization features with Classic ASP, PHP, JSP, .HTM, .JPG and other non-ASP.NET URLs.

The good news is that this is pretty easy with ASP.NET 2.0 and IIS 6.0 today, and will get even easier in the IIS 7.0 timeframe.  The below blog post demonstrates how to integrate ASP.NET 2.0's Forms Authentication and Login/Membership features with classic ASP and static .HTML files. 

For a much more detailed walkthrough of how to achieve this (as well as how to integrate features like ASP.NET role based authorization with classic ASP applications), please read Chapter 6 of Stefan Schackow's excellent ASP.NET 2.0 Security, Membership, and Role Management book.

Some Background on IIS 6.0 Wildcard Mappings

IIS 6.0 with Windows Server 2003 added support for an ISAPI feature called "wildcard mappings".  Wildcard mappings provide a way to configure IIS to cause all requests coming into the server to first be routed to one or more ISAPI extensions for processing.  What is cool about wildcard mappings in IIS 6.0 is that after the ISAPI extension that is processing the wildcard extension is finished, it can then cause IIS to pass control of the request to the extension or internal handler within IIS that normally would process the request.

ASP.NET 2.0 includes built-in support to take advantage of this wildcard mapping feature.  This enables you to run ASP.NET code (or your own custom code) before and after the existing ISAPI extension that processes a non-ASP.NET URL (for example: a .asp, .php or .htm request) executes.

We can use this feature to enable a bunch of cool integration features - including using ASP.NET authentication and authorization features to secure all URLs on a web-server. 

How to Configure an IIS 6.0 Wildcard Mapping

For this sample I've created a new IIS application within the IIS 6.0 admin tool called "wildcardtest".  It points to a directory that will contain a few files: "default.aspx", "login.aspx", "test.asp" and "test.htm" (these last two files being resources not usually handled by ASP.NET):

By default when a URL request for a .aspx page comes to the application, the ASP.NET ISAPI will process the request.  By default when a URL request for test.asp comes to the application, the classic ASP ISAPI will process the request - and no ASP.NET code will run.  When a URL request for test.htm comes to the application, IIS6 will process the request internally - and again no ASP.NET code will ever run.

We'll change this by enabling wildcard mappings for this application, and configure ASP.NET to run code before and after all requests to the server.  To-do this, right-click on the application within the IIS Admin Tool and select the "properties" context menu item on it.  This will bring up the application properties dialog:

You can then click the "configuration" button to pull up the URL mapping rules for the application:

Note how the top of this dialog lists the default ISAPI extension mappings (each URL extension is mapped to an ISAPI responsible for processing it).  The bottom half of the dialog lists the "wildcard application map" rules.  We can add a application wildcard mapping to the ASP.NET ISAPI by clicking the "insert" button, and pointing at the ASP.NET 2.0 ISAPI extension on disk:

Very, Very Important: Make sure that you uncheck the "Verify this file exists" checkbox.  If you don't do this ASP.NET URL resources like WebResource.axd and other URL's handled by ASP.NET that aren't backed by a physical file won't work anymore.  This will lead to errors within your pages.

Now close out all of the dialogs by clicking "ok" to accept the changes.  You've now configured ASP.NET to be able to run code before and after each URL request into the application.

Enabling Forms Authentication and Url Authorization for non-ASP.NET resources

Once we've completed the above steps to register ASP.NET 2.0 as a wild-card mapping for all URLs into our IIS application, we can then use the standard ASP.NET authentication and authorization techniques to identify users in our application and grant/deny them access to it.

For example, we could add a web.config file to our application that enables ASP.NET's forms authentication feature for this application, and sets up two URL Authorization rules that deny "anonymous" users access to both the test.asp and test.htm URLs:

<?xml version="1.0"?>

<configuration>

    
<system.web>
        
<authentication mode="Forms" />
    </
system.web>

    
<location path="test.asp">

        
<system.web>
            
<authorization>
                
<deny users="?"/>
                <
allow users="*"/>
            </
authorization>
        
</system.web>
        
    
</location>

    
<location path="test.htm">

        
<system.web>
            
<authorization>
                
<deny users="?"/>
                <
allow users="*"/>
            </
authorization>
        
</system.web>
        
    
</location>

</configuration>

Now, when I attempt to access either "test.asp" or "test.htm", the ASP.NET authentication and authorization system will execute first to check whether I'm logged into the application with forms-authentication, and if not redirect me to the login.aspx page within my application for me to login:

Note how the "ReturnUrl" used by ASP.NET's forms authentication system above has automatically set the "test.asp" url to redirect back to once I'm logged in (this works just like it would for a .aspx page).  Once I successfully enter a username/password, I'll then have access to the test.asp page:

Since the above page is a classic ASP file, I obviously don't have a "User.Identity.Name" property that I can use to identify the logged in user like I would in an ASP.NET page.  However, I can retrieve the "AUTH_USER" ServerVariable value within classic ASP to obtain the username (ASP.NET automatically populates this before delegating the processing back to the classic ASP ISAPI). 

The code to use this from within classic ASP would look like below:

<html>
    
<body>
        
<h1>Classic ASP Page</h1>

        
<h3>
            You are logged in as: 
            
<u>
                
<%=Request.ServerVariables("AUTH_USER") %>
            
</u>        
        
</h3>
    
</body>
</html>

Click here to download a complete sample application that implements the above solution.  By default it will use a SQL Express database to store ASP.NET 2.0's Membership and Role Management data.  Alternatively, you can create and register a SQL 2000 or SQL 2005 database to store the membership and role management values.  This older ASP.NET Security tutorial I did shows how to-do this.

How to Learn More about ASP.NET Security

I highly recommend buying a copy of Stefan Schackow's excellent ASP.NET 2.0 Security, Membership, and Role Management book.  Stefan is a key member of the ASP.NET team, and owned and designed the security features in the ASP.NET 2.0 release.  As such, he really, really, really knows what he is writing about. 

Chapter 6 of his book is titled "Integrating ASP.NET Security with Classic ASP" and contains much more detail about the solution I demonstrated above (as well as how to use role security with it, and pass data back and forth between ASP.NET and classic ASP). 

 

Click here to learn more about the book and/or buy it online.

Other Online ASP.NET Security Resources

I've published a number of ASP.NET Tips, Tricks, Recipes and Tutorials in the past that cover ASP.NET 2.0 security.  Below is a short-list of them that you might want to review:

For more free ASP.NET Tips, Tricks, and Tutorials I've written, please check out my ASP.NET Tips, Tricks and Tutorials listing.

Hope this helps,

Scott

19 Comments

  • Interesting post for using the security features of the Asp.net 2.0 in other langauge

  • Nice! I already have that book but I'm currently in chapter 3... :-)

  • Are there any performance implications I should be aware of using this approach?

  • Hi Alfred,

    The nice thing about the above solution (versus just mapping the .htm or .jpg extensions to the ISAPI extension directly) is that it uses the origional ISAPI to process the request. This means that from a runtime efficiency perspective it is pretty efficient.

    With a non-trivial .asp page (for example: one that hit a database), I don't think you'd see any real performance difference when using the above wildcard mapping solution.

    Hope this helps,

    Scott

  • Scott,
    As usual great content..


    It would be great to see how this would work in a more complex scenario such as in a java application, where the isapi filter is just passing the request onto tomcat or another servlet container.

  • Scott, Scott, Scott... As always, your article perfectly explains an issue I've skirted around for years. This is very pertinent to what I am doing with my current employer. Keep the good stuff coming!

  • Hi Cole,

    I haven't tried it with a Tomcat or other Java Servlet container - but that should work just fine.

    Because ASP.NET modifies the ServerVariables collection to pass things like the username or role-list to the later ISAPI, the approach should work well for any ISAPI Extension - since there is no special requirement for them to implement in order to take advantage of this.

    The only thing I'm not 100% sure of is whether it works for both ISAPI Filters or just ISAPI Extensions. I suspect it might only work for ISAPI Extensions - which is what any application container should be implemented as anyway (otherwise you end up chewing up IIS' I/O threads).

    I know that ColdFusion and JRun are implemented both as ISAPI Extensions. I presume Tomcat is as well.

    Hope this helps,

    Scott

  • Hi Akaka,

    Unfortunately this feature isn't supported on IIS 5.1. :-(

    It requires IIS 6.0 or higher.

    Sorry,

    Scott

  • Just what I was looking for, to complete my upgrade for full ASP.NET Membership...

  • Hi Scott,

    First time poster. I've always enjoyed your writings--always found them to be informative and insightful.

    One quick question tho., will I be able to achieve extensionless URL rewriting using the very same manner? Please advise.

  • Hi Eric,

    You should be able to achieve this if you use the ISAPIRewrite option I described in this blog post: http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx

    ISAPIRewrite is implemented as an ISAPI filter which should execute before the wildcard mapping logic. So if your rewrite rule rewrites the classic asp page to a .asp file, it should then go through the wildcard mapping scheme I described above before being handed off to the classic ASP ISAPI.

    Let me know if this works (or if you run into any problems with it)!

    Thanks,

    Scott

  • Hi Ingar,

    What do the authorization rules in your web.config file look like?

    One important thing to keep in mind is that URL rewriting happens relatively early in the request processing - and before URL Authorization kicks in.

    So if you need to make sure that the rewritten URL (in your case ~/Pub/account/signin.aspx) is available for everyone to visit. Otherwise, they will get an access denied, in which case ASP.NET will redirect them back to /account/login - which is of course going to cause the redirect to happen again, and again, and again.

    This might be what you are running into.

    Hope this helps,

    Scott

  • REALLY GREAT BLOG, Scott!

    I've been playing with Forms Authentication for a couple weeks trying to deny authenticated and/or authenticated users access to non-ASP.Net files via web.cong.

    I did the thing in IIS and now it looks like all file types are treated as .aspx pages. In other words, it appears access is forbidden to unauthorized users for all file types.

    Is this the way it should work? Block all file types from unauthenticated users?

    I didn't add or specific files, just left my and tags the same in web.config.

    VERY COOL!

  • Hi Jon,

    If you add the wildcard mapping support above, then it will run all URLs through the authentication and authorization system.

    You'll want to use directives to grant/deny access to either individual URLs or directories in the application.

    Hope this helps,

    Scott

  • Just a query

    I came across this article while troubleshooting my existing implementation of a similar solution where i have a asp.net security wrapper around a complete asp system (stage 1 of migration to .net). With regard to the use of forms authentication using the above method it would appear that the sliding expiration for the auth cookie that is created is not being refreshed while in the internal classic asp pages and thus times out after a finite time period (mine is set to 40 min). Any ideas on how to rectify this so that the classic asp pages cause a refresh and the sliding expiration can be used?

    Cheers Ed

  • Hi Scott!

    Great stuff!

    I try to implement this on my IIS, and it works just like I need it for and application ASPX with one other ASP in the same virtual directory, but I have another virtual directory with an other ASPX application, that have an independent login page for authentication, and that login page doesn’t authenticated anymore since I made the change.

    I have just implemented the ISAPI extension mappings, like your instructions, to the virtual directory I wanted, but it affects the other virtual directory…what I’m doing wrong?

    TKS Once again!

    Bruno

  • Thanks Scott. I've been looking for this.

    One question: why is it that the ASP.NET security works for all (non-aspx) files within my local built in server (Cassini)?

  • Hi Eds,

    The built-in web-server (aka Cassini) handles all URLs that are sent to it - and so the standard ASP.NET authentication/authorization system ends up being invoked for everything.

    I blogged about this a little here: http://weblogs.asp.net/scottgu/archive/2006/01/31/437027.aspx

    Thanks,

    Scott

  • Hi Scott,

    Would you be able to provide instructions for doing this under IIS 7?

    Thanks

    Al

Comments have been disabled for this content.