Business App: Login and Password Encryption Sample

I’m in the process of building a business app, but I’m relatively new to corona, and most of my work before has been done with game apps.

I was hoping someone could help me with account creation (to be stored in a remote database) and then login to show a different scene, I have a good idea of how the logic of it works but I don’t know how to implement any of it with actual code.

Any insight, tips, or code samples would be greatly appreciated!

We recently updated the Business App sample (https://github.com/coronalabs/business-app-sample) to include form entry and reading and writing from a local SQLite database that might get you started, however I specifically did not use login’s as an example because I felt the app was complex enough.

For inputting the user information, you will new native.newTextField(). There is an option .isSecure that turns a field into a password field. This is all documented under those APIs. That’s the easy part.

From there you would use network.request() to make an HTTP request to a server that you have access to. On that server you would likely have a script written in PHP or ASP or some other web scripting language. That script would then take the values, process then and the script would talk to whatever database you’re using (MySQL, etc.), compare the username and password and if they match, return a value to your app that network.request() would present to you in it’s call back listener function. Your app can then react to the results accordingly.

That’s a basic outline of the data flow. Now lets switch up and talk about the password itself.

Your database should never allow passwords to be read back to clear text. Never store the password in clear text either. You should use some form of one way encryption. MD5 hashes uses to be the preferred way, but those have gotten to easy to hack. SHA 256 bit seems to be what most people favor today. Our OpenSSL plugin can do SHA 256.  But that’s not enough to. You need to also “salt” your passwords. Basically you need to add some additional data to the password beyond what the person typed. You can look up various salt algorithms. But the idea is that if  the user gives you the password “password123”, you would change it to: ABCD1235password123ZYXW9876 where the added text is something you control and probably should be something different for each user. You’ve made the password more complex but more importantly if it gets decrypted, the user won’t know what part they have to type in.  This is a very general description of salting.

Then when your script runs, you might do an SQL query like:

$query = "SELECT id FROM usertable WHERE username = " . $username . " AND password = " . $password;

Where the password is the encrypted + salted password. When you execute the query, it will either find the person or not. Then return something your app recognizes as success or fail.

First off I want to thank you for your help! Your response clears a lot of things up for me, but unfortunately also raises a few new questions.

I’ve never used the network.request() command before, so I’m not really sure how it works. Would i be passing something in the parameters, say for example, the php script that would be stored in the same directory as the database?

The network.request() API is fairly well documented: https://docs.coronalabs.com/api/library/network/request.html

You are most likely going to use either an HTTP GET or POST request. GET requests, any thing you need to pass to the server is done as part of the URL:  http://mysite.com/myscript.php?key1=value1&key2=value2&key3=value3

You probably have used URLs like this. The value’s have to be URL encoded if they have something other than letters and numbers in them. For instance if you want to pass the string “Hello World” as the value for key3 above it would change the URL to:

http://mysite.com/myscript.php?key1=value1&key2=value2&key3=Hello%20World

Note how the space was encoded to its hex value and prepended with a %. There are various URL encoding functions you can google for.

Then for a basic network request call looks like:

URL = "http://mysite.com/myscript.php?key1=value1&key2=value2&key3=Hello%20World"

network.request( URL, “GET”, requestListener )

In this case you also need to provide a function (in the example above named requestListener) that handles the return from the web server. The documentation should explain all of that.

HTTP POST requests don’t pass data on the URL like GET instead you create a table named “body” and in the body you put your & delimited key/value pairs. Why the difference? GET requests are limited to like 200 characters. If you need to send more data, POST is the way to go. Then there are the semantics of it. GET is generally used to “fetch” data. POST is generally used to send data. But if you’re in control of  your own script, it’s perfectly fine to use GET to send small amounts of data.

Your script can return anything but you have to parse it if its more than a simple value. It’s best to have your script output JSON. If you’re using PHP, and you store the information in an associative array (pretty much the same thing as a Lua table). you can use the PHP json_encode() function to handle making the JSON data:

$result = json\_encode( $myArray ); echo( $result );

Back in your app, inside that requestListener function you will get a table named "event (if you follow our examples) and a member of that is event.response. This will hold the output of your online script. If it’s JSON data then you can do:

local myTable = json.decode( event.response )

Now myTable will magically contain everything in the PHP $myArray with the same names and same table structure.

Rob

sorry about that haha I originally tried using <code> tags the first time i posted it. Here it is.

this is my php script used to register a new account (no encryption, just testing purposes):

\<?php require 'connection.php'; if (!$conn) {die( "Connection failed: " . mysqli\_connect\_error());} echo 'hi'; $email = mysqli\_real\_escape\_string($conn, $\_POST['email']); $password = mysqli\_real\_escape\_string($conn, $\_POST['password']); $mobile = mysqli\_real\_escape\_string($conn, $\_POST['mobile']); $gender = mysqli\_real\_escape\_string($conn, $\_POST['gender']); $query = mysqli\_prepare($conn, "INSERT INTO USER (email, password, mobile, gender) VALUES(?,?,?,?)"); mysqli\_stmt\_bind\_param($query, 'ssss', $email, $password, $mobile); mysqli\_stmt\_execute($query); mysqli\_stmt\_close($query); msqli\_close($conn); ?\>

which seems like it would work fine, but I’m still stuck in the network.request() part.

From the the documentation on corona labs, I figured I’d use the last example that uploads a text file, using POST.

It looks like this:

local function networkListener( event ) &nbsp;&nbsp;&nbsp; if ( event.isError ) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print( "Network error: ", event.response ) &nbsp;&nbsp;&nbsp; else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print ( "Upload complete!" ) &nbsp;&nbsp;&nbsp; end end local headers = {} headers["Content-Type"] = "application/json" headers["X-API-Key"] = "13b6ac91a2" local params = {} params.headers = headers -- Tell network.request() to get the request body from a file: params.body = { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --is this section where I would get what i want to post data from my textfields?, say for example: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --email = emailTextField.text &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --and if so, how would i deal with the scope problem of my textfields being inside the scene:show function? &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } --this is my register button local function continueEvent(event) &nbsp;&nbsp; &nbsp;local phase = event.phase &nbsp;&nbsp; &nbsp;if phase == "ended" then &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if emailField.text == "" or passwordField.text == "" or mobileField.text == "" then &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;alert = native.showAlert( "title", "All of the fields must be filled!", {"OK"}, onComplete ) &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;else &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;network.request( "http://cliptest.net16.net/registration\_script.php", "POST", networkListener, params ) &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;end &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return true &nbsp;&nbsp; &nbsp;end end

Can you re-edit this and put your code in the proper code tags. Use the <> button and paste the code into the window (you can highlight the code already typed in and click the <> button too.

edited!

Let’s look at the Corona code:

local function urlencode(str) &nbsp; if (str) then &nbsp;&nbsp;&nbsp; str = string.gsub (str, "\n", "\r\n") &nbsp;&nbsp;&nbsp; str = string.gsub (str, "([^%w])", &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function (c) return string.format ("%%%02X", string.byte(c)) end) &nbsp;&nbsp;&nbsp; str = string.gsub (str, " ", "+") &nbsp; end &nbsp; return str&nbsp;&nbsp; &nbsp; end local function networkListener( event ) if ( event.isError ) then print( "Network error: ", event.response ) else print ( "Upload complete!", event.response ) end end local headers = {} headers["Content-Type"] = "application/json" headers["X-API-Key"] = "13b6ac91a2" -- I don't see where you're using this in your PHP script, but it's not an issue to leave it local params = {} params.headers = headers --this is my register button local function continueEvent(event) local phase = event.phase if phase == "ended" then if emailField.text == "" or passwordField.text == "" or mobileField.text == "" then alert = native.showAlert( "title", "All of the fields must be filled!", {"OK"}, onComplete ) else params.body = "email=" .. urlencode(emailField.text) .. "&password=" .. urlencode(passwordField.text) .. "&mobile=" .. urlencode(mobileField.text) network.request( "http://cliptest.net16.net/registration\_script.php", "POST", networkListener, params ) end return true end end

Look at the differences between what I did and what you did, on how I constructed the data for the POST body.

Now, some PHP servers have issues giving you a blank $_POST[] array. You can Google for solutions on that. But your PHP code has a couple of things going on that could crash it too.

\<?php require 'connection.php'; if (!$conn) {die( "Connection failed: " . mysqli\_connect\_error());} echo 'hi'; $email = mysqli\_real\_escape\_string($conn, $\_POST['email']); $password = mysqli\_real\_escape\_string($conn, $\_POST['password']); $mobile = mysqli\_real\_escape\_string($conn, $\_POST['mobile']); $gender = mysqli\_real\_escape\_string($conn, $\_POST['gender']); $query = mysqli\_prepare($conn, "INSERT INTO USER (email, password, mobile, gender) VALUES(?,?,?,?)"); mysqli\_stmt\_bind\_param($query, 'ssss', $email, $password, $mobile); mysqli\_stmt\_execute($query); mysqli\_stmt\_close($query); msqli\_close($conn); ?\>

The line:  echo ‘hi’;  will result is your event.response being the string “hi”.

These two lines:

$query = mysqli_prepare($conn, “INSERT INTO USER (email, password, mobile, gender) VALUES(?,?,?,?)”);
mysqli_stmt_bind_param($query, ‘ssss’, $email, $password, $mobile);

It’s expecting four parameters in the order presented. You appear to be only using three. You never provide gender to this and I expect that mysqli_stmt_bind_param() is failing since it needs a 4th variable.

Finally other than saying “hi” back to your Lua code you never output anything of use. You should output something:

$results = array('success'&nbsp;=\>&nbsp;1); echo( json\_encode( $results ) );

Or something like that.

Thanks for the help Rob! I made the changes to the php script and added the urlencode function to my corona code, but the json table always returns nil, and of course, the database isn’t getting updated.

I added this to the end of my php script (before the query close and connection close):

$user = array(); while(mysqli\_stmt\_fetch($query)){ $user[email] = $email; $user[password] = $password; $user[mobile] = $mobile; $user[gender] = $gender; } echo json\_encode($user);

and my corona code looks like this now (after the corrections you made):

local function urlencode(str) if (str) then str = string.gsub (str, "\n", "\r\n") str = string.gsub (str, "([^%w])", function (c) return string.format ("%%%02X", string.byte(c)) end) str = string.gsub (str, " ", "+") end return str end local function networkListener( event ) if ( event.isError ) then print( "Network error: ", event.response ) else print ( "Upload complete!" ) local myTable = json.decode( event.response ) print (myTable) end end local headers = {} headers["Content-Type"] = "application/json" -- Tell network.request() to get the request body from a file: local params = {} params.headers = headers local function continueEvent(event) local phase = event.phase if phase == "ended" then if emailField.text == "" or passwordField.text == "" or mobileField.text == "" then alert = native.showAlert( "CLIP", "All of the fields must be filled!", {"OK"}, onComplete ) else if (emailField.text:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?")) then params.body = "email=" .. urlencode(emailField.text) .. "&password=" .. urlencode(passwordField.text) .. "&mobile=" .. urlencode(mobileField.text) network.request( "http://cliptest.net16.net/registration\_script.php", "POST", networkListener, params ) else alert = native.showAlert( "CLIP", "Invalid email", {"OK"}, onComplete ) end return true end end end

And in the console, the output I get is:

15:04:35.506  Upload complete!
15:04:35.506  nil
 

You may have errors in your PHP script. What happens if you try and call it from a browser (you may have to setup a small form to post the data or switch to GET and do it from the URL?

Rob

Yeah good idea! I thought about doing that too, I’ll set up an html form real quick and see what happens.

Fixed! I was closing the connection too early… Silly me. New problem arose though. The script seems to be working with the app and test form. When the script is called from the app, it creates a new row in the USER table of my database, but all the columns/attributes are empty! Whereas while calling the script from the test html form, it works fine. I now get this as output in the console:

16:24:18.571  Upload complete!
16:24:18.571  table: 0B209878
 

Fixed. I don’t know why having

local headers = {} headers["Content-Type"] = "application/json"

was affecting it… I commented it out and it now works! The data from the textfields are now successfully being updated into the database.

To a new topic, is there a way to show some sort of loading progress (like the little generic wheel) while the network.request() is being process?

This probably should be a new topic. But we have three options. There is a native.setActivtyIndicator(). You would call it right before you call network.request() and then in your call back function, call it again to cancel it. Pass it true if you want it, false to cancel. See: https://docs.coronalabs.com/api/library/native/setActivityIndicator.html

Since this native, it’s going to shutdown the whole screen until it completes.

There is a widget.newSpinner() that gives you customization abilities and it’s OpenGL based which means it doesn’t have to sit on top and take over the UI. See: https://docs.coronalabs.com/api/library/widget/newSpinner.html

And also in the widget library, is a widget.newProgressView(). Again this is an OpenGL based item which creates display objects. This would be useful if you’re downloading a file. network.request() does support download progress and you can catch these events in your network.request()'s event listener and update it as you get progress events.

But for a login thing, a progress bar isn’t a good choice for a UI option. Depending on how fast the login takes, the native.setActivityIndicator() could create a flash if the login is pretty quick. If you know its going to take a couple of seconds then it would be fine.

Rob

Thanks for the tips, I’ll be sticking for the activityIndicator for now.

Back to things related to logging in and account creation, is there a way for the app to recognize a session was already started? Like a “remember me” type deal, where if you’ve logged in before, it takes you straight to the main screen instead of the log in screen? I was thinking maybe a local database or something of the sort, but I’m unsure as to what the right way to approach this would be.

Anyhow, I’d like to thank you again and let you know how much I appreciate your insight! I was honestly considering switching to a new platform and relearning a whole new language, not because of the lack of capability of corona, but because of how little help I could find (sample code, forum posts) about the problems I was having, compared to other platforms where there is an abundance of tutorials and samples out there for pretty much everything really, as all these things have already been done before in said platforms. If it weren’t for your help, I’d probably be learning java and working with android studio (not xcode because I don’t own a mac) right now! So again, thank you very much!

As far as keeping track of if the user has already logged in, it’s a matter of you saving a file if they successfully logged in. Then on app start (and potentially app resume if you expire the login after a point), read that saved file and see if the person is logged in or not. This is a great way to also keep track of the first time someone ran the app in case  your initializing local databases.

Personally I have a settings table that I save my settings in:

local settings = {}

settings.firstRun = true

settings.loggedIn = false

Then I use the code and info in this tutorial to load and save the table:  https://coronalabs.com/blog/2014/10/14/tutorial-saving-and-loading-lua-tables-with-json/

Rob

Thanks Rob! Your tutorial was really easy to follow and implement! Regarding account creation, do you have any idea how companies/apps create an “account verification” through email or text message, where they send you a code and you are required to provide the code during the account creation process in order to verify your account?

And about password encryption, I’m a little confused about the openssl plugin. Would you suggest the encryption to be done in the app, before sending it over to the web server, or with a php script once the data has been received from the app?

Account verification emails would be generated by your server and sent to the person. In the email you would have link to a script on your webserver that has some code attached to the URL as a GET parameter:

https://yourserver.com/verifyaccount.php?verificationcode=SomebiglongBase64encodedhardToTypebyhandstring

Of course you would later decode that base64 encoded string and compare that value to a value  you’ve stored in your database for that user. If they match, you’ve got a good verification. Your mobile app isn’t involved in this part.

Password encryption, if you’re connecting to your script on an https: server, you wouldn’t necessarily need to encrypt it and your server script would do the one way encryption and compare with the already encrypted value in the database (checking a longing) or do the initial write with the one way encrypted password.

However, if you’re not going over https: and using http:, you need to encrypt it in your app. It doesn’t hurt even going over https that you encrypt it. On initial create, you could just store the value you received, but it might make more sense to apply your salt string and re-encrypt it.

Rob