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:
- Any page div that's a protected resource is marked with a
data-needs-auth="true"attribute - 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.
- if the said context isnt available,
- Cancel default event handling since we're now going to navigate the user to the login page.
- save the toPage object - so once the user is logged in, we know where to take him.
- navigate to the login page.
- In the login page, the page can call the server apis to autheticate the user. Once the user is authenticated, then
- See if there's a valid returnTo object, if so, take the user to the page.
- If not, take the user to a 'default' page - in our case, this is the app dashboard page.
Code below
[sourcecode language="javascript"]
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"));
}
}
}
});
[/sourcecode]
The login event handler that handles the server response that's received once we pass the username and password
[sourcecode language="javascript"]
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 });
}
}
}
}
[/sourcecode]
Awesome piece of code, very elegant. Thanks
ReplyDeletethx. saved me some time. there. appreciate the post.
ReplyDeletegood code ,exactly what i was looking for!tx
ReplyDeleteThis is a very useful example of JQM. I have one question.
ReplyDeleteInside my JQM page, '/app1', I have created footer tabbar in my JQM application that looks like this. (I actually used codiqa to generate this code, so results may vary if there is non-standard JS or CSS behind it.)
Account Details
Generate Invoice
My "#page1" Account Details div is using the data-needs-auth attribute. Punters are correctly redirected to a login page using the pagebeforechange event specified above.
I'd like to do a similar login check for the external link to "/app2". In this case, I would the user to be redirected to login after they press the "Generate Invoice' button, but before the HTML for /app2 is actually requested.
Can you suggest a similar event binding so that I could assign a "data-needs-auth" attribute to the link?
Apologies, I should have escaped my code above with a 'pre' tag.
ReplyDeleteAccount Details
Generate Invoice
@krisgest looks like what you're asking is how to intercept a click on a link and force login?
ReplyDeleteIf that's so, why do you create a page in your app with the data-needs-auth and once the user lands on the page, pull in the html for the page from the external link? (or redirect to the link - but then you lose all your page state)..
If this 'external' link is something you control, you'd be better off consuming JSON from it...
I'm sorry - but with out additional info, that's all I can venture... drop me a PM at raghu dot rajagopalan at dot com
I'm a bit confused by the email address you supplied in the above post.
ReplyDeleteI think it is missing part of the domain.
I transcribed: rxxxx.rxxxxxxxxx@.com
sorry :) - gmail
ReplyDelete