Example of server-based login with PhoneGap
Yesterday I worked on a simple PhoneGap that would demonstrate how to perform a login before using the application. (Ok, technically, you are using the application when you login, but you get my drift.) I worked up a simple demo of this and then extended it to allow for automatic login after you've first successfully authenticated. This could probably be done better, but here's my first draft demo application that will hopefully be useful to others.
I began by creating a new PhoneGap application in Eclipse and included jQuery Mobile. My home page will include my login form and upon successful login I'll simply push the user to the "real" home page. Here's the login screen.
2<html>
3
4<head>
5 <meta name="viewport" content="width=320; user-scalable=no" />
6 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
7 <title>Auth Demo</title>
8 <link rel="stylesheet" href="jquery.mobile/jquery.mobile-1.0rc2.css" type="text/css" charset="utf-8" />
9 <script type="text/javascript" src="js/jquery-1.7.min.js"></script>
10 <script type="text/javascript" charset="utf-8" src="js/phonegap-1.2.0.js"></script>
11 <script src="jquery.mobile/jquery.mobile-1.0rc2.js"></script>
12 <script type="text/javascript" charset="utf-8" src="js/main.js"></script>
13</head>
14
15<body onload="init()">
16
17<div id="loginPage" data-role="page">
18
19 <div data-role="header">
20 <h1>Auth Demo</h1>
21 </div>
22
23 <div data-role="content">
24
25 <form id="loginForm">
26 <div data-role="fieldcontain" class="ui-hide-label">
27 <label for="username">Username:</label>
28 <input type="text" name="username" id="username" value="" placeholder="Username" />
29 </div>
30
31 <div data-role="fieldcontain" class="ui-hide-label">
32 <label for="password">Password:</label>
33 <input type="password" name="password" id="password" value="" placeholder="Password" />
34 </div>
35
36 <input type="submit" value="Login" id="submitButton">
37 </form>
38
39 </div>
40
41 <div data-role="footer">
42 <h4>© Camden Enterprises</h4>
43 </div>
44
45</div>
46
47</body>
48</html>
I assume most of my readers are familiar with jQuery Mobile now so I won't discuss the mechanics of it above. That being said, here's a quick screen shot of how this looks.

Ok, now let's switch over to the code.
2 document.addEventListener("deviceready", deviceReady, true);
3 delete init;
4}
5
6function deviceReady() {
7
8 $("#loginForm").on("submit",function(e) {
9 //disable the button so we can't resubmit while we wait
10 $("#submitButton",this).attr("disabled","disabled");
11 var u = $("#username", this).val();
12 var p = $("#password", this).val();
13 if(u != '' && p!= '') {
14 $.post("http://www.coldfusionjedi.com/demos/2011/nov/10/service.cfc?method=login&returnformat=json", {username:u,password:p}, function(res) {
15 if(res == true) {
16 $.mobile.changePage("some.html");
17 } else {
18 navigator.notification.alert("Your login failed", function() {});
19 }
20 $("#submitButton").removeAttr("disabled");
21 },"json");
22 }
23 return false;
24 });
25
26}
I begin with a deviceready listenener. Technically I'm not actually using any of the phone features for this demo, but in general I think this is a good way to start up your application specific code. The code we care about is in the submit handler for my form. All we need to do when run is do an Ajax request to the server. Remember: The restriction on non-same-domain Ajax requests does not exist in a PhoneGap app. If my authentication returns true, I then use jQuery Mobile's changePage feature to shift them over to the page. (Note - my remote service is just returning a boolean. It would probably be more useful to return some data about the user. Their username for example. You get the idea I think.)
So this works - but let's ramp it up a bit. What I'd like to do is ensure that once you login with the application, you don't have to do it again. This could be optional, but for now I've made it just happen by default. I'm going to modify my code to...
- Store my username and password in local storage upon successful login
- When the application starts, check for these values, and if they exist, prepopulate the form and submit it automatically
So, my first change was just to store the values. HTML5 local storage to the rescue...
2 var form = $("#loginForm");
3 //disable the button so we can't resubmit while we wait
4 $("#submitButton",form).attr("disabled","disabled");
5 var u = $("#username", form).val();
6 var p = $("#password", form).val();
7 console.log("click");
8 if(u != '' && p!= '') {
9 $.post("http://www.coldfusionjedi.com/demos/2011/nov/10/service.cfc?method=login&returnformat=json", {username:u,password:p}, function(res) {
10 if(res == true) {
11 //store
12 window.localStorage["username"] = u;
13 window.localStorage["password"] = p;
14 $.mobile.changePage("some.html");
15 } else {
16 navigator.notification.alert("Your login failed", function() {});
17 }
18 $("#submitButton").removeAttr("disabled");
19 },"json");
20 }
21 return false;
22}
Handling the check on startup was a bit more difficult. I'm still trying to wrap my best way to handle events when working with PhoneGap and jQuery Mobile together. For my case, I wanted to populate the form fields. Therefore it made sense to do this both after the page is loaded and the page is "decorated" by jQuery Mobile. For that I needed the pageinit function. However, it is tricky to get the listener in for this. If I add it to my init function, the one called by body/onload, it's too late. A bit of Googling seems to suggest the best place is in the DOM itself. Here's how my home page looks now.
2<html>
3
4<head>
5 <meta name="viewport" content="width=320; user-scalable=no" />
6 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
7 <title>Auth Demo</title>
8 <link rel="stylesheet" href="jquery.mobile/jquery.mobile-1.0rc2.css" type="text/css" charset="utf-8" />
9 <script type="text/javascript" src="js/jquery-1.7.min.js"></script>
10 <script type="text/javascript" charset="utf-8" src="js/phonegap-1.2.0.js"></script>
11 <script src="jquery.mobile/jquery.mobile-1.0rc2.js"></script>
12 <script type="text/javascript" charset="utf-8" src="js/main.js"></script>
13</head>
14
15<body onload="init()">
16
17<div id="loginPage" data-role="page">
18
19 <div data-role="header">
20 <h1>Auth Demo</h1>
21 </div>
22
23 <div data-role="content">
24
25 <form id="loginForm">
26 <div data-role="fieldcontain" class="ui-hide-label">
27 <label for="username">Username:</label>
28 <input type="text" name="username" id="username" value="" placeholder="Username" />
29 </div>
30
31 <div data-role="fieldcontain" class="ui-hide-label">
32 <label for="password">Password:</label>
33 <input type="password" name="password" id="password" value="" placeholder="Password" />
34 </div>
35
36 <input type="submit" value="Login" id="submitButton">
37 </form>
38
39 </div>
40
41 <div data-role="footer">
42 <h4>© Camden Enterprises</h4>
43 </div>
44
45</div>
46
47<script>
48 $("#loginPage").live("pageinit", function(e) {
49 checkPreAuth();
50 });
51</script>
52
53</body>
54</html>
And here is my re-engineered main.js file:
2 document.addEventListener("deviceready", deviceReady, true);
3 delete init;
4}
5
6
7function checkPreAuth() {
8 var form = $("#loginForm");
9 if(window.localStorage["username"] != undefined && window.localStorage["password"] != undefined) {
10 $("#username", form).val(window.localStorage["username"]);
11 $("#password", form).val(window.localStorage["password"]);
12 handleLogin();
13 }
14}
15
16function handleLogin() {
17 var form = $("#loginForm");
18 //disable the button so we can't resubmit while we wait
19 $("#submitButton",form).attr("disabled","disabled");
20 var u = $("#username", form).val();
21 var p = $("#password", form).val();
22 console.log("click");
23 if(u != '' && p!= '') {
24 $.post("http://www.coldfusionjedi.com/demos/2011/nov/10/service.cfc?method=login&returnformat=json", {username:u,password:p}, function(res) {
25 if(res == true) {
26 //store
27 window.localStorage["username"] = u;
28 window.localStorage["password"] = p;
29 $.mobile.changePage("some.html");
30 } else {
31 navigator.notification.alert("Your login failed", function() {});
32 }
33 $("#submitButton").removeAttr("disabled");
34 },"json");
35 } else {
36 //Thanks Igor!
37 navigator.notification.alert("You must enter a username and password", function() {});
38 $("#submitButton").removeAttr("disabled");
39 }
40 return false;
41}
42
43function deviceReady() {
44
45 $("#loginForm").on("submit",handleLogin);
46
47}
The code you care about here is checkPreAuth. It basically looks for the existing values, and if there, sets them up in the form and automatically runs the submission. In my tests, this works well. You would probably want to configure the login failed message to handle a case where an auto-login failed. Another issue - and one I do not have a good feel for - is how secure the local storage data will be on the device. Use with caution!

My original confusion was that I hadn't used that syntax before - $("#username", form) - which looks like it would have its advantages if you were using class names rather than IDs to target your username and password elements, which you may very well do in some situations.
var form = $("#loginForm");
var u = form.find("#username");
var $form = $("#loginForm"),
$username = $("#username"),
$password = $("#password"),
$submitButton = $("#submitButton");
Also, csslint would complain about multiple id's in selectors. Ex. #loginForm #username.
You can use my Field Plug-in to make it easier to get form field values. There's a formHash() method which will return all the fields in a form as a hash/struct. By default it also hashes multiple values as a comma separated values--essentially emulating the way CF would see the data if the form was submitted to the server.
So, instead of having to grab the value for each field, you can just grab all the fields in a form.
http://www.pengoworks.com/workshop/jquery/field/fi...
There's also a ton of other stuff you can do--including passing in a hash/struct to formHash() to fill in a form. There's many other helpers as well.
@Dan: I've not had trouble w/ forms like that before... except in terms of checkboxes/radio fields, where I've had to do my own logic. Next time I need something like that I'll try to remember it. But you have to agree that would be a bit overkill for just grabbing 2 fields. :)
if(u != '' && p!= '') {
...
$("#submitButton").removeAttr("disabled");
}else{
navigator.notification.alert("You must enter a username and password", function() {});
$("#submitButton").removeAttr("disabled");
}
return false;
But how many projects do you work on where you'll only ever need to grab the value for 2 form fields? :)
Seriously though, I mentioned it more because most forms become more complex and working in logic for non-text fields can become much more complex, so there's a huge advantage in using code that will automate everything for you.
* Being honest. ;)
Thanks for the demo! I've been retooling it a little and have 1 quick question. After the user has been authenticated and you come back to the application, is there anyway to supress the android prompt of Confirm: " Do you want the browser to remember the password" - Not Now, OK, Never"
http://stackoverflow.com/questions/32369/disable-b...
thanks for sharing this.
can you upload a sample of this?
or a sample how the json should look like?
V.
.........
if(u != '' && p!= '') {
$.post("http://www.sample.com/login.php", {username:u,password:p}, function(res) {
if(res == true) {
//store
...........
http://www.coldfusionjedi.com/demos/2011/nov/10/se...
component {
remote boolean function login(string username, string password) {
if(arguments.username == "admin" && arguments.password == "password") {
session.loggedin = true;
return true;
} else return false;
}
remote numeric function randomNumber() {
return randRange(1,10000);
}
}
I tried but could not be succeed :(.
There is no notification nothing. Only page changes and goes to same page.
alert("I'm auto logging in.");
If that runs, it means you logged in once and it remembered it.
But handleLogin () is not fired.
? 've changed the code and test with Firefox, Chrome before build for Phone but alert is not shown :( :
function handleLogin() {
alert("Tested HANDLE Login");
var form = $("#loginForm");
//disable the button so we can't resubmit while we wait
$("#submitButton",form).attr("disabled","disabled");
var u = $("#username", form).val();
Now it works fine :) Good job Raymond. Also which modification can we do to see Loading animation while waiting json response ?
Before the post, do
$("#status").html("Stand back, doing stuff...");
In the post result handler, clear it:
$("#status").html("");
Obviously you can change the message to your liking.
also some friends have asked your service file.
Here is my .PHP service file that turns boolean value with JSON header
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json');
$u = strip_tags($_POST['username']);
$p = strip_tags($_POST['password']);
$con = mysql_connect("localhost","db_user","db_pass");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db ("your_database_name");
$sorgu="SELECT * from db_users WHERE username ='$u' AND password ='$p' ";
$ssql_select = mysql_query($sorgu)or die(mysql_error());
if(mysql_num_rows($ssql_select)>0)
{
echo 'true';
}else{
echo 'false';
}
mysql_close($con);
Now i have another question. We are making a post with the code and check the response data.
if(res == true) {
//store
window.localStorage["username"] = u;
window.localStorage["password"] = p;
Also i want to store USER_ID value from the users table. But how can we read it from JSON. Our response code is just true or false. Can we change it like true:12311?
12311 is USERID return value from service
(Note - the code below isn't exactly real JSON, it's just a representation. You would be responsible to encode it in PHP.)
On failure:
{ result="false" }
On success:
{ result="true", userid=4} (4 being the userid and dynamic)
{"result":"true","memberId":null}
How should i change this part of code:
$.post("http://adasdas.com/loginservice.php", {username:u,password:p}, function(res) {
if(res == true) {
//store
window.localStorage["username"] = u;
window.localStorage["password"] = p;
Thanks
if(res.result == 'true') {
//store
window.localStorage["username"] = u;
window.localStorage["password"] = p;
;)
I like your blog ! Just found it when I was searching for a server side based android login (and wauw this was acactly what I was searching for!) in a few minutes, with some tweaks I got it working on my android device. The part I changed was the local storage:
function checkPreAuth() {
var form = $("#loginForm");
if(typeof(window.localStorage) != 'undefined'){ //HERE IS THE CRASH!
if(window.localStorage["username"] != undefined && window.localStorage["password"] != undefined) {
$("#username", form).val(window.localStorage["username"]);
$("#password", form).val(window.localStorage["password"]);
handleLogin();
}
else{
//alert("ERROR!");
}
}
}
It checks if any then exites then it will create and and go to the handLogin, if succeded. els it trows an error (wich is commented out now)
So after this (GREAT!) script was working in android I tought lets " copy past " it to my IOS device (I'm developing it for the both of them)
But this isn't going for smooth.... it crashes on that part.... I changed it back what you had but this also didn't work. I can't seem to find the issue, meby you can? (its commented where it crashes, without an error....)
Hope you can help me and I'm looking forward to the other blog posts and your help!
Thanks
Next time I blog on this and share the code, I'll remind folks to replace the phonegap.js file.
neither the less, great post and thanks for every thing!
Well anyhow, it's working like a charm now so, so far 1.7.x isn't giving me any problems ;)
If anyone on this thread knows PHP, I'd gladly accept a comment pointing to a pastebin demo. I'll even edit the post to put a note at the end.
And yes - it works the same. So AJAX calls can pick up, and reuse cookies, so cookie-based sessions "just work".
Thanks for your reply. If I store values in local storage and close the app, will those local storage values go away? Or do I have to manually remove them? if so, how?
localStorage.removeItem("somekey");
In the last part of your article you mention about the security of local storage. Here is a link to clear it up.
http://stackoverflow.com/questions/3718349/html5-l...
This would suggest putting the variables in local storage not a good idea if they are sensitive, like a password. This also assumes that the person has access to the computer. Now since this is meant to be on a device like a phone or tablet then this may not be a concern.
I can't make this work on iOs, despite the comment from Niels. Any idea ? What does he mean by "Just used the ios phonegap js file" ?
Thanks,
Quentin
One of the things that PG team is working on is moving to ONE file for all the platforms (one JS file).
Thanks for the swift answer! Well that's ok then, I have a phonegap-1.2.0.js file in my project, so that's not it.
I can't find what's going wrong, no errors when running it in the simulator but the button doesn't do anything.
Here's my code, if you -or someone- has some time to waste to quickly check on it : http://pastie.org/3735130
Thanks for the advice. I had a whitelist rejection from the URL which I fixed by adding a * in the array, but now I have this :
2012-04-05 23:17:22.741 LCDemo[5819:13403] Device initialization: DeviceInfo = {"name":"iPhone Simulator","uuid":"8BA3CEC6-27B2-5573-8C42-7A2DB234EF32","platform":"iPhone Simulator","gap":"1.2.0","version":"5.0","connection":{"type":"wifi"}};
2012-04-05 23:17:22.759 LCDemo[5819:13403] *** WebKit discarded an uncaught exception in the webView:decidePolicyForNavigationAction:request:frame:decisionListener: delegate: <NSRangeException> *** -[JKArray objectAtIndex:]: index (1) beyond bounds (1)
Any idea ?
Thanks,
Quentin
http://www.raymondcamden.com/index.cfm/2011/11/10/...
LocalStorage
WebSQL (Database)
Files
So yes.
Note though that CHrome cannot make remote Ajax calls by default. You can either make use of CORS, or the command line arg that lets you do it. (I can't remember it offhand.)
And it's not like they can't share any code at all of course. If your desktop web site is using an MVC architecture, then your mobile app can make use of the same model.
I tried to modify this a bit in order to retrieve some informations from a database during the login. The web service is built in PHP and is returning an array in JSON but the login is not working anymore. Would you -or anyone- have an idea why ?
$.post("http://app.we3r.net/login2.php", {username:u,password:p}, function(data) {
if(data.success == true)
{
window.localStorage["username"] = usernameToSave;
window.localStorage["userId"] = useridToSave;
$.mobile.hidePageLoadingMsg();
$.mobile.changePage("dashboard.html");
}
else
{
$.mobile.hidePageLoadingMsg();
navigator.notification.alert("Your login failed", function() {});
}
}, "json");
one question: why do you use deviceready?
May I do something like:
$(document).on('pageinit', '#loginPage', function(event) {
handleLogin();
});
will they be the same?
thanks
Now - as for pageinit - that's another matter. That's jQuery Mobile specific.
http://samcroft.co.uk/2011/updated-loading-data-in...
i am new to phonegap.
is your tutorial applicable for blackberry OS 5.0 and higher? where can i find "phonegap-1.2.0.js" for blackberry ?
thanks
Thank you for your help. So, i just go with
o> jquery.mobile-1.1.0.js
o> cordova.js
o> jquery-1.7.2.min.js
i load it to blackberry simulator 9930. When i open the application, it went loading and then crash. It happened whenever i load the "jquery-1.7.min.js". Do you have any idea regarding to this ? and where can i get the error message for debugging ?
Sorry for bothering you. I really appreciate your help. thanks
I found something in web that if Phonegap want to communicate with external data, we have to use ajax to connect to server for cross domain.
But I read here that you don't need to handle it.
Is it true??
For instance http://www.google.com
window.location.href="http://website.com?username="+u+"&pa...;
I have another question. In your example your program saves the username and password to the local storage. What if someone changes their password on the server side? I have tried an example and it just sits at the login form. When I push the button to login, I don't get a message saying login failed. If I go ahead and type in the new password, it will redirect correctly. Is there anyway to pop up an alert saying that login failed or check password? Thank you!
My error is this:
Uncaught TypeError: Cannot call method 'alert' of undefined at file://android_asset/www/index.html
That is the line of code that should pop up the notification alert that shows "Your login has failed".
The idea is that someone might change their password on the server but forget to change on the device. They will be stuck at the form that won't go anywhere and not have any indicator saying hey your login failed. I'm trying to make it user friendly :).
BTW thank you for your assistance.
It is possible that we have a case here where checkPreAuth fires BEFORE deviceReady does which means... bad news.
If you want a quick fix, change it to just an alert(). Will be a bit ugly, but will work.
The longer fix is me doing an update to the entry that handles the 2 events correctly so one doesn't fire before the other. (Err, well, that one fires whenever but it correctly waits.)
http://www.raymondcamden.com/index.cfm/2012/6/21/U...
I tried the example which you have posted, but I am facing a problem. The control is not going into the " $.post("http://www.coldfusionjedi.com/demos/2011/nov/10/se...;, {username:u,password:p}, function(res) { .... } " function.
What can be the problem?
$("#loginForm").on("submit",handleLogin);
You can add a console.log before it. You can add a console.log as the first line in handleLogin as well.
Thanks
thats great work
can u mail complete source code to my mail pls
a) Remove the values from localStorage (easy enough)
b) Call the remote CFC to clear the session on the server side (again, easy enough in ColdFusion, should be easy in any language).
@Ashok: What complete source do you want? It is all there except the login routine on the server which really wasn't critical to the blog post.
$.post("http://www.coldfusionjedi.com/demos/2011/nov/10/se...;, {username:u,password:p}, function(res) {
I build the code using Phonegap BUILD and used a valid Signing key also. But the post doesn't get executed , my code can be seen at http://stackoverflow.com/questions/13115866/blackb...
I tried the example which you have posted, can you give me cfc code ? thnxx before..
http://www.raymondcamden.com/index.cfm/2011/11/10/...
I'm behzad from Iran.
your site really great.
how can i replace PHP instead service.cfc?
(i don't know what is the --> .cfc file !)
i want to get stuff from MYSQL Database by PHP
and past it to the phone.
thank you sir.
Running a query.
Turning the result into an array.
Converting the array into JSON.
That should be very possibly in PHP. I don't know PHP though so I can't help you with that.
https://sso.sp.edu.sg/login/main.jsp?TAM_OP=login&...
I won't be able to edit and they did not do anything on the code related to JSON. Although i tried to login but i failed instead probably because it needed json.
Please help! thanks
function init() {
document.addEventListener("deviceready", deviceReady, true);
delete init;
}
function deviceReady() {
$("#loginForm").on("submit",function(e) {
//disable the button so we can't resubmit while we wait
$("#submitButton",this).attr("disabled","disabled");
var u = $("#username", this).val();
var p = $("#password", this).val();
if(u != '' && p!= '') {
$.post("http://www.coldfusionjedi.com/demos/2011/nov/10/se...;, {username:u,password:p}, function(res) {
if(res == true) {
$.mobile.changePage("some.html");
} else {
navigator.notification.alert("Your login failed", function() {});
}
$("#submitButton").removeAttr("disabled");
},"json");
}
return false;
});
}
1) Listen for the back button. Example from docs: http://docs.phonegap.com/en/2.3.0/cordova_events_e...
2) Depending on your UI framework, you can listen for a pageshow type event (that's jQuery Mobile specific) and say (in pseudo-code), "If I'm loading the login page and the dude is logged in, push them to the home page."
One thing I've had a continual issue with is page transitioning in jQuery Mobile. It's so confusing and I can't quite wrap my mind around the concept. Does it actually visit the new page? Do you need all of the script and css inclusions on each page? Do you just use multiple divs in ONE page rather than loading a whole new page? It's just very confusing!
It is absolutely NOT required. In general, I try to make my examples as simple as possible and not mix stuff in. In this case, JQM just made some things easier. As a tech blogger, it is a hard balance. :)
"Does it actually visit the new page?" Define visit. JQM loads in pages via Ajax and puts them in the DOM. Is that a visit? Probably not.
"Do you need all of the script and css inclusions on each page?" See the above. You can get away with doing it just once as the other pages are loaded in via Ajax and only the 'page' content is displayed.
" Do you just use multiple divs in ONE page rather than loading a whole new page?" Normally you have one div per HTML file just to make things simpler.
" It's just very confusing!" I apologize. I'd suggest the jQuery Mobile web site for more info, or, just think of the basic concepts of this article at a higher level. Don't get hung up on the UI aspects of it.
Issue is, I am not able to call $.post call to http://localhost:55209/Account/Login which is dev server.
Is it due to cross domain issue?
I was thing about whitelist but according to phoneGap APi "Domain whitelisting is unsupported on Windows Phone. By default, all domains are accessible."
What could be the issue? Please help!
I was able to fix the issue. I just added $.support.cors = true; in my .js file and after that code works like a charm on the phone emulator.
Thanks for helping me by this post. :)
Am I reading the above comments correctly in that to deploy this to an iOS device all you would need to do would be change the js file included? The PhoneGap docs imply you need to develop on a Mac, but I'm assuming that's for native development rather than using JQMobile and PhoneGap?
Thanks in advance.
"The PhoneGap docs imply you need to develop on a Mac, but I'm assuming that's for native development rather than using JQMobile and PhoneGap?"
It depends. You can use PG Build to generate iOS apps based on HTML you write on a PC. If you don't want to use PG Build, you have to use a Mac.