1. #1
    Ext User
    Join Date
    Jun 2007
    Posts
    16
    Vote Rating
    0
    Ben is on a distinguished road

      0  

    Question [SOLVED] Coping with session timeout

    [SOLVED] Coping with session timeout


    My application uses ExtJS to provide a front-end to a bunch of stateless services (using a RESTful architecture). However, in order to use these services the user must authenticate themselves with the system, which creates a session with the overriding web application (a Spring/Hibernate application). We're using the Acegi sub-project from Spring to provide security services at a variety of levels within the application stack.

    Now the actual problem: from time to time, the user's session will timeout. When this happens, the web application will respond to their next request with a login form. That's all very well in a standard page-refreshing application, but with ExtJS single-page application, the login form is next to useless with AJAX requests. I have found out how to detect this situation at the Connection/XMLHttpRequest level - I can alert the user and throw away the request, but what I'd really like to be able to do is to store the original XMLHttpRequest, put up a login dialog, re-authenticate, and then re-submit the original request, sending the results back into the single-page application in the manner originally intended (via callbacks, etc).

    I've got as far as this:

    PHP Code:
    Ext.lib.Ajax['handleTransactionResponse'] = function(ocallbackisAbort) {

        if (!
    callback) {
            
    this.releaseObject(o);
            return;
        }

        var 
    httpStatusresponseObject;

        try
        {
            if (
    o.conn.status !== undefined && o.conn.status != 0) {
                
    httpStatus o.conn.status;
            }
            else {
                
    httpStatus 13030;
            }
        }
        catch(
    e) {
            
    httpStatus 13030;
        }

        if (
    httpStatus >= 200 && httpStatus 300) {
            if (
    o.conn.responseText.indexOf('<meta name="Purpose" content="login" />') > 0) {
                
    alert('re-authentication required');
                
    // open my login page (perhaps this should be an ExtJS alert,
                // or a hidden div in the single-page application with the login form in it
                
    openWindow('login.jsp''Login'800800);
            }
            else {
                
    responseObject this.createResponseObject(ocallback.argument);
                if (
    callback.success) {

        .... 
    However, I'm struggling with how I approach the next stage...

    Does anyone have any suggestions as to refine this?

    Many thanks

    Ben
    Last edited by Ben; 5 Oct 2007 at 5:09 AM. Reason: Add Solved to thread title

  2. #2
    Sencha - Community Support Team hendricd's Avatar
    Join Date
    Aug 2007
    Location
    Long Island, NY USA
    Posts
    5,962
    Vote Rating
    10
    hendricd will become famous soon enough hendricd will become famous soon enough

      0  

    Lightbulb


    Study this thread a bit. It may generate some ideas. And after that, consider this as well. It adds username, password support (and more) to lib.Ajax requests as well.
    "be dom-ready..."
    Doug Hendricks

    Maintaining ux: ManagedIFrame, MIF2 (FAQ, Wiki), ux.Media/Flash, AudioEvents, ux.Chart[Fusion,OFC,amChart], ext-basex.js/$JIT, Documentation Site.


    Got Sencha licensing questions? Find out more here.


  3. #3
    Sencha - Ext JS Dev Team evant's Avatar
    Join Date
    Apr 2007
    Location
    Sydney, Australia
    Posts
    16,655
    Vote Rating
    583
    evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute

      0  

    Default


    In my application, I use a standard request handler that everything passes through. This handler expects the response in a particular format. If it doesn't receive the response in that format, it assumes there was an error (either a 403/500/etc, or a session timeout).

    Something along the lines of:

    Code:
    Ext.Ajax.request(
    {
       callback: function(options, success, response)
       {
          var text = response.responseText;
          var o;
          response = null;
          try
          {
             o = Ext.decode(text);
          }
          catch(e) { /*some error*/ }
          if (o && o.success) //success is some JSON property indicating that it worked
             doSuccess();
          else
             failed();
       }
    }
    );

  4. #4
    Ext User
    Join Date
    Jun 2007
    Posts
    16
    Vote Rating
    0
    Ben is on a distinguished road

      0  

    Default


    Thanks for the suggestions, guys.

    My final solution is to do the following
    PHP Code:
    var loginDialog;
    Ext.lib.Ajax['runLoginDialog'] = function (objectcallbackmsg) {

        if (
    loginDialog === undefined) {
            
    loginDialog = new Ext.BasicDialog('login-dialog', {
                
    width320height180modaltrueshadowtruemyform: new Ext.form.Form({ labelWidth100 }),
                
    password_field: new Ext.form.TextField({ fieldLabel'Password'name'password'width:100inputType'password' }),
                
    user_field: new Ext.form.TextField({ fieldLabel'User Name'name'username'width:100 })
            });

            
    loginDialog.myform.addloginDialog.user_fieldloginDialog.password_field );
            
            var 
    submitFunction = function(dialog) {
                var 
    btn = (dialog.mybutton !== undefined dialog.mybutton dialog);
                
    Ext.Ajax.request({
                    
    params: { j_username loginDialog.user_field.getValue(), j_password loginDialog.password_field.getValue() },
                    
    url'j_acegi_security_check'originalCallbackbtn.mycallbackoriginalObjectbtn.myobject
                
    });
                
    loginDialog.myform.reset();
                
    loginDialog.hide();
            };
                
            
    loginDialog.addKeyListener([1013], submitFunction);

            
    loginDialog.mybutton loginDialog.myform.addButton({ text'Continue'cls'x-btn-text-icon login'handlersubmitFunction });
            
    loginDialog.myform.render('j_acegi_security_check-form-ct');
        }
        
    document.getElementById('j_acegi_security_check-form-messages').innerHTML = (msg !== undefined msg '');

        
    loginDialog.mybutton.mycallback callback;
        
    loginDialog.mybutton.myobject object;
        
        
    loginDialog.show();
        
    loginDialog.myform.reset();
        
    loginDialog.user_field.focus();
    }

    Ext.lib.Ajax['handleTransactionResponse'] = function(ocallbackisAbort) {

        if (!
    callback) {
            
    this.releaseObject(o);
            return;
        }

        
    /* if the request came from here originally, use it's callback */
        
    var originalCallback undefinedoriginalObject undefined
        try {
            
    originalCallback callback.argument.options.originalCallback;
            
    originalObject callback.argument.options.originalObject;
            if (
    originalCallback !== undefined) { 
                
    callback originalCallback;
            }
        }
        catch (
    e) {
        }

        var 
    httpStatusresponseObject;
        try {
            
    httpStatus = (o.conn.status !== undefined && o.conn.status != 0) ? o.conn.status 13030;
        }
        catch(
    e) {
            
    httpStatus 13030;
        }

        if (
    httpStatus >= 200 && httpStatus 300) {
            var 
    response o.conn.responseText;
            if (
    response.indexOf('<meta name="Purpose" content="login" />') > 0) {
                var 
    index response.indexOf('<div class="error">');
                var 
    errorDiv undefined;
                if (
    index 0) {
                    var 
    endIndex response.substr(index 19).indexOf('</div>') - 1;
                    
    errorDiv response.substr(index 19endIndex);
                }
                
    Ext.lib.Ajax.runLoginDialog(ocallbackerrorDiv);
            }
            else {
                if (
    originalObject !== undefined) {
                    
    o.tId originalObject.tId;
                }
                
    responseObject this.createResponseObject(ocallback.argument);
                if (
    callback.success) {
                    if (!
    callback.scope) {
                        
    callback.success(responseObject);
                    }
                    else {
                        
    callback.success.apply(callback.scope, [responseObject]);
                    }
                }
            }
        }
        else {
            switch (
    httpStatus) {
                case 
    12002:
                case 
    12029:
                case 
    12030:
                case 
    12031:
                case 
    12152:
                case 
    13030:
                    
    responseObject this.createExceptionObject(o.tIdcallback.argument, (isAbort isAbort false));
                    if (
    callback.failure) {
                        if (!
    callback.scope) {
                            
    callback.failure(responseObject);
                        }
                        else {
                            
    callback.failure.apply(callback.scope, [responseObject]);
                        }
                    }
                    break;
                default:
                    if (
    originalObject !== undefined) {
                        
    o.tId originalObject.tId;
                    }
                    
    responseObject this.createResponseObject(ocallback.argument);
                    if (
    callback.failure) {
                        if (!
    callback.scope) {
                            
    callback.failure(responseObject);
                        }
                        else {
                            
    callback.failure.apply(callback.scope, [responseObject]);
                        }
                    }
            }
        }

        
    this.releaseObject(o);
        
    responseObject null;
    }; 
    The critical parts are as follows (in order of definition above):
    1. in the login dialog, the handler on the submit button puts the originating callback & request object in the button's configuration (in the line with url: 'j_acegi_security_check'), for use in the next step
    2. at the beginning of the handleTransactionResponse method, we recall the original callback & request object so that the results from the 'j_acegi_security_check' call can be applied to the original XMLHttpRequest, in the course of the normal handling of this method
    3. the handleTransactionResponse method detects the authentication failure and launches the login dialog based on the 'Purpose' meta tag.

    This technique relies upon the feature of Acegi whereby it remembers your intended HTTP request in the Session, and returns the response to that request when you successfully authenticate.

    Also, I've found the dozen or so locations in my code base where I make requests that might fail authentication, and I wrap those with a 'ping' request. That 'ping' request is usually the one that fails, and when it returns correctly, the wrapped request is then sent:

    PHP Code:
    function openFileWindow(urlname) {
        
    Ext.Ajax.request({
            
    url'images/ping.gif',
            
    callback: function(optionssuccessresponse) {
                
    openWindow(urlname850850);
            }
        });

    Hope this mechanism helps someone else.

    Regards,
    Ben

  5. #5
    Ext User
    Join Date
    Sep 2009
    Posts
    5
    Vote Rating
    0
    djarquin is on a distinguished road

      0  

    Default Catching all the requests

    Catching all the requests


    I had a lot of request in one application, what I do is to catch then all
    PHP Code:
    Ext.util.Observable.observeClass(Ext.data.Connection);
        
    Ext.data.Connection.on('requestcomplete', function(dataconnresponse){
            var 
    txt response.responseText;
    //ERROR CATALOG, DAVID JACOB JARQUIN PRG: 10133817 SESSION TIMEOUT

            
    if(txt.indexOf("Error:0x10133817")>-1){
                
    alert("SESSION TIMEOUT...");
                
    document.location "../../Default.aspx";
                return
            }
        }); 
    very useful

Thread Participants: 3