February 15, 2008

Save web form contents on authentication timeout and restore it

In ASP.NET solutions using forms based security, there is a problem if the forms authentication ticket times out while the user is filling out a large web form. This is a simple solution to that problem using a custom HttpModule and a single .aspx page:

A typical scenario is where:
  1. The solution is using forms authentication (using cookies, and with a timeout set to 30 minutes in web.config).

  2. The user starts filling out a large web form. 

  3. The user takes a long phonecall or goes to lunch. 

  4. The user returns, resumes filling out the form and submits. 

  5. Bang - the user is redirected to the login page because the authentication ticket timed out.
    After logging in again the form will be empty - all work filling out the form is lost.
The easiest solution would be to make the forms authentication ticket live very long (ex. 24 hours). But in my experience many customers require that the login times out after typically 30 minutes for security reasons.

I had no luck googling a solution, so here is what I came up with:

Solving this requires a simple HttpModule and a transit page. The HttpModule will capture the posted form and save it to application state, just before the forms authentication redirects the user to the login page. Application state is used because Session state is not accessible at the time of interception. A few tricks are used here, see the code for details.

After login, the same HttpModule will redirect the request to a transit page. This transit page will restore the form contents as hidden fields and do a submit (http post triggered on load) to the original page. There you go, form restored. The flow looks like this:


This way everything from the original form post will be restored, including ViewState. The original page will happily recieve the postback and do normal page processing. The restoring of the form will be transparent to the end user. Usually the transit page will be so fast that the user doesn't see it.

This will also work with Ajax to some extent. Form fields inside an UpdatePanel will be re-populated by the form saver, but any callback from controls inside the UpdatePanel will be "replaced" by a full page postback from the transit page.

I have put together a small demo with the HttpModue and the transit page at MSDN Code Gallery: http://code.msdn.microsoft.com/formsaver.

1 comment:

  1. Excellent idea, just one thing to note, in
    FormSaverHttpModule.context_BeginRequest
    it may be better to have
    context.Response.Redirect("~/Timeouts/FormStateRestore.aspx?ReturnUrl=" + HttpUtility.UrlPathEncode(context.Request.RawUrl));
    (UrlEncode replaced by UrlPathEncode to preseve slashes)

    ReplyDelete