Creating an interstitial login page with JqueryMobile

· Read in about 3 min · (432 words) ·

So, at work, we’re building a mobile website using JqueryMobile. The app has a bunch of publicly visible pages however, other pages require the user to be authenticated. We didn’t want the user to be forced to login on the first page. Instead, whenever a protected page is accessed, and if the user insn’t logged into the app, we’d like to take him to the login page. Once he’s successfully authenticated, then take him to the page he was navigating to. Doing this in a normal webapp is quite standard - however, with JqueryMobile, query params meddle with the hash navigation model. Also, the page that the user tries to access could be a div in the same physical page or a different url that needs to be fetched.

Trying to solve this was interesting as we were all really just getting started with JqueryMobile - so finding the ideal solution required a few tries. The solution takes a leaf out of JqueryMobile’s approach. The outline of the solution is:

  1. Any page div that’s a protected resource is marked with a data-needs-auth="true" attribute

  2. We hook into the document level pagebeforechange event to see if the user is trying to transition to a page requiring authentication. If so, then check if we have the user’s authenticated context available.

  3. if the said context isnt available,

  4. Cancel default event handling since we’re now going to navigate the user to the login page.

  5. save the toPage object - so once the user is logged in, we know where to take him.

  6. navigate to the login page.

  7. In the login page, the page can call the server apis to autheticate the user. Once the user is authenticated, then

  8. See if there’s a valid returnTo object, if so, take the user to the page.

  9. If not, take the user to a 'default' page - in our case, this is the app dashboard page.

Code below

var pageVars = {}
$(document).bind("pagebeforechange", function (event, data) {
    if (typeof data.toPage == 'object' && data.toPage.attr('data-needs-auth') == 'true') {
        if (!sessionStorage.getItem("TokenSSKey")) {
            if (!localStorage.getItem("TokenLSKey")) {
                pageVars.returnAfterLogin = data;
                event.preventDefault();
                $.mobile.changePage("#Login_Page", { changeHash: false });
            } else {
            sessionStorage.setItem('TokenSSKey',
            localStorage.getItem("TokenLSKey"));
            }
        }
    }
});

The login event handler that handles the server response that’s received once we pass the username and password

function SuccessLogin(data) {
    if (data != null && data.LoginResult != null) {
        if (data.LoginResult.Code === 0) {
            localStorage.setItem('UNameLSKey', data.LoginResult.User.AccountName);
            if ($("#RememberMeChkBx").is(":checked")) {
                ErrorPanel.html("");
                localStorage.setItem('TokenLSKey', data.LoginResult.Token);
                sessionStorage.setItem('TokenSSKey', data.LoginResult.Token);
            } else {
                ErrorPanel.html("");
                sessionStorage.setItem('TokenSSKey', data.LoginResult.Token);
            }
            if (pageVars && pageVars.returnAfterLogin) {
                $.mobile.changePage(pageVars.returnAfterLogin.toPage);
            } else {
                $.mobile.changePage("#DashBoard_Page", { changeHash: false });
            }
        }
    }
}