2013-08-01



Writing a registration system is a lot of work. You have to write code that validates email addresses, sends confirmation emails, provides forgotten password functionality, stores passwords securely, validates input forms and a lot more. Even when you do all of this, users will still be reluctant to register as it involves a lot of effort on their part as well.

In this tutorial, we will make a very simple registration system that doesn’t require or store passwords at all! The result will be easy to modify and embed into an existing PHP website. Read on to find out how it works.

The Idea

Here is how our super simple registration system will work:

There will be a combined login/registration form, where users will fill in their emails and hit submit;

Upon submission, if an email address is not found in the database, a new user record is created. A random token is generated and sent to the user via email as a clickable link that is only valid for 10 minutes;

Clicking the link in their inbox will take them to our site. The system will detect the presence of the token and will log the person in.

Here are the advantages of this approach:

No need to store and validate passwords;

No need for a password restoration feature, secret questions etc;

You can be certain that a person can be reached on the given email address from the first time they login;

The registration process is very simple and inviting.

Here are the disadvantages:

It is as secure as the user’s email account. If somebody has access to the person’s email, they can login. This is the case with any forgotten password feature, but is a thing to consider;

Email is not secure and can be intercepted. Keep in mind that this is also the case with any forgotten password feature or any regular login system that doesn’t use HTTPS to transfer username/password info;

Unless you take the time to configure your outgoing email properly, there is a chance that the messages with the login links will hit the spam box;

Given the advantages/disadvantages above, our login system is high on usability but not very high on security, so you should only use it for things like forum registrations, site memberships and services that don’t deal with sensitive information.

Using The Registration System

In case you only wish to use the login system in your site and not to follow the tutorial, here is what you need to do:

First you need to download the zip file above;

In the zip file, find tables.sql. Import it in your database by using phpMyAdmin’s import option. Alternatively you can open the file in a text editor, copy the SQL and execute it;

Open includes/main.php and fill in your database connection details and username/password combo. In the same file, you also have to add an email which will be used as the originating address for the messages sent out by the system. Some web hosts block outgoing email unless it originates from a real email address that is created from their control panel, so put a real address there;

Upload all the files via FTP or other means;

Add this code to every PHP page that you want to make available only after a login;

Have fun!

For the rest of you, who wish to learn how the login system works, read on!

The HTML

The first step is to write the HTML of the login form. The following HTML code resides in index.php. That file also holds the PHP code that handles the form submission and other useful functions of the login system. You will learn more about this in the PHP section.

index.php

In the head section, I’ve included the main stylesheet (it is not presented in the tutorial, so you will have to open it in a code editor to see it). Before the closing body tag, I am including the jQuery library and the script.js file, which we will be writing in the next section.



The Login / Registration Form

Now let’s write some jQuery!

The JavaScript

jQuery listens for the submit event on the form, calls e.preventDefault() on it, and sends an AJAX request instead. Depending on the response from the server, it shows a message and prevents further submissions.

assets/js/script.js

The .loading CSS class is added to the form while the AJAX request is commencing (this is made possible by the ajaxStart() and ajaxComplete() methods that you can see near the end of the file). This CSS class makes the rotating gif indicator show, and in addition acts as a flag which prevents double submissions (submitting the form while a request is pending). The .loggedIn class is another flag that is set once the email has been sent. It blocks all further form submissions permanently.

Database Schema

Our simple registration system uses two MySQL tables (you can find the SQL code in tables.sql in the downloadable zip). The first table holds the user accounts, and the second holds the login attempts.



Users Table Schema

The system doesn’t use passwords, which is reflected in the absence of a password field in the schema. There is a token column, accompanied by a token_validity column. The token is set when a user logs into the system and is sent to the user via email (more on that in the next section). The token_validity is then set to 10 minutes into the future, after which the token becomes invalid.

Every time somebody tries to log in, a new record is written to the second table. As you will see in our PHP code, thanks to this we are able to implement rate limiting by IP address. The limits are 10 login attempts per 10 minutes, and 20 attempts per hour. Anything more than that will result in the IP address being blocked until these limits are met.

Login Attempt Table Schema

In both tables, the IP address is stored as a an integer using the ip2long PHP function.

PHP

Now we are ready to write some PHP code. The main functionality of the registration system is provided by the User class, which you can see below. This class makes heavy use of Idorm (docs), which is a minimal library for working with databases that we’ve used in tutorials before. The User class handles database access, the generation of login tokens and their validation. It exposes a simple interface that makes it easy to include the registration system into your PHP-powered websites.

User.class.php

Tokens are generated with the SHA1 algorithm and saved to the database. I am using MySQL’s date and time functions to set the value of the token_validity column to 10 minutes into the future. When validating the token, we explicitly tell the engine that we are looking for a token whose token_validity field has not yet expired. This way we limit how long the login tokens are considered valid.

Notice that I use the __get magic method (docs) near the end of the file to capture property access on the user object. This makes it possible to access the data held in the database as properties: $user->email, $user->token etc. For an example on how to use this class in the next code fragments.

The Protected Page

Another file that holds useful functionality is functions.php. There we have a number of helper functions that make the rest of the code cleaner.

functions.php

The rate_limit and rate_limit_tick functions enforce rate limiting (maximum number of login attempts for a period of time). The login attempts are written to the reg_login_attempt database. These functions are called when handling the submissions of the login form, as you can see from the next fragment.

The code below is taken from index.php and it handles the login form submission. It returns an JSON response, which is handled by the jQuery code we saw in assets/js/script.js.

index.php

On a successful login or registration, the code above will send out an email to the person with a login link. The token is made available as the tkn $_GET variable in the generated URL.

Visiting the login link will trigger this code:

index.php

Calling $user->login() will create the needed session variables, so that subsequent views of any page of the site will keep the person logged in.

Logging out of the system is handled similarly:

index.php

At the end of the code, I am redirecting to index.php again, so that the ?logout=1 parameter in the URL is removed.

Our index.php file needs another protection – we don’t want already logged in people to see the form. For this purpose, we are going to use the $user->loggedIn() method:

index.php

Lastly, here is how to protect a page of your site and make it available only after a login:

protected.php

After this check, you can be sure that the user has been successfully logged in. You will also have access to the data stored in the database as properties of the $user object. To output the user’s email and their rank, use this code:

rank() is a method here, because the rank column in the database normally contains numbers (0 for a regular user, 1 for an administrator) and we need to transform these into rank names, which is done in that method. To turn a regular user into an administrator, simply edit their record from phpMyAdmin (or a database management program of your choice). Being an administrator will not give him any additional abilities – it will simply report him as such. It is up to your code to give special powers to administrators.

We’re Done!

With this our super simple registration system is complete! You can easily use it in an existing PHP website and modify it any way you like.

Show more