logo

Advanced PHP User Login

Add comment

If you ever had a bank account you are familiar with TAN-s (Transaction Authentication Number). What we will do today, is create user login using that kind of system. This will be just simple overview how to do it so you can create more complex and secure login systems. You need to have basic knowledge about classes, HTML, CSS and AJAX because I will not explain in depth what I do.

What is TAN? Top

TAN stands for Transaction Authentication Number. It is used to authenticate transactions on Net-banking. Some banks use it when user want to create transaction and some banks use it when user tries to log in. TAN tables are tables that have string (mostly 6-8 characters long). Each string has its own row and columns. Using that grid it’s identified and asked for user to enter it.

Set up Top

First create folders and files as you can see them on picture below this text. You can download jQuery library from here and place it in jquery folder and loader image you can download from here.

Foder structure

Foder structure

After that create new database execute SQL code below in that database (or in any existing one).

-- `tan_tables` table

CREATE TABLE IF NOT EXISTS `tan_tables` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `row` int(11) NOT NULL,
  `column` int(11) NOT NULL,
  `value` varchar(32) COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin ;

-- `users` table

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(25) COLLATE utf8_bin NOT NULL,
  `password` varchar(32) COLLATE utf8_bin NOT NULL,
  `salt` varchar(12) COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin ;

-- Relations

ALTER TABLE `tan_tables`
  ADD CONSTRAINT `tan_tables_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

Now, that we have folders, files and database we can continue.

Markup and CSS Top

Now we do some HTML code. This will be our index.php.

< ?php
/**
 * @author Marijan Šuflaj <msufflaj32@gmail.com>
 * @link http://www.php4every1.com
 */

session_start();

$row = rand(1, 6);
$column = rand(1, 4);
$_SESSION['tan'] = array(
   'row'    => $row,
   'column' => $column
);
?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Login using tan tables.</title>
        <link href="css/main.css" type="text/css" media="screen, projection" rel="stylesheet" />
    </head>

    <body>
        <div id="wrapper">
            <div id="message" style="display: none;">
            </div>
            <div id="waiting" style="display: none;">
                Please wait<br />
                <img src="images/ajax-loader.gif" title="Loader" alt="Loader" />
            </div>
            <form method="post" id="loginForm">
                <fieldset>
                    <legend>Login form</legend>
                    <span style="font-size: 0.9em;">Please insert your credidentials.</span>
                    <p>
                        <label for="email">E-Mail:</label>
                        <input type="text" name="email" class="inputText" id="email" value="" />
                    </p>
                    <p>
                        <label for="password">Password:</label>
                        <input type="password" name="password" class="inputText" id="password" value="" />
                    </p>
                    <p>
                        <label for="tan">
                            Tan code (row: <span id="tanRow">
                                < ?php echo $row; ?>
                            </span>, column: <span id="tanColumn">
                                < ?php echo $column; ?>
                            </span>):
                        </label>
                        <input type="tan" name="tan" class="inputText" id="tan" value="" />
                    </p>
                    <p>
                        <input type="submit" name="submit" id="submit" style="float: right; clear: both; margin-right: 3px;" value="Submit" />
                    </p>
                </fieldset>
            </form>
        </div>
        <script type="text/javascript" src="js/jquery/jquery-1.3.2.js"></script>
        <script type="text/javascript" src="js/userLogin.js"></script>
    </body>
</html>

This is all just basic HTML. At top we have some PHP code that takes care about creating session and choosing grid position for TAN code. $row can be any number from 1 to 6 because we’ll have 6 rows. $column can be any number from 1 to 4 because we’ll have 4 columns. After that we add those values to tan array in our $_SESSION. Lines 48 and 50 just echo those values so user knows what to enter.

Copy this code to your CSS file.

@CHARSET "UTF-8";

body {
    background-color: #f0f0f0;
}

#wrapper {
    margin: 100px auto;
    width: 310px;
}

#waiting {
    color: #767676;
    text-align: center;
}

fieldset {
    margin-top: 10px;
    background: #fff;
    border: 1px solid #c8c8c8;
    background-color: #fff;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
}

legend {
    background-color: #fff;
    border-top: 1px solid #c8c8c8;
    border-right: 1px solid #c8c8c8;
    border-left: 1px solid #c8c8c8;
    font-size: 1.2em;
    padding: 0px 7px;
    -moz-border-radius-topleft: 5px;
    -webkit-border-radius-topleft: 5px;
    -moz-border-radius-topright: 5px;
    -webkit-border-radius-topright: 5px;
}

.inputText {
    width: 275px;
}

.success {
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    width: 298px;
    background: #a5e283;
    border: #337f09 1px solid;
    padding: 5px;
}

.error {
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    width: 298px;
    background: #ea7e7e;
    border: #a71010 1px solid;
    padding: 5px;
}

Config and database connection Top

Now we will create our config.php that will have configuration values and then we will create some simple database class that we will use to connect to our database and execute queries.

Our config.php looks like this.

< ?php
/**
 * @author Marijan Šuflaj <msufflaj32@gmail.com>
 * @link http://www.php4every1.com
 */

/**
 * Config class.
 */
class config
{
    /**
     * Database host.
     */
    const HOST                  = 'localhost';

    /**
     * Database username.
     */
    const USERNAME              = 'root';

    /**
     * Database password.
     */
    const PASSWORD              = '';

    /**
     * Database name.
     */
    const NAME                  = 'tan_table_tutorial';
}

As you can see it’s really nothing special. It’s just one class that has some constants defined. We use class so we can easily access our config information in any function or class so we do not need some global variables.

Now we will create our database.php.

< ?php
/**
 * @author Marijan Šuflaj <msufflaj32@gmail.com>
 * @link http://www.php4every1.com
 */

/**
 * Database class.
 *
 */
class dataBase {

	/**
	 * Connection resource.
	 *
	 * @var resource
	 */
	private static $_con                     = null;

	/**
	 * dataBase class.
	 *
	 * @var dataBase
	 */
	private static $_self                    = null;

	/**
	 * Constructor.
	 *
	 */
	private function __construct()
	{

	}

	/**
	 * Destructor.
	 */
	public function __destruct()
	{
	   mysql_close(self::$_con);
	}

	/**
	 * Returns instance of this class.
	 *
	 * @return dataBase
	 */
	public static function getInstance()
	{
		return (is_null(self::$_self)) ? new dataBase() : self::$_self;
	}

	/**
	 * Connect.
	 *
	 * @param string $host Host
	 * @param string $user User
	 * @param string $pass Pass
	 * @param string $db Database
	 * @return bool True if connected, otherwise false
	 */
	public static function connect($host, $user, $pass, $db)
	{
		if (is_resource(self::$_con))
            return true;

        if ((self::$_con = mysql_connect($host, $user, $pass)) === false)
            return false;

		return (mysql_select_db($db, self::$_con) === false) ? false : true;
	}

	/**
	 * Function inserts new row.
	 *
	 * @param string $table Table name
	 * @param array $fields Fields
	 * @return bool
	 */
	public function insert($table, $fields)
	{
		if (!is_array($fields))
	        return false;

	    $sql = 'INSERT INTO `' . mysql_real_escape_string($table) . '` (';
	    $tmp = 'VALUES (';

	    foreach ($fields as $field => $value) {
	        $sql .= '`' . mysql_real_escape_string($field) . '`, ';
	        $tmp .= "'" . mysql_real_escape_string($value) . "', ";
	    }
	    $sql = substr($sql, 0, -2) . ') ' . substr($tmp, 0, -2) . ')';

	    if (!mysql_query($sql, self::$_con))
	        return false;

	    return true;
	}

	/**
	 * Function that executes query.
	 *
	 * @param string $query
	 * @return resource | false
	 */
	public function query($query)
	{
		if (!($rez = mysql_query($query, self::$_con)))
	        return false;

	    return $rez;
	}
}

We implement here singletron patter so that we can only have 1 connection to our database. To do that we make our __constructor() private. __destructor() just closes our MySQL connection. getInstance() is static function that returns instance of our class (if it does not exist it creates new instance). connect() is simple function that we use to connect to our database and selecting database. It returns false on error or true on success. Then we have function insert() that function is used to simplify inserting. We just pass our table name and in an array key => value pairs. Key is column name in our database and value is columns value that will be inserted. Now we have function query() that is used to execute query.

AJAX Submit Top

Copy this to userLogin.js.

/**
 * @author Marijan Šuflaj <msufflaj32 @gmail.com>
 * @link http://www.php4every1.com
 */

$(document).ready(function(){

	$('#submit').click(function() {

		$('#waiting').show(500);
		$('#loginForm').hide(0);
		$('#message').hide(0);

		$.ajax({
			type 	 : 'POST',
			url 	 : 'post.php',
			dataType : 'json',
			data	 : {
				email 	 : $('#email').val(),
				password : $('#password').val(),
				tan	     : $('#tan').val()
			},
			success : function(data){
				$('#waiting').hide(500);
				$('#message').removeClass().addClass((data.error === true) ? 'error' : 'success')
					.text(data.msg).show(500);
				if (data.error === true) {
					$('#tanRow').text(data.tan.row);
					$('#tanColumn').text(data.tan.column);
					$('#loginForm').show(500);
				}
			},
			error : function(XMLHttpRequest, textStatus, errorThrown) {
				$('#waiting').hide(500);
				$('#message').removeClass().addClass('error')
					.text('There was an error.').show(500);
				$('#loginForm').show(500);
			}
		});

		return false;
	});
});

This is code that handles AJAX requests. I don’t want to repeat myself so check this tutorial in order to know what’s going on. I don’t want to explain twice how this is done because I’ve done just some minor changes to my previous tutorial.

Validate Log In Top

This is code that validates users data.

< ?php
/**
 * @author Marijan Šuflaj <msufflaj32@gmail.com>
 * @link http://www.php4every1.com
 */

session_start();

require_once 'incs/database.php';
require_once 'config.php';

if (!dataBase::connect(
	    config::HOST,
	    config::USERNAME,
	    config::PASSWORD,
	    config::NAME
	)) {
	$return['error'] = true;
	$return['msg'] = 'Failed to connect to database.';
}

$db = dataBase::getInstance();

while (true) {
	if (empty($_POST['email'])) {
		$return['error'] = true;
		$return['msg'] = 'You did not enter you email.';
		break;
	}

	if (empty($_POST['password'])) {
		$return['error'] = true;
        $return['msg'] = 'You did not enter you password.';
        break;
	}

    if (empty($_POST['tan'])) {
        $return['error'] = true;
        $return['msg'] = 'You did not enter you tan code.';
        break;
    }

    $sql = "SELECT `salt` "
    . "FROM `users` "
    . "WHERE `username` = '" . mysql_real_escape_string($_POST['email']) . "' "
    . "LIMIT 1";

    if (($rez = $db->query($sql)) === false) {
    	$return['error'] = true;
        $return['msg'] = 'Failed to execute query.';
        break;
    }

    if (mysql_num_rows($rez) !== 1) {
    	$return['error'] = true;
        $return['msg'] = 'Username with this e-mail does not exists.';
        break;
    }

    $salt = mysql_fetch_assoc($rez);

    $pass = md5($_POST['password']);
    $div = intval(32 / strlen($_POST['password']));
    $pass = md5(substr($pass, 0, $div)
    . $salt['salt']
    . substr($pass, $div));

    $sql = "SELECT `id` "
    . "FROM `users` "
    . "WHERE `username` = '" . mysql_real_escape_string($_POST['email']) . "' "
    . " AND `password` = '" . $pass . "'"
    . "LIMIT 1";

    if (($rez = $db->query($sql)) === false) {
        $return['error'] = true;
        $return['msg'] = 'Failed to execute query.';
        break;
    }

    if (mysql_num_rows($rez) !== 1) {
        $return['error'] = true;
        $return['msg'] = 'Your password is invalid.';
        break;
    }

    $id = mysql_fetch_assoc($rez);

    $sql = "SELECT * "
    . "FROM `tan_tables` "
    . "WHERE `row` = '" . $_SESSION['tan']['row'] . "'"
    . " AND `column` = '" . $_SESSION['tan']['column'] . "'"
    . " AND `value` = '" . md5($_POST['tan']) . "'"
    . " AND `user_id` = '" . $id['id'] . "'";

    break;
}

if (isset($return['error'])) {
	$row = rand(1, 6);
	$column = rand(1, 4);
	$_SESSION['tan'] = array(
	   'row'    => $row,
	   'column' => $column
	);
	$return['tan']  = array(
       'row'    => $row,
       'column' => $column
    );
}
else {
	$return['error'] = false;
	$return['msg'] = 'You are loged in.';
}

echo json_encode($return);

First we start session, include required files and connect to our database. Then we do some checking. If you are wondering why we use while loop to check our data, read this tutorial (at the bottom of the page). First 3 checks validate if user has entered all required data. Then we select salt from database and by doing so we check if user exists. If he does exist we check if passwords match. If that so, we check his TAN input. If that is also true then our user is ready to log in. Last we have if statement that we use to generate new TAN request (after each unsuccessful log in user needs to enter different TAN code) if user has failed to log in, or we generate message about successful log in. Then we just return our results in JSON format.

Creating new user Top

File genTansAndUser.php generates new user.

< ?php
/**
 * @author Marijan Šuflaj <msufflaj32@gmail.com>
 * @link http://www.php4every1.com
 */

require_once 'config.php';
require_once 'incs/database.php';

if (!dataBase::connect(
        config::HOST,
        config::USERNAME,
        config::PASSWORD,
        config::NAME
    )) {
    echo 'Failed to connect.';
    die();
}

$db = dataBase::getInstance();

$salt = substr(md5(uniqid()), 0, 12);

$userName = 'msufflaj32@gmail.com';
$pass = 'um0b0l3sn1k';
$len = strlen($pass);

$pass = md5($pass);
$div = intval(32 / $len);
$pass = md5(substr($pass, 0, $div)
. $salt
. substr($pass, $div));

$db->insert('users', array(
    'username' => $userName,
    'password' => $pass,
    'salt'     => $salt
));

$id = mysql_insert_id();
?>
<table>
	<tr>
	    <td> </td>
	    <td>Column 1</td>
	    <td>Column 2</td>
	    <td>Column 3</td>
	    <td>Column 4</td>
	</tr>
< ?php
for ($i = 1; $i < 7; $i++) :
?>
    <tr>
        <td>Row < ?php echo $i; ?></td>
< ?php
    for ($j = 0; $j < 5; $j++) :
        if ($j === 0)
            continue;

        $tanVal = rand(1000000, 9999999);
        $db->insert('tan_tables', array(
            'user_id' => $id,
            'row'     => $j,
            'column'  => $i,
            'value'   => md5($tanVal),
        ));
?>
        <td>< ?php echo $tanVal; ?></td>
< ?php
    endfor;
?>
    </tr>
< ?php
endfor;
?>
</table>

This is just quick example on how you can create users and it’s tans. In $userName you place users name and in $pass you place its salt. Then you just run this page and your user is generated. PHP generates tan table so you can save it somewhere else and use it later. Each TAN value is a radnom number betwean 1000000 and 9999999 that is then saved as MD5 hash.

Conclusion Top

This is very simple way to implement TAN. You can integrate it to your web site and make more secure logins for users. You can see demo here or download source code here. To log in use as username msufflaj32@gmail.com and as password  um0b0l3sn1k. You can see TAN table codes below.

TAN Table Codes

TAN Table Codes

I hope you like it. Thank you for reading.

Related Posts
  • 09.09.2009 — AJAX Multi-Level Comments (16)
    In this tutorial we will create multi level comments. You must have seen comments on youtube and tha…
  • 18.08.2009 — Permissions Using Bitwise (2)
    This will be a quick tutorial on how to use bitwise operators in PHP to create permissions control. …
  • 23.12.2009 — Learning Resources (0)
    Reader Satish requested a list of tutorials where he could learn about PHP, MySQL and jQuery. Since …
  • 29.06.2009 — jQuery AJAX tutorial (46)
    This tutorial cover some of basics about jQuery and AJAX. We will build AJAX form submit where we wi…
  • 02.01.2010 — Enable E-mail In PHP – Win (0)
    This will be a quick tutorial that will show you how to enable e-mail function in PHP on Windows….
  • 25.11.2009 — Implementing Bitwise Permissions (0)
    Reader Freddy requested a tutorial about implementing bitwise permissions in real application. This …
  • 16.10.2009 — PHP DomDocument Tutorial (5)
    This will be a quick tutorial that will show you how to use PHP’s DOMDocument to parse your XML so y…

logo

8 comments to “Advanced PHP User Login”

  1. You should have array with two dimensions for errors so you do not have $return['msg'] but $return['msg'][].

    Next you should remove break lines.

    Then you have to, in jQery part, where you have
    if (data.error === true) {
    ...
    }
    a
    dd for loop that will echo each error.

  2. Emil says:

    What should I do to make the script show more than one error at a time? I guess I need to use some form of loop.

    Ex: Show both “You did not enter your email.” and “You did not enter your password.” if non of them are filled in.

  3. You can make same thing for normal page (save user to session and then check if he has privileges to access this part of the page) :) .

  4. Charith J says:

    Ok. I like your second option for the my question #1.
    When i mean “protect the page” i mean like a user cannot view the content of the page without login in first.
    But i think if I program it right, a true/false variable can take care of that too.
    Awesome. Thanks for your help :)

    1. There are few options.
      1. You can add new variable (array key) that will hold html that you will display (not msg) and an other variable that will be true/false if user is loged in. Variable msg is just used to echo message.
      2. You can add just one variable that will be true/false if user is loged in and then if user is loged in just refresh page. PHP will take care for if/else (save user to sessions).
    2. Don’t know how you mean ‘Protect it’.
  5. Charith J says:

    Ya i saw that already. but typing more into that $return['msg'] string will add it to the green box that shows that u successfully logged in.
    I want to add a table and have included files n everything. Sorry i wasnt more clear about it earlier.
    I got 2 questions:

    1) I know that in post.php it does the check to verify the user and log them in. Is there a way to do a check in the index.php file so I can display the login form if the user isnt logged in and display something else if user logged in successfully?

    2) How can i protect other pages? is there a php/js if statement or something I can include to a page to protect it?

  6. What ever you place in

    $return['msg']

    if there are no errors. If you place something like

    <b>You are loged in.</b>

    you’ll get You are loged in.

    You need to explore post.php file a little bit more :) .

  7. Charith J says:

    This script is awesome but I cant figure out where to type in the encrypted data. Like when i click submit, it shows “You are logged in”. How to i put other text there?

Leave a Reply


 *


 *


logo
logo
Powered by Wordpress | Designed by Elegant Themes | CopyRight ©2010 php4every1.com