Back in November of last year I blogged an example of adding server-based login to a PhoneGap application. Essentially - how to create an HTML application on your mobile device that calls out to a remote server for authentication purposes. The blog post got quite a bit of traffic (and 100 comments at last count), but today a user pointed out an interesting issue and I thought it would be a great time to post an update to the demo with a fix in place.
Basically, the user noticed something odd with login failures. The code I wrote supported caching the username and password values in LocalStorage and on application startup would automatically log you in. This worked fine - until your password was changed on the server. Consider the code below:
You can see that when an error is returned by the server, we use PhoneGap's Notification Alert API to let the user know about the problem.
Ok - so that should have worked, right? However - here is where we have a problem. Now - when I do blog entries and demos, I try to balance between 'real world' code and code that focuses narrowly on the issue I'm trying to discuss. I don't like a lot of clutter as I want people to learn without distractions. In this case though I built my demo using jQuery Mobile for the UI framework, and here's where the issue came about.
We listen for the pageinit event on the first page to fire off code that checks for cached login credentials:
This is a jQuery Mobile event. But PhoneGap has its own events - with the most critical of them being deviceready. You can't do anything device-related until this event fires.
So take a wild guess what happened. My jQuery Mobile page was up and running before my code was actually ready to speak to the device. (And if you are curious, I tested this using simple console messages. See my blog entry for more on that topic.) For my user, I just told them to quickly switch to a browser-native alert() call. But I really wanted a 'proper' fix.
This led to the question then: If I want my code to not do anything until deviceready is fired, what is the best way to handle it?
While I'm not sure this is the "best" way, here is the solution I came up with. I began by simply adding a new page to my initial HTML page. Remember that jQuery Mobile supports multiple "pages" per HTML file.
For the most part, this is the same as the previous blog entry. I did update to the latest PhoneGap and jQuery Mobile libraries. The critical part is the addition of "launcherPage." Now let's look at main.js:
The important changes are right at the bottom. I use the deviceready event callback (deviceReady) to register my pageinit function and also change the page manually from the blank screen to the 'real' login page. Now that the deviceready event has fired, when and if an error is returned by the automatic login, we can be assured that the native alert functionality provided by PhoneGap's API will actually work.
For those interested, I've included a zip of the code with this blog entry. You can upload it directly to PhoneGap Build to get a native installer on the platform of your choice.
Archived Comments
Great post Ray! Thanks! This is going to be quite helpful to me in the near future.
Hi, i'm not sure to understand your code. What i'm trying to do is to authenticate the user from the phonegap app and the retrieve restricted contents from server that is running PHP. i've this working using a webapp and jqtouch, but with a webapp i simply submit the form, the page is reloaded and i can send jquery requests cause the user is logged, but i'm not sure about how to do this with phonegap without reloading the page and without using cookies...
@Longhorn: When you use Ajax from a Phonegap app, it will automatically include any cookies sent by your server. If you are using a session in your PHP app that is cookie based, then it should "just work".
Fantastic Raymond!! Thanks a lot... i'll try it as soon as possible and report results here ;)
Hi Raymond,
i think that your Post sample (http://www.coldfusionjedi.c... doesn't work. Cause i'm tasting it on eclipse with Android SDK and i have a alert with this message: "Your login failed".
Please help me.
Best regards Calin
It works for me - here is an example:
http://www.coldfusionjedi.c...
Ahh it works. Sorry my mistake. I use this Login Data username: username & password: password.
Okay, but i have another question. Can i See your Post Request Data? It is .php or javascript? I testing on php and it works. But i need .js Data for Post Request webservice.
Can you help me?
Best regards Colin
I'm not quite sure I get your question. The POST data is what you see being sent in the JavaScript. If you are asking about the server-side code, it is just some simple ColdFusion.
Hi ray, thanks first for your article. It's really great.
I am actually using cordova2.0 and trying your code. And if you use your app just like this, withoiut any modification it works only once...
For example, launch the app, and leave the username and password blank. Click Login. Then you get the alert. Everything is fine. Then do it again... click again on Login leaving everything blank. We are ending on a blank page... which is in fact the launcher page...
I am very new in html, but how do you link the submit action of the button to the call of handlelogin?
O, I found it !
in deviceReady() : It's the ".on(*) function instead of the .one(*)..
$("#loginForm").on("submit",handleLogin);
And now it's working !! :)
Glad you got it. I used one so that it would only listen for one click and remove itself when done - but that was obviously a mistake. Going to tweak the code now. Thanks!
No worries.
Another thing I see, is that this "fake" launcher page is in fact used and displayed during the start of the app. Which can also be a problem... you have your splash screen then the white screen from launcherPage and the your loginPage... which is not very beautiful...
I am searching for a way to solve this...but maybe you already have an idea? I tried to use the attribute "hidden" to the <div launcherPage> but this is not working...
I think one could replace the white screen with something more informative. So your splash screen is a company logo of some sort - a pretty picture. The white screen could be something that says "Loading...". This would make it more obvious what is going on. Technically stuff is loading during the splash screen, but to the end user, it just looks like a typical app startup experience.
Thoughts?
Hi Raymond, Very nice post, it helped me a lot.
Im a bit newbie, but i want to know what should i change if i have a local rest server?
This comment continues the last one ..
A local rest server like this:
http://localhost:8080/Web/ServiciosRest?usuario=123456&clave=android&modulo=login
and it goves me back something like this
{"type":"0"} if failed authentication or
{"tipo":"1"} if succesful authentication
Thanks
In a PhoneGap app, localhost would refer to the phone. If you mean your dev server and you are connected via USB, then 10.0.2.2 should work.
Hi again, thansks for your answer.Im using Eclipse to emulate an application in Android. The same as you i Think, The problem is I dont get a 'true' as answer from REST, but something like this {"Type":"1"}. How can i comprare that kind of chain with a value to let it continue to "some.html"
thanks
Are you asking how to handle a response of {Type:1}? If so - remember that the response will be parsed from JSON automatically. So you can look at the result object and check the value just like any other JavaScript variable.
If i get something like this?{"firstName": "John","lastName": "Smith","age": 25}
Does it parse automatically too?
As long as your server returns valid JSON, then it just works. To be clear, you need to update your logic to handle the different result, but jQuery will handle converting the JSON into a normal JS variable.
Hi Raymond, I did it. It wasnt that hard. Thanks for your time to answer my questions.
Hi create script. Just what i was after. Problem is im using cordova 2.2.0 and if i press the submit button with a wrong user/pass i get the alert message, If i type it wrong again or leave it blank the page goes blank.
Any ideas
Found it - an earlier commenter found it actually - this line
$("#loginForm").one("submit",handleLogin);
needs to be
$("#loginForm").on("submit",handleLogin);
I believe I fixed it in the code sample, but not the zip. I'll correct this now.
Hi, I am trying to make 'index.html' to be a login page, which connects to a Joomla! server. Once authenticated, it will redirect to, 'app.html' which retrieves data from the server. I am not sure how to set this login to authenticate from the site, 'web1.sydneytech-h.schools.n...' . Thanks,
Arsh
Um, I'm not exactly sure what you're asking. I don't know the site you mention nor do I know how it handles login.
Great job Raymond. Just want to clarify about using of "launcherPage" in the app. As you mentioned it's something like splashscreen while device is loading... but program doesn't going to the next steps when I'm running it on my emulator (stays in that white screen). In logcat I have "Skipped 34 frames! The app may be doing to much woork on its main tread" and next message "onMessage(spinner, stop)". I can't detect what I'm doing wrong. Have you any idea about this?
Also wanted to mention that by "login" click, it must process handleLogin(), which is: $("#loginForm").on("submit", handleLogin); and save the user data in local storage which are these lines:
window.localStorage["username"] = u;
window.localStorage["password"] = p;
But these two activities are not working for me,... Actually by default phonegap is html-based and automatically asks you to remember, skip or never save your data in browser (in my emulator it works so). But not saves by my main.js in localStorage. What do you think about it?
Ray,
Are you building and testing this locally?
I tried building this code without making any changes using adobes build service and it hangs on the launcherPage and never loads the login page. It appears that the deviceready is not triggering. I saw your post about the build service bug and removed the the cordova-1.8.1.js from the .zip file with no success either.
Craig - going to reply to you now since your question may be a bit easier. You removed the JS file, good, but do you still have
<script src="cordova.js"></script>
in your HTML? Build looks for this before injecting it.
Yes, I was building it with <script type="text/javascript" charset="utf-8" src="cordova-1.8.1.js"></script>. Replaced that line of code with: <script src="cordova.js"></script>. Zipped everything up.....without cordova.js, updated code in build, rebuilt, copied to Samsung G3 running android 4.1.1. Still no go.
Creg - maybe try checking the console:
http://www.raymondcamden.co...
Albert, can you see if the deviceready event is firing for you? You may be having the same issue as Creg.
Only exception was "exception firing pause event from native:1". Googling that now for more details.
My google search found a post that I should be using: <script src="phonegap.js"></script>. Tried that and now it appears to be working. I have to run to a meeting and will confirm later. Thanks Ray!
Holy smokes - duh - so sorry for that! I really think PG Build should include a small log message to the effect of "Couldn't find <script...> Did you mean to include it?"
Hello,
I wanted to know your complete php server side code.
Please tell me.
I do not write PHP.
The code using to handle login is *very* simple code. You take in a username. You take in a password. You check it with a SQL query. You return true or false.
If you can't write that in PHP than you probably shouldn't be writing PHP. Sorry to be blunt, but it's honest. Typically for a web platform doing SQL calls is one of the first things you learn.
just wanted that what shud b the output of the php and got it
thanx....
Ok. To be clear, should be true or false. Sorry - but I've gotten a number of requests like this lately. It's getting to me. ;)
hi Raymond,I have similar question as above
when I open index.html, i can only see the blank page.
im not so understand the above solution.
is it simply mean to rename the js as phonegap.js ?
a silly question, is it the same on both i open the html in computer's browser and that I install it in phone?
How are you opening the index.html file?
Hi Raymond . I have this all working . Problem is when I'm logged in and it redirects to the main HTML Non of the JavaScript or jquery works . I've tried outing it all again into the header bit with no luck. Am I missing something ? Any ideas would be great. This is for iOS and phone gap I'm doing it for
@Steve: I'd have to see more of your code. Please use Pastebin or Gists.
Hi Raymond.
First of all thanks for the example.
But I have some problems with it.
When I try to use it nothing happens when I click on to login button except for a message whether I want the password to be saved.
When I click again on the button nothing happens.
I wrote a similar little app and it reacts the same by doing nothing.
You need to test the application in Chrome and check the console to see what is going wrong.
It does work in chrome.
I was having problems with my ip adress.
It now also works on the device
Ah, to be fair, my code should have a generic Ajax error handler that would notice stuff like that and report it. :)
hi ajax not working in my chrome any solution pleaze
thks
If you are testing on the desktop, do not forget that you can't make an AJAX request to another domain due to security reasons. Either make use of the Ripple extension or use the Chrome flag that bypasses this restriction.
Thank you very much for your reply. is that you can explain a little more your answer
I'm working on a project and cordova jquery and I have a wsdl file (SOAP web service). I want to use ajax? Any solution or exemple? thank you
WSDL is just XML, so you can still access it via AJAX. Parsing it will be a bit of a pain. I'd google for a JS library for working with SOAP.
thank you very much but how to parse the wsdl file?? I searched a lot in google and problem is there are many things to know :) In any case, you help me a lot
I can't really tell you which code to use for parsing WSDL as I don't think I've done that with JS. You're going to have to keep at it. Use your best judgement with the results from Google and good luck. I do everything possible to avoid WSDL in everything I do.
it's okay thank you again and good work
Thanks for the example. I am new to j query and phone gap. In regards to authentication if some html is retrieved before index html or the authenticated session has expired how is the redirection back to the login applied?
Whatever services you write for your app to handle returning data should have a consistent way to report a session time out. Your front end code then needs to handle that.
Thanks! Running your application on my desktop windows 7 + chrome v28.0 I just get a blank page. I added a console.log to the init function which fires but console.log("deviceReady") does not. Any help.
deviceready fires on a device, not on your desktop. If you want to test there with Chrome, please look at the Ripple emulator. http://www.raymondcamden.co...
Ray, do you know if the window.localstorage solution works on all mobile OS's?
I'm not going to answer. Instead, I'm going to be a jerk and tell you what I do when I don't know the answer to that type of question. :) Go to caniuse.com. Here is your specific answer:
http://caniuse.com/#feat=na...
Devices I should say. I want to use this solution but also am curious to know if it will work on all the devices that pg build deploys to.
Ha. Where did you find this site? It's telling me ios, android, and blackberry. Sound about right?
I'm not sure that site fully addresses my question. Unless I don't understand how a native app works.... But this caniuse.com site is telling me about the different browsers that do/don't support localstorage. I'm wondering about a native app. If I use localstorage and then deploy using pg build, will it work as a native app on the different devices? I guess the question here is do native apps use the default web browsers on the device (browsers shown on caniuse.com) or do they use something unrelated?
"Devices I should say. I want to use this solution but also am curious to know if it will work on all the devices that pg build deploys to."
To be clear, caniuse.com won't tell you HTC One X versus iPhone5. It will tell you based on the browser. So you have to know, for example, that your HTC One X is using Android X.Y or so. Hope that makes it clearer.
When you use PG Build, it is using the native browser on the device. So you need to consider that when you check caniuse.com. But just FYI, localstorage is supported 100% for mobile. Totally safe to use. (Ok, maybe not 100%, but practically.)
Lovely. Thank you for clearing that up for me. And now, I proceed using localstorage. Cheers!
Hello Raymond,
Thanks for the example.
I created project and it works.
But I don't understand http://www.coldfusionjedi.c... : where were admin and password saved ?
(Sorry My English, :D)
Are you asking where admin/admin is definied? If so - as I believe I said in the blog entry - the authentication system is fake on the server side. In a real app it would check the values against a database.
Hi,
I cant get this to run on Android or Firefox....
If I upload it as is, I just get the initial pre-login page. However, if I replace the first line of main.js with deviceReady(); I can get the login page to load up but it doesnt respond in anyway.
Putting in a username and password, pressing the submit button just results in the page sitting where it is..
Any ideas?
Thanks.
It sounds like deviceready isn't loading on Android. Can you tell me how you built the file into an APK?
Also - this was not built to work in Firefox. This is a PhoneGap demo.
Hi Raymond. I built it with PhoenGap Build.
I was just trying to see if it would do anything for testing purposes when using firefox. My real issue, is with the Android build. Although Ive not tried other platforms.
When you uploaded to PG Build, did you remove my cordova-1.8.1.js and replace the script src call with
<script src="cordova.js"></script>
so PGB could add the appropriate JS file itself?
I'm having the same issue getting stuck on the white screen with Android. I'm on Android 4.3 on a Galaxy S4. I changed the cordova call as you just recommended, no change. I too am building on build.phonegap.com
So first off - ensure that the deviceready event is firing. You can do that with a simple alert.
Um, I'm not sure if putting it in a subfolder would cause a problem. I can't say more unless you can provide a bit more information about why it is failing.
Hello there, I really liked your tutorial, but I am concerned about security of the localstorage to store sensitive data, what is your opinion in relation to XSS vulnerability and since localstorage is accessible through javascript, theoretically, a link with this could get the values stored:
<a href="javascript:alert(localStorage.getItem('password'))">
I found some good articles about this:
https://blog.whitehatsec.co...
http://michael-coates.blogs...
Thanks
That link would return the local storage value on my own system, so I'm not sure how bad it would be. If you were able to inject more JS you could do a Ajax post to another server, *if* the user browser supported CORS and your server did (or use JSON/P), but at the point you let people inject HTML in your app your failed miserably anyway.
To be clear, I'm not saying use LocalStorage w/o even thinking about this, but I'm not sure how big the attack vector is, especially if you are letting folks write arbitrary code.
BUT - I'll read those links as well. :)
Oh very good tutorial, thanks. On my nexus 7 with android 4 I'm having a issue due to the fact that when killing the app on task manager, it clears automatically the localStorage and I have to put again the username and password, instead in my samsung mini 2 with android 2.3 and in a galaxy tab with android 3 works fine. Maybe the problem is caused by the new android that let the user kill the app???? Is that a way to overcome this issue????
Odd - I can't believe Android would nuke localStorage like that. Well, if there isn't a way around that, you could use WebSQL. It is overkill for 2 values like that. You could also use the filesystem (you could write it out as a JSON packet).
Hi raymond,please in your ajax call(http://www.coldfusionjedi.c... i dont understand the method=login. There is no function login in the javascript. The code works perfectly well on my android emulator. I just want to know what that line is doing. Thank you very much.
What you are seeing there is a ColdFusion thing. CF lets you build a service that can be called via Ajax. The file contains a number of methods (or can contain a number of methods) and a URL parameter is used to tell the server which we want to run.
You can ignore this if you are not a CF person, which I can surmise you are not. The server side code could be done in *any* other language.
Okay.Thank you very much for your response. Am using Php and its just a validation page. <?php
require_once("connection.php");
if(isset($_POST['username'],$_POST['password'])){
$email=$_POST['username']; $password=md5($_POST['password']);
$query=mysql_query("SELECT * FROM users_tbl WHERE Email='".$email."' AND Password='".$password."'");
if(mysql_num_rows($query)>0){
$response=true;
}else{
$response=false;
}
}
?>
i guess it means i can just remove the method completely.
I guess so. I don't use PHP. :)
Thanks alot sir. Really appreciate
Searching on the web I found that phonegap 2.6 have a bug with memory. Then I updated to phonegap 2.7 and the issue related to task manager and localStorage has disappeared. Thanks anyway raymond for replaying istantantly.
Can you help me to use PHP?
I need it
thank for
I don't know PHP.
Hi, thank you Raymond!
Question:
1) Do I need to add any special code in "some.html"? what if this page was accessed before the login page? (is it possible?)
2) What is the best practice to pass parameters from the login page to the other pages so I can tell which user is this?
1) First part doesn't make sense, second part: Remember this isn't a browser, so the user can't bookmark/change the URL. But what if they did? The assumption is that if you are calling your server to request stuff, then your server is going to enforce a secured session. So if they skip the login, they still won't be able to do Bad Stuff. Your HTML post login, like some.html, is just window dressing.
2) Don't pass them - keep them in memory. Remember jQuery Mobile creates a SPA - so you aren't actually leaving the initial page.
Hi,
I am new to mobile apps.. I have to develop a app using phonegap and jquerymobile where on opening the app I have to login(check the username and password with mssql db).. using websevice on success it should navigate to another page.. I don't know how to use webservice ur clear description ll help me.. thank u
You need to look into doing XHR hits with your JavaScript. jQuery makes this pretty easy.
sry I didn't get u..
all I need isto know how to create a webservice(http://www.coldfusionjedi.c... what happens in it..it would be helpful is u explain with screenshots
i use it with m.q-1.4.1, it didnt work for me. i fixed it with:
in deviceReady
from:
$("#loginPage").on("pageinit",function() {
to:
$("#loginPage").on("pageshow",function() {
@SWATHI: I'm not sure what to tell you. If you are asking how to make network requests with JS, I'd urge you to read the jQuery documentation as it makes it easy.
Login with storage it Ok working fine But how to logout...or Clear Such local storage..please make a code on it..Thnaks
You would use the regular LocalStorage API for removing items. Please read the docs here: https://developer.mozilla.o...
Hi
I have implemented the code and i noticed something strange tell me if you can figure out why. This works fine when i test it on a browser, but when I install it on the tab by the cordova cmd line tools it docent seem to work ...
any ideas why this would happen?
by the way thanks for the great tutorial. keep writing...
It could be anything really. Did you try Chrome Remote Debug to see if you can determine the error?
Hi Ray
I think Rituparna's problem is probably the same as mine - the Chrome Debugger is giving me a CF error response from the service - the error is "Element USERNAME is undefined in ARGUMENTS"
That means the username value isn't being passed via Ajax. This line here does that:
$.post("http://www.coldfusionjedi.c...", {username:u,password:p}, function(res) {
If you modified it, then that would be why you get that error.
I haven't modified the code (apart from the call to cordova.js), just copied straight into a new PG 3.5 project
Then in your debugger, look at the network call. Ensure it is being POSTed.
HI,
Its a great tutorial. But there is few queries. In a conventional web application after successful login we are able to maintain a session state to check whether the user has been authenticated between pages. In this tutorial how to do that. Also username and password post through URL , itsn't it risky
Depending on your server, you may not have to do anything. With ColdFusion, for example, cookies are used to 'mark' a session. So when your PhoneGap/Cordova app makes calls, it carries those cookies along with each XHR call. "It just works".
Hi,
Thanks for the reply.. could you suggest me what will be in case of IIS server
Well, it would depend on the app server. If you are using ASP, you need to check their docs. I *believe* sessions w/ cookies as markers is pretty common though.
Thank you Raymond... I would love to follow your blog.. its amazing
Thanks so much Ray! What's the licensing on this code? Are we free to use it however we'd like? Thanks!
Free to use. I ask that if you are a commercial entity, or have a few bucks to spare, you visit my Amazon wishlist. http://www.amazon.com/wishl...
Attached file is down.
Fixed.
Thank you for that super fast reply! Can you also link the serverside files?
It was just one. A ColdFusion component with one remote method. It took in the username/password arguments, checked them, and returned true or false based on the credentials. If you've ever done a CF query before, I assume you know how to do this. :) (And in my demo it wasn't even a query, just a hard coded check.)
I have a problem and can not solve someone help me white screen in version 1.0.0 and displays content in version 2.0.0
http://i.imgur.com/X06IFVV.jpg
log phonegap
200 /socket.io/socket.io.js
200 /css/index.css
200 /main.js
200 /jquery.mobile/jquery.mobile...
200 /jquery.mobile/jquery-1.7.2....
200 /cordova-1.8.1.js
200 /socket.io/?EIO=2&transport=...
200 /socket.io/?EIO=2&transport=...
200 /socket.io/?EIO=2&transport=...
undefined Ripple :: Environment Warming Up (Tea. Earl Gray. Hot.)
200 /config.xml
404 /ripple/user-agent
undefined cordova :: Setting the user agent server side failed.
undefined cordova :: Initialization Finished (Make it so.)
200 /icon.png
200 /
200 /socket.io/socket.io.js
200 /css/index.css
200 /main.js
200 /jquery.mobile/jquery-1.7.2....
200 /jquery.mobile/jquery.mobile...
200 /cordova-1.8.1.js
200 /socket.io/?EIO=2&transport=...
200 /socket.io/?EIO=2&transport=...
undefined cordova :: fired deviceready event!
200 /socket.io/?EIO=2&transport=...
200 /__api__/autoreload
200 /__api__/autoreload
You are using a *very* old version of Cordova. Please update to the latest and try again. Also, Ripple has issues and I do not recommend it anymore. (Although an update is coming out soon.)
ok thank you ...
I have another doubt when I load another page and call the <body onload="changeText ()"> he does not carry it as you after entering the login he no longer accepts
Sorry - I have no idea what you are saying here.
got this error nothing happens not of error but also does not work help me?
Don't use Ripple. Test w/ an emulator/device.