AuthenticateRequest not raised when doing a Server.Transfer

Background
I’ve recently enhanced a legacy product with support for “custom links” by writing a simple HttpModule. (Similar to the built in UrlMapping-support, just a little bit more flexible.)

The module is very simple, it hooks in to each request and check if a mapping exists for the current path.
In order to do this all it needs is a simple map of new to old (legacy) paths.
E.g. {/Home => /Page/1/, /About => /Page/24/Article/2}

If a mapping is found, a call to Server.Transfer is done to transfer the request the the correct page.
This worked like a charm and I did not have to touch the legacy application on order to get it working.

The problem
Yesterday I needed to do some minor tweaking in the the legacy application and all when pretty smooth until I hit a page that was served using my module above.

The log output of a working request looks like this:

[10] INFO  ASP.global_asax [(null)] - /########################################\
[10] INFO  ASP.global_asax [(null)] - ### Begin Request ########################
[10] INFO  ASP.global_asax [(null)] - /default.aspx
[10] DEBUG ASP.global_asax [(null)] - User IS authenticated.
[10] DEBUG MyProject.Impl.AdminService [(null)] - LoadUserFindUserById(http://ola.kodar.se/)
[10] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - Opening a new session [
[10] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - 	session: 16143157
[10] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - ]
[10] DEBUG MyProject.Impl.NHibernate.GenericManager [(null)] - Executing criteria [
[10] DEBUG MyProject.Impl.NHibernate.GenericManager [(null)] - 	UserId = http://ola.kodar.se
[10] DEBUG MyProject.Impl.NHibernate.GenericManager [(null)] - ]
[10] DEBUG MyProject.Impl.AdminService [(null)] - User found: UserId: http://ola.kodar.se, Name: Ola Herrdahl
...
[10] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - Closing session [
[10] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - 	session: 16143157
[10] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - ]
[10] INFO  ASP.global_asax [(null)] - ### End Request ##########################
[10] INFO  ASP.global_asax [(null)] - /default.aspx
[10] INFO  ASP.global_asax [(null)] - \########################################/

But a request that passed my fancy custom http module would look like this:

[7] DEBUG MyProject.Web.UrlMappingModule [(null)] - Checking url:/seo-friendly-url
[7] DEBUG MyProject.Web.HttpHandler [(null)] - Processing request [
[7] DEBUG MyProject.Web.HttpHandler [(null)] - 	path: /
[7] DEBUG MyProject.Web.HttpHandler [(null)] - 	name: Page.aspx
[7] DEBUG MyProject.Web.HttpHandler [(null)] - 	AbsolutePath: /Pages/215
[7] DEBUG MyProject.Web.HttpHandler [(null)] - ]
[7] DEBUG MyProject.Web.HttpHandler [(null)] - Transfering user to: /Page.aspx?Pages=215&
[7] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - Opening a new session [
[7] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - 	session: 21943666
[7] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - ]
...
[7] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - Closing session [
[7] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - 	session: 21943666
[7] DEBUG MyProject.Impl.NHibernate.NHibernateHelper [(null)] - ]
[7] INFO  ASP.global_asax [(null)] - ### End Request ##########################
[7] INFO  ASP.global_asax [(null)] - /seo-friendly-url
[7] INFO  ASP.global_asax [(null)] - \########################################/

I noticed that the neither Application_BeginRequest nor Application_AuthenticateRequest (in global.asax) seemed to be called when the request was handled by my UrlMappingModule.
But strangely Application_EndRequest was executed as expected…

Obviously, my UrlMappingModule was not so unobtrusive as I first thought…

The investigation
In the custom module I did all the magic upon the BeginRequest event.

        public void Init(HttpApplication context) {
            context.BeginRequest += OnBeginRequest;
            if (!initialized) {
                initialized = true;
                LoadMappings();
            }
        }

But looking at this again, I realized that doing a Server.Transfer so early in the pipeline was probably a bad idea.
However the HttpServerUtility.Transfer documentation states that:

For the current request, terminates execution of the current page and starts execution of a new page by using the specified URL path of the page.

This seemed to be ok until I read the remarks:

ASP.NET does not verify that the current user is authorized to view the resource delivered by the Transfer method. Although the ASP.NET authorization and authentication logic runs before the original resource handler is called, ASP.NET directly calls the handler indicated by the Transfer method and does not rerun authentication and authorization logic for the new resource. If your application’s security policy requires clients to have appropriate authorization to access the resource, the application should force reauthorization or provide a custom access-control mechanism.

So obviously the request pipeline events are not raised as one might excepted them to be in the page that one is transferring to.

The solution
When doing a server transfer one should probably aim at doing it as late as possible in the HttpApplication pipeline.
In this case, I changed my module to do the transfer call upon the PostAuthenticateRequest event instead of on BeginRequest and everything started working as expected.

,

No comments yet.

Leave a Reply