Shawn Miller

Forms Authentication With Absolute Return URLs

October 31, 20068:00 AM

We've talked briefly about our single sign-on implementation before.  This time we'll go more in-depth and explain how we changed ASP.NET 2.0's return URLs in forms authentication from relative form to absolute form.

The Problem
Using ASP.NET 2.0 membership and some web.config tricks we were able to provide our users a single sign-on that works across several of our web sites scattered on various servers.  Our users sign in one time and share their authentication across our www, blogs, and atlas sites.

When an anonymous user attempts to access a secured resource (i.e. http://www.freshlogicstudios.com/MyAccount/Default.aspx) ASP.NET forms authentication redirects them to the login page and appends the original requested resource in the "ReturnURL" query string (i.e. http://www.freshlogicstudios.com/Members/Default.aspx?ReturnURL=/MyAccount/Default.aspx).

Notice that the ReturnURL parameter is a relative URL.  This works well until we request a secured resource on another site (i.e. http://atlas.freshlogicstudios.com/MyAccount/GpsDevices/Default.aspx).  By default we get a redirect to our "www" site with a "ReturnURL" query parameter that contains a relative path (i.e. http://www.freshlogicstudios.com/Members/Default.aspx?ReturnURL=/MyAccount/GpsDevices/Default.aspx).

Not at all what we need.  The relative ReturnURL probably doesn't exist on the site handling authentication.  Our users are redirected to a page prompting them for a username/password, and then are greeted with a 401: page not found error.

The Solution
When an anonymous user requests a secured resource we want the ReturnURL to contain an absolute path (i.e. http://www.freshlogicstudios.com/Members/Default.aspx?ReturnURL=http://atlas.freshlogicstudios.com/MyAccount/GpsDevices/Default.aspx)

We make this happen by implementing an HttpModule.  In the module we handle the PostAuthenticateRequest event.  This event takes place after the identity of the user has been established yet before the module has verified that the user is authorized for the requested resource.

In the PostAuthenticationRequest event we manually check if the user is able to access the requested resource.  If not, we redirect them to the login URL with the ReturnURL parameter specified as an absolute URL.

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

 

namespace FreshLogicStudios.Framework.Web.Security

{

    /// <summary>

    /// Verifies that the user has permission to access the URL requested.

    /// </summary>

    public class AuthorizationModule : IHttpModule

    {

        #region Constructors

 

        /// <summary>

        /// Default constructor.

        /// </summary>

        public AuthorizationModule()

        {

        }

 

        #endregion

 

        #region Event Handlers

 

        protected void context_PostAuthenticateRequest(object sender, EventArgs e)

        {

            if (!UrlAuthorizationModule.CheckUrlAccessForPrincipal(HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath, HttpContext.Current.User, HttpContext.Current.Request.RequestType))

            {

                HttpContext.Current.Response.Redirect(String.Format("{0}?ReturnUrl={1}", FormsAuthentication.LoginUrl, HttpContext.Current.Request.Url.AbsoluteUri));

            }

        }

 

        #endregion

 

        #region IHttpModule Members

 

        /// <summary>

        /// Disposes of the resources (other than memory) used by this module.

        /// </summary>

        public void Dispose()

        {

        }

 

        /// <summary>

        /// Initializes this module and prepares it to handle requests.

        /// </summary>

        /// <param name="context">An HttpApplication that provides access to the methods, properties, and events common to all application objects within an ASP.NET application.</param>

        public void Init(HttpApplication context)

        {

            context.PostAuthenticateRequest += new EventHandler(context_PostAuthenticateRequest);

        }

 

        #endregion

    }

}