Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Business App: Login and Password Encryption Sample
Started by yanko_l10 Mar 09 2016 08:03 AM

30 replies to this topic
business app login encryption sample remote database
[TOPIC CONTROLS]
Page 1 of 2 1 2
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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!

 



[TOPIC: post.html]
#2

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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.



[TOPIC: post.html]
#3

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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?



[TOPIC: post.html]
#4

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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



[TOPIC: post.html]
#5

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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 )

    if ( event.isError ) then
        print( "Network error: ", event.response )
    else
        print ( "Upload complete!" )
    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 = {
       --is this section where I would get what i want to post data from my textfields?, say for example:
       --email = emailTextField.text 
       --and if so, how would i deal with the scope problem of my textfields being inside the scene:show function?
      
}

--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
            network.request( "http://cliptest.net16.net/registration_script.php", "POST", networkListener, params )
        end
        return true
    end
end


[TOPIC: post.html]
#6

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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.



[TOPIC: post.html]
#7

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

[TOPIC: post.html]
#8

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

Let's look at the Corona code:

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!", 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' => 1);
echo( json_encode( $results ) );

Or something like that.



[TOPIC: post.html]
#9

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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
 



[TOPIC: post.html]
#10

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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



[TOPIC: post.html]
#11

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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



[TOPIC: post.html]
#12

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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
 



[TOPIC: post.html]
#13

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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.



[TOPIC: post.html]
#14

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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?



[TOPIC: post.html]
#15

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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



[TOPIC: post.html]
#16

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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!



[TOPIC: post.html]
#17

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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



[TOPIC: post.html]
#18

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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?



[TOPIC: post.html]
#19

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

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?



[TOPIC: post.html]
#20

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

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



[TOPIC: post.html]
#21

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

Hey Rob,

 

I am now at a different phase of my app, and got all the logging in and account creation finished and working, password encryption and email verification included. I now would like to incorporate google maps into the app, and I'm now using the business sample app as guidance on how to implement the api. I was just curious though if it is possible to use the maps api to show location of other users using the app who are online, similar to how the ios Find My Friends app works or Uber does.

 

BTW, should I post this as a new topic?

 

Thanks in advance,

-YL



[TOPIC: post.html]
#22

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

If you just want to drop pins where people are and annotate those pins, it's possible. The Business App sample should show you how to do that. What it won't show you is how to retrieve that data from your server.

 

Rob



[TOPIC: post.html]
#23

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

I'm not sure I understand what you mean. By drop pins, did you mean do that in the code for preset locations?

Allow me to further explain what I would like to do with my app. Hypothetically, users would want to interact with each other, and should be able to see nearby users who choose to be discoverable for others (in real time), and maybe set meet up locations too. Is this possible with corona? If so, do you have any idea as to how this would be achieved or if it has been discussed before in the forums or with sample apps?



[TOPIC: post.html]
#24

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,263 posts
  • Enterprise

What I mean by drop pins (which is the map lingo) is to place a marker at the location where something you care about exists. The sample you are working from drops four custom pins (Starbucks Logos) at the location where there are Starbucks located around the Palo Alto area.

 

In the code (I'm going from memory as I don't have it open in front of me) there is a table that contains the latitude and longitude for each of the locations and perhaps some other info. I believe I loop over that table with a for loop and instruct the map API's to place the pin a the lat/long spot on the map.

 

What I don't do is I don't fetch that information from a server. To do what you want, you have to have a way for each app user to find the locations of the other. Typically that means your app will report your location to your server. Then you will have a way to talk to your server and get all people near you. That should return to you a list of people, their lat/long and other info. You can then plot those pins on your map in a way similar to the sample.

 

If you want more real time behavior you may need to implement a way to have your server send out push notifications that can update your app, or use a multi-player game plugin feature (check out Photon Cloud in our store.coronalabs.com as a starter). Your app could reasonably send out updates and Photon cloud would broadcast values back out to everyone in near real time.

 

Rob



[TOPIC: post.html]
#25

yanko_l10

[GLOBAL: userInfoPane.html]
yanko_l10
  • Enthusiast

  • 81 posts
  • Corona SDK

Hey Rob,

 

I was wondering if you would be willing to chat? You've been helping me a lot with my app through these forums, but it would probably be easier if we could actually have a conversation on some other platform, assuming of course you are willing to do so. I just needed some more help with the maps API like I mentioned before, and I really don't know where else to go for help.

 

Thanks in advance,

YL




[topic_controls]
Page 1 of 2 1 2
 
[/topic_controls]