Geeks With Blogs
Blake Caraway

I have an UpdatePanel on a page within a web application that is using Forms Authentication. The UpdatePanel callback executes as a result of a custom JavaScript function that calls __doPostback.

If a user loads the page in question, but then allows the forms auth ticket to expire on the server due to inactivity (i.e. goes to lunch, goes home for the day, etc) and then interacts with the web page to fire off the UpdatePanel callback, the partial postback fails b/c the forms auth ticket is expired. The partial postback is short-circuited b/c the forms authentication has done its job and has issued the redirect to the login.aspx page. The UpdatePanel does not handle this. Instead, it tries to parse the page response which is directing the browser to redirect. AJAX (thru the ScriptManager/UpdatePanel) throws an error about not being able to parse the response.

Obviously, I would rather detect that the forms auth expired BEFORE attempting the __doPostback and have the user redirected to the login page to authenticate once again. What I needed was a way to see (from the browser) if a user had a valid forms auth cookie on the server or not.

I was able to find a solution by using the ASP.NET 2.0 AJAX Extensions that enable the seamless proxy generation for Web services for client-side JavaScript running from the browser.

Here's what I did:

Web Service

Create a web service and decorate it with the [ScriptService] attribute. This declares to ASP.NET that it should provide a JavaScript proxy class for use in calling the web service. After doing this, you can then call this web service directly (append /js to the end) in the browser to see the generated JavaScript proxy code necessary to invoke the web method on the server from client-side script.

    [ScriptService] 
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    public class Authentication : WebService 
    {
        private IWebContext _webContext = ObjectFactory.GetInstance<IWebContext>();
 
        [WebMethod]
        public bool IsLoggedIn()
        {
            return this._webContext.IsUserAuthenticated;
        }
    }
 

Service Reference

Next add a ServiceReference within the ScriptManager pointing to the web service you just created. Just by doing this you should be able to load the page that you AJAX-ified in your browser and see the generated JavaScript code in the page source that will enable the page to "phone home" to the web service. You will see that to call the web service method, the JavaScript function will take three arguments: a success method callback, a failure method callback, and a variable to hold something useful from the calling context (note: this context info is NOT passed across the wire, it's just "forwarded" on to the client side callback functions after the call completes).

    <asp:ScriptManager ID="Manager" runat="server" EnablePartialRendering="true" EnablePageMethods="true"> 
        <Services> 
            <asp:ServiceReference path="~/WebServices/Authentication.asmx" InlineScript="true" /> 
        </Services> 
    </asp:ScriptManager> 


JavaScript

After this, I just wrote the necessary JavaScript methods to call the proxy method that handles the call across the wire. In my code below, the loadSKUs() method is the entry point. This method makes the call across the wire, indicating the callback methods that should fire after completion. In the successfulAuthCallback() method, the result is inspected to determine if we are still logged in or not. All I'm looking for is a value of 'true' coming back from the call to my WebMethod. If this is the case, then we are still considered to be 'logged in' and I can allow the UpdatePanel to do it's magic. If not, then I tell the entire page to reload b/c it will then be a real/normal full page postback and the normal flow will occur where the forms auth will validate the session and redirect to the login URL or allow the page to proceed normally.

When we make the async call once the forms auth cookie has expired, the result that comes back is actually the HTML response telling the browser to redirect -- and is the markup that the UpdatePanel was choking on to begin with b/c it didn't know what to do with it -- hence the parser error.


         function loadSKUs(selectedProductAndHand) {
            WebServices.Authentication.IsLoggedIn(successfulAuthCallback, failedAuthCallback, selectedProductAndHand);
         }


 
         function setProductAndHandAndPostBack(selectedProductAndHand)
         {
            var hiddenField = $get(hiddenProductAndHandField);
 
            if (hiddenField) {
                if (hiddenField.value != selectedProductAndHand)
                {
                    hiddenField.value = selectedProductAndHand;
                    __doPostBack(hiddenProductAndHandField,'');
                }
            }
         }
 
 
         function successfulAuthCallback(isLoggedInResult, selectedProductAndHand)
         {
            if (isLoggedInResult == true)
            {
                setProductAndHandAndPostBack(selectedProductAndHand);
            }
            else 
            {
                // reload the entire page so the Forms Auth will see that the cookie has expired and redirect to login                 window.location.reload(true);
            }
         }
 
         function failedAuthCallback(result, selectedProductAndHand)
         {
            alert("Auth Callback failed: Reason -- " + result);
         }



One other thing I had to add in my web.config is add an AJAX HTTP Handler for .asmx files:

    <httpHandlers> 
      <add verb="GET,HEAD" path="ScriptResource.axd"
type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, 
            Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
validate="false"/> 
      <remove verb="*" path="*.asmx"/> 
      <add verb="*" path="*.asmx"      type="System.Web.Script.Services.ScriptHandlerFactory"
validate="false"/>       
    </httpHandlers> 


So that's it. Now a user won't get an ugly parsing error from AJAX when an async call is made from the client after the forms auth cookie has expired on the server.


Problem Solved...Final Thought

I think this is too involved. I don't think we should be forced to write any custom code to handle the async calls to the server in order for forms auth to work properly within the AJAX space. Shouldn't the partial postback play nicely and handle this situation automatically somehow?

It's entirely possible I'm missing something here, though. This was a pain to overcome. I Googled extensively and posted a message on the ASP.Net AJAX forum looking for guidance on this issue, but didn't find any help. Please feel free to leave comments if you have solved this same problem -- especially if you have a different/easier solution.

 

          

Technorati :
Del.icio.us :

Posted on Saturday, June 2, 2007 11:17 PM ASP.Net AJAX | Back to top


Comments on this post: AJAX UpdatePanel Callback And Expired Forms Auth Ticket

# re: AJAX UpdatePanel Callback And Expired Forms Auth Ticket
Requesting Gravatar...
Hey Blake
Thanks for your post ... definitely agree with you
I don't think we should be forced to write any custom code to handle the async calls to the server in order for forms auth to work properly within the AJAX space.


I have just found the same problem with timeout while testing an app we are developing.

Cheers

JasonM
Left by JasonM on Jun 23, 2007 11:48 PM

# re: AJAX UpdatePanel Callback And Expired Forms Auth Ticket
Requesting Gravatar...
I have been trying to fix this problem, you seem to have the most graceful solution to handle the timeout event.

Any chance of a sample code download?

Chrys
Left by Chrys on Dec 10, 2007 9:27 PM

# re: AJAX UpdatePanel Callback And Expired Forms Auth Ticket
Requesting Gravatar...
If you are not getting desired results on your updatepanels without this fix, you should try to deploy your site to IIS6 before you give up.

The built in web server in VS2005 doesn't seem to do redirects but just to test I deployed and the updatepanels redirect properly if the ticket has timed out.
Left by Robert on Feb 07, 2008 2:12 PM

Comments have been closed on this topic.
Copyright © Blake Caraway | Powered by: GeeksWithBlogs.net