18. Registration

Registering a user on our platform is simple: you just have to save the data in the database. But things get complicated when we have to check whether it is a human being or an android. Ideally there would be an interview with a box that emits little beeps and a magnifying glass pointing at our suspect's eye while we ask them questions about turtles (Blade Runner style), but nowadays we are going to settle for knowing whether the email account is real. And the best strategy is to send an email with a secret text (token) that we know in advance.

The steps to do a good registration and make sure no Replicant slips through would be:

  1. Ask for the data to create the account.
  2. Validate that the data is compatible with the database (the email does not exist, it has a valid format...).
  3. Save it in the database, and mark that for now it is not enabled.
  4. We send a token to the email address they gave us to check that it is a real user.
  5. Activate the account if they click the link and the token is correct.
  6. Take them to the identification page and notify them that their account has been activated.

Complete example

The database has a table called usuarios with the structure:

Field Type Primary key
email String Yes
password String No
activo Int No
token String No

You can download the prepared SQLite compatible with the example.

registro.php

We will have our form, the validation that the email does not exist in the database, the ability to save the data and send the email with the link to validate our account.

<?php
//======================================================================
// PROCESS FORM
//======================================================================

//-----------------------------------------------------
// Validation Functions
//-----------------------------------------------------

/**
 * Method that validates whether a text is not empty
 * @param {string} - Text to validate
 * @return {boolean}
 */
function validar_requerido(string $texto): bool
{
    return !(trim($texto) == '');
}

/**
 * Method that validates whether the text has a valid E-Mail format
 * @param {string} - Email
 * @return {bool}
 */
function validar_email(string $texto): bool
{
    return filter_var($texto, FILTER_VALIDATE_EMAIL);
}

//-----------------------------------------------------
// Variables
//-----------------------------------------------------
$errores = [];
$email = isset($_REQUEST['email']) ? $_REQUEST['email'] : '';
$password = isset($_REQUEST['password']) ? $_REQUEST['password'] : '';

// We check whether the data reaches us via POST
if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    //-----------------------------------------------------
    // Validations
    //-----------------------------------------------------
    // Email
    if (!validar_requerido($email)) {
        $errores[] = 'Email field is required.';
    }

    if (!validar_email($email)) {
        $errores[] = 'Email field does not have a valid format';
    }

    // Password
    if (!validar_requerido($password)) {
        $errores[] = 'Password field is required.';
    }

    /* Verify that the same email does not exist in the database */
    // Connect to the database
    $miPDO = new PDO('sqlite:base-de-datos.sqlite');
    // Count how many emails exist
    $miConsulta = $miPDO->prepare('SELECT COUNT(*) as length FROM usuarios WHERE email = :email;');
    // Run the search
    $miConsulta->execute([
        'email' => $email
    ]);
    // Collect the results
    $resultado = $miConsulta->fetch();
    // Check if it exists
    if ((int) $resultado['length'] > 0) {
        $errores[] = 'The email address is already registered.';
    }

    //-----------------------------------------------------
    // Create account
    //-----------------------------------------------------
    if (count($errores) === 0) {
        /* Registration In The Database */

        // Prepare INSERT
        $token = bin2hex(random_bytes(16));
        $miNuevoRegistro = $miPDO->prepare('INSERT INTO usuarios (email, password, activo, token) VALUES (:email, :password, :activo, :token);');
        // Run the new registration in the database
        $miNuevoRegistro->execute([
            'email' => $email,
            'password' => password_hash($password, PASSWORD_DEFAULT),
            'activo' => 0,
            'token' => $token
        ]);

        /* Sending Email With Token */

        // Header
        $headers = [
            'From' => 'curso@php.com',
            'Content-type' => 'text/plain; charset=utf-8'
        ];
        // Variables for the email
        $emailEncode = urlencode($email);
        $tokenEncode = urlencode($token);
        // Email text
        $textoEmail = "
           Hello!\n
           Thanks for registering on the best platform on the internet, you show intelligence.\n
           To activate, go to the following link:\n
           http://midomino.com/verificar-cuenta.php?email=$emailEncode&token=$tokenEncode
            ";
        // Sending the email
        mail($email, 'Activate your account', $textoEmail, $headers);

        /* Redirect to login.php with GET to inform of the email being sent */

        header('Location: identificarse.php?registrado=1');
        die();
    }
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>Registration</h1>
    <!-- We display errors via HTML -->
    <?php if (isset($errores)): ?>
    <ul class="errores">
        <?php
            foreach ($errores as $error) {
                echo '<li>' . $error . '</li>';
            }
        ?>
    </ul>
    <?php endif; ?>
    <!-- Form -->
    <form action="" method="post">
        <div>
            <!-- Email field -->
            <label>
                E-mail
                <input type="text" name="email">
            </label>
        </div>
        <div>
            <!-- Password field -->
            <label>
                Password
                <input type="password" name="password">
            </label>
        </div>
        <div>
            <!-- Submit button -->
            <input type="submit" value="Register">
        </div>
    </form>
</body>
</html>

verificar-cuenta.php

It will be reached through the link sent after registration. If the email and token match what is saved in the database, we will change the activo field to 1 (we mark it as enabled or verified).

<?php

//-----------------------------------------------------
// Variables
//-----------------------------------------------------
$email = isset($_REQUEST['email']) ? urldecode($_REQUEST['email']) : '';
$token = isset($_REQUEST['token']) ? urldecode($_REQUEST['token']) : '';

//-----------------------------------------------------
// CHECK WHETHER THE DATA IS CORRECT
//-----------------------------------------------------
// Connect to the database
$miPDO = new PDO('sqlite:base-de-datos.sqlite');
// Prepare SELECT to obtain the user's stored password
$miConsulta = $miPDO->prepare('SELECT COUNT(*) as length FROM usuarios WHERE email = :email AND token = :token AND activo = 0;');
// Run query
$miConsulta->execute([
    'email' => $email,
    'token' => $token
]);
$resultado = $miConsulta->fetch();
// The user with the token exists
if ((bool) $resultado['length']) {
    //-----------------------------------------------------
    // ACTIVATE ACCOUNT
    //-----------------------------------------------------
    // Prepare the update
    $miActualiacion = $miPDO->prepare('UPDATE usuarios SET activo = 1 WHERE email = :email;');
    // Run update
    $miActualiacion->execute([
        'email' => $email
    ]);
    //-----------------------------------------------------
    // REDIRECT TO IDENTIFICATION
    //-----------------------------------------------------
    header('Location: identificarse.php?activada=1');
    die();
}

// It is not a valid user, we send them to the identification form
header('Location: identificarse.php');
die();

identificarse.php

It displays information messages such as account activated or registered, but it is also responsible for checking whether the data is correct. And in that case, creating a session and taking the user to the private page.

<?php
    //======================================================================
    // PROCESS FORM
    //======================================================================

    //-----------------------------------------------------
    // Variables
    //-----------------------------------------------------
    $email = isset($_REQUEST['email']) ? $_REQUEST['email'] : null;
    $password = isset($_REQUEST['contrasenya']) ? $_REQUEST['contrasenya'] : null;
    $errores = [];

    // We check that the form data reaches us
    if ($_SERVER['REQUEST_METHOD'] == 'POST') {

        //-----------------------------------------------------
        // CHECK WHETHER THE ACCOUNT IS ACTIVE
        //-----------------------------------------------------
        // Connect to the database
        $miPDO = new PDO('sqlite:base-de-datos.sqlite');
        // Prepare SELECT to obtain the user's stored password
        $miConsulta = $miPDO->prepare('SELECT activo, password FROM usuarios WHERE email = :email;');
        // Run query
        $miConsulta->execute([
            'email' => $email
        ]);
        // I save the result
        $resultado = $miConsulta->fetch();
        if ((int) $resultado['activo'] !== 1) {
            $errores[] = 'Your account is not active yet. Have you checked your inbox?';
        } else {
            //-----------------------------------------------------
            // CHECK THE PASSWORD
            //-----------------------------------------------------
            // We check whether it is valid
            if (password_verify($password, $resultado['password'])) {
                // If it is correct, we create the session
                session_start();
                $_SESSION['email'] = $email;
                // We redirect to the secure page
                header('Location: privado.php');
                die();
            } else {
                $errores[] = 'The email or password is incorrect.';
            }
        }
    }
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Log in</title>
</head>
<body>
    <h1>Log in</h1>
    <!-- We display errors via HTML -->
    <?php if (count($errores) > 0): ?>
    <ul class="errores">
        <?php
            foreach ($errores as $error) {
                echo '<li>' . $error . '</li>';
            }
        ?>
    </ul>
    <?php endif; ?>
    <!-- Notice message when you register -->
    <?php if(isset($_REQUEST['registrado'])): ?>
    <p>Thanks for registering! Check your inbox to activate the account.</p>
    <?php endif; ?>
    <!-- Active account message -->
    <?php if(isset($_REQUEST['activada'])): ?>
    <p>Account activated!</p>
    <?php endif; ?>
    <!-- Identification form -->
    <form method="post">
        <p>
            <input type="text" name="email" placeholder="Email">
        </p>
        <p>
            <input type="password" name="contrasenya" placeholder="Password">
        </p>
        <p>
            <input type="submit" value="Log in">
        </p>
    </form>
</body>
</html>

privado.php

Just like in the Login lesson, the private area follows the identification.

``` php

Secret panel

Your secret panel

```

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.