views:

212

answers:

1

Hello,

I have a website that uses basic ASP.Net forms authentication. In the web.config file we specify specific access rules for individual pages and directories. Everything works great.

However, now I have some new requirements. My domain contains many different sites setup among different subdomains. I have two DNS subdomains that map to this application. One is aaa.mysite.com and the other is www.mysite.com. If a particular web request is received from subdomain aaa.mysite.com for a page protected by FormsAuthentication, before the FormsAuthentication logic is handled (the user would be re-directed to the login page), I want to execute some code first. This code would essentially try to read a cookie from a third subdomain, say zzz.mysite.com, and if does not exist, Response.Redirect to a login page in the zzz.mysite.com application.

I tried handling this via a base class that any of my Forms Authentication protected pages could inherit from, and then calling the special code in the Page_PreInit function. However, FormsAuthentication handles the redirect to the Login page even before the PreInit function is called.

Does anyone know a good way to handle this case? If Page_PreInit won't work, where can I put code so that it executes prior to the FormsAuthentication redirect does, but where I also have access to which page it is (and what class it inherits from, so I can see if it inherits from System.Web.UI.Page or if it inherits from my special BasePage).

Any ideas? I think I could use the Globals Application_BeginRequest, but then this would be called for every single request, which doesn't seem like a very good idea.

I can't be the first person who has needed a way to handle an event prior to the FormsAuthentication, so please if you could give me some additional ideas I would be very appreciative!

Thanks!

+1  A: 

If the cookie has been written to zzz.example.com then a site on www.example.com cannot read it - the way to share cookies across subdomains is to write them to .example.com.

This can be configured in forms authentication using the domain attribute on the forms element in the web.config:

<forms [...]
   domain=".example.com">

Note the leading period in the domain names.

Edit to respond to comment

You should probably be hooking into the PostAuthenticateRequest event - this is fired after the users identity (or lack of) is established, and you could register a custom HttpModule to receive this event.

Edit to show workings

Ok, I've just tested the following setup:

A web application project, with the following directory structure:

/Default.aspx                    -- Simple aspx page.
/Login.aspx                      -- Simple aspx page, with a Login control.
/web.config                      -- Main application config.
/Classes/CheckingAuthenticate.cs -- HttpModule, configured in root.
/Restricted/Default.aspx         -- Simple asp page.
/Restricted/web.config           -- Config file for authorization

So, the root web.config sets up Forms authentication, using a standard ASP.NET membership provider, and sets /Login.aspx as the login page. I have also registered a custom HttpModule in there:

<httpModules>
  <add name="CheckingAuthenticate" 
       type="TempWebApp.Classes.CheckingAuthenticate"/>
  [...]
</httpModules>

The web.config in /Restricted/ denies access to anonymous users (this could equally be done in a <location> element in the root):

<configuration>
  <system.web>
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>
</configuration>

I then have the following code in my http module:

using System;
using System.Web;
using System.Web.Security;

namespace TempWebApp.Classes {
  public class CheckingAuthenticate : IHttpModule {

    public void Dispose() {
        //clean-up code here.
    }

    public void Init(HttpApplication context) {
      context.PostAuthenticateRequest += OnPostAuthenticate;
    }

    public void OnPostAuthenticate(object sender, EventArgs e) {
      var app = sender as HttpApplication;

      if (!UrlAuthorizationModule.CheckUrlAccessForPrincipal(app.Request.Path,
                                                            app.User,
                                                            "GET")){
        //Code here to read cookies, redirect user etc.
      }
    }
  }
}

This will fire after the user has been authenticated, but before ASP.NET attempts to authorize the user, so you get a chance to check the access yourself and redirect instead. I've been able to hit breakpoints on that quite happily. I didn't get to see the AuthorizeRequest or PostAuthorizeRequest events if the user didn't have access to those pages.

Zhaph - Ben Duguid
Yes, this is true, the cookie is written to .example.com, so that all the subdomains can access it. The question remains then if the cookie does not exist yet, how can I Response.Redirect them to another domain prior to the Forms Authentication redirect taking place?
kazzamalla
I can definitely write code in my Global.asax.cs file for this event, but doesn't that fire after the user has been authenticated? I want to do my redirect prior to the user being redirected by the forms Authentication to the login page and asked to login. However, as in my comment to Hank, I need a way to test whether or not the page the user is trying to access is protected by the forms authentication first. Any ideas?
kazzamalla