Laravel Random Keys with Keygen

Glad Chinda

When developing applications, it is usually common to see randomness come into play - and as a result, many programming languages have built-in random generation mechanisms. Some common applications include:

  • Generating a random numeric code for email confirmation or phone number verification service.
  • Password generation service that generates random alphanumeric password strings.
  • Generating random base64-encoded tokens or strings as API keys.
  • Generating random strings as password salts to hash user passwords.

When your application is required to generate very simple random character sequences like those enumerated above, then the Keygen package is a good option to go for.

Introducing the Keygen Package

Keygen is a PHP package for generating simple random character sequences of any desired length and it ships with four generators, namely: numeric, alphanumeric, token and bytes. It has a very simple interface and supports method chaining - making it possible to generate simple random keys with just one line of code. The Keygen package can save you some time trying to implement a custom random generation mechanism for your application. Here are some added benefits of the Keygen package:

  • Seamless key affixes: It's very easy to add a prefix or suffix to the random generated string.
  • Key Transformations: You can process the random generated string through a queue of callables before it is finally outputted.
  • Key Mutations: You can control manipulations and mutations of multiple Keygen instances.

This tutorial provides a quick guide on how you can get started with the Keygen package and using it in your Laravel applications. For a complete documentation and usage guide of the Keygen package, see the README document at Github.

Getting Started

In this tutorial, we would be creating a simple REST API service. The API simply provides endpoints for creating user record, showing user record and generating a random password.

This tutorial assumes you already have a Laravel application running and the Composer tool is installed in your system and added to your system PATH. In this tutorial, I am using Laravel 5.3, which is the latest stable version at this time of writing. You can refer to the Laravel Installation guide if you don't have Laravel installed.

Next, we would install the Keygen package as a dependency for our project using composer. The Keygen package is available on the Packagist repository as gladcodes/keygen.

composer require gladcodes/keygen

If it installed correctly, you should see a screen like the following screenshot.

Keygen Installation Screenshot

Creating an alias for the Keygen package

The functionality of the Keygen package is encapsulated in the Keygen\Keygen class. For convenience, we would register an alias for this class, so that we can easily use it anywhere in our application. To create the alias, we would edit the config/app.php file and add a record for our alias after the last record in the aliases array as shown in the following snippet.

// config/app.php

'aliases' => [
    // ... other alias records
    'Keygen' => Keygen\Keygen::class,
],

Now we can use the Keygen package anywhere in our application. Add the use Keygen directive in your code to use the Keygen package as shown in the following usage example code.

// usage example

<?php

use Keygen;

$id = Keygen::numeric(10)->generate();
echo $id; //2542831057

Creating the User Model

Next, we would create a database table called users to store our users records. The schema for the table is as follows:

  • id INT(11) NOT NULL PRIMARY
  • code CHAR(24) NOT NULL UNIQUE
  • firstname VARCHAR(32) NOT NULL
  • lastname VARCHAR(32) NOT NULL
  • email VARCHAR(80) NOT NULL UNIQUE
  • password_salt CHAR(64) NOT NULL
  • password_hash CHAR(60) NOT NULL

What about autoincrement?
For this tutorial, the id of our users table would be a unique random generated integer, just to demonstrate with the Keygen package. This choice is based on preference, and does not in anyway discourage the use of auto-incremented IDs.

If created correctly, it should be as shown in the following screenshot.

Users table Screenshot

Before you proceed, check the config/database.php file and .env file of your application to ensure that you have the correct configuration for your database.

Next, we would create a model for the users table using Laravel's artisan command-line interface. Laravel ships with a built-in User model so we have to create our custom User model in a different location - app/Models folder, as shown in the following command.

php artisan make:model Models/User

We would modify the created User class in the app/Models/User.php file as shown in the following code to configure our model as required.

// app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $table = 'users';

    public $timestamps = false;

    public $incrementing = false;

    public function setEmailAttribute($email)
    {
        // Ensure valid email
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new \Exception("Invalid email address.");
        }

        // Ensure email does not exist
        elseif (static::whereEmail($email)->count() > 0) {
            throw new \Exception("Email already exists.");
        }

        $this->attributes['email'] = $email;
    }
}

In the preceeding code, we set the timestamps property to false to disable Laravel's timestamps features in our model. We also set the incrementing property to false to disable auto-incrementing of the primary key field.

Finally, defined a mutator for the email attribute of our model with email validation check and check to avoid duplicate email entries.

Defining Routes for the API

Next, we would define routes for the API endpoints. There are basically four endpoints:

  • GET /api/users
  • POST /api/users
  • GET /api/user/{id}
  • GET /api/password

// routes/web.php
// Add the following route definitions for API

Route::group(['prefix' => 'api'], function() {
    Route::get('/users', 'ApiController@showAllUsers');
    Route::post('/users', 'ApiController@createNewUser');
    Route::get('/user/{id}', 'ApiController@showOneUser');
    Route::get('/password', 'ApiController@showRandomPassword');
});

Next, we would create our ApiController using Laravel's artisan command-line interface and then add the methods registered in the routes.

php artisan make:controller ApiController

The above command creates a new file app/Http/Controllers/ApiController.php that contains the ApiController class. We can go ahead to edit the class and add the methods registered in the routes.

// app/Http/Controllers/ApiController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ApiController extends Controller
{
    public function showAllUsers(Request $request)
    {}

    public function createNewUser(Request $request)
    {}

    public function showOneUser(Request $request, $id)
    {}

    public function showRandomPassword(Request $request)
    {}
}

Laravel ships with a middleware for CSRF verification on all web routes. We won't require this for our API, so we will exclude our api routes from the CSRF verification service in the app/Http/Middleware/VerifyCsrfToken.php file.

// app/Http/Middleware/VerifyCsrfToken.php

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        '/api/*',
    ];
}

Generate Unique ID for User

The Keygen package will be used to generate a unique 8-digit integer ID for the user. We will implement the unique ID generation mechanism in a new generateID() method. We will also add use directives for Hash, Keygen and App\Models\User classes in our controller.

First let's add a new generateNumericKey() method for generating random numeric keys of length 8 integers.

// app/Http/Controllers/ApiController.php

<?php

namespace App\Http\Controllers;

use Hash;
use Keygen;
use App\Models\User;
use Illuminate\Http\Request;

class ApiController extends Controller
{
    // ... other methods

    protected function generateNumericKey()
    {
        return Keygen::numeric(8)->generate(); 
    }
}

The Keygen package generates numeric keys by statically calling the numeric() method of the Keygen\Keygen class. It takes an optional length argument which specifies the length of the numeric key and defaults to 16 if omitted or not a valid integer. In our case, the length of the generated numeric key is 8. The generate() method must be called to return the generated key.

Usually it is not desirable to have zeroes starting-off integers that will be stored in the database, especially IDs. The following snippet modifies the generation mechanism of the generateNumericKey() method by using the prefix() method provided by the Keygen package to add a non-zero integer at the beginning of the numeric key. This is known as an affix. The Keygen package also provides a suffix() method for adding characters at the end of generated keys.


// modified generateNumericKey() method
// Ensures non-zero integer at beginning of key

protected function generateNumericKey()
{
    // prefixes the key with a random integer between 1 - 9 (inclusive)
    return Keygen::numeric(7)->prefix(mt_rand(1, 9))->generate(true);
}

In the preceeding code, observe how we called numeric() with length 7. This is because we are adding a random non-zero integer as a prefix, making the length of the final generated numeric key to be 8 as is required.

And now let's implement the generateID() method to generate unique user IDs.


// generateID() method

protected function generateID()
{
    $id = $this->generateNumericKey();

    // Ensure ID does not exist
    // Generate new one if ID already exists
    while (User::whereId($id)->count() > 0) {
        $id = $this->generateNumericKey();
    }

    return $id;
}

Generate Code for User

Now we will generate a random code of the form XXXX-XXXX-XXXX-XXXX-XXXX for the user such that X is a hexadecimal character and always in uppercase. We will use a feature provided by the Keygen package called Key Transformation to transform randomly generated bytes to our desired code.

What is a Key Transformation?
A transformation is simply a callable that can take the generated key as the first argument and returns a string. Each transformation is added to a queue and executed on the generated key before the key is returned.

Let's create a new generateCode() method to handle the code generation logic.


protected function generateCode()
{
    return Keygen::bytes()->generate(
        function($key) {
            // Generate a random numeric key
            $random = Keygen::numeric()->generate();

            // Manipulate the random bytes with the numeric key
            return substr(md5($key . $random . strrev($key)), mt_rand(0,8), 20);
        },
        function($key) {
            // Add a (-) after every fourth character in the key
            return join('-', str_split($key, 4));
        },
        'strtoupper'
    );
}

Here we generated some random bytes by calling the byte() method of the Keygen package and then added three transformations to the randomly generated bytes as follows:

  • The first is a custom function that manipulates the randomly generated bytes, computes an MD5-hash and returns a substring of the hash that is 20 characters long.
  • The second is a custom function that adds a hyphen (-) after every fourth character of the substring from the previous transformation.
  • The last is the built-in strtoupper PHP function that makes the resulting string uppercase.

Creating a New User

Let's write the implementation of the createNewUser() method in our ApiController to create record for new user.


public function createNewUser()
{
    $user = new User;

    // Generate unique ID
    $user->id = $this->generateID();

    // Generate code for user
    $user->code = $this->generateCode();

    // Collect data from request input
    $user->firstname = $request->input('firstname');
    $user->lastname = $request->input('lastname');
    $user->email = $request->input('email');

    $password = $request->input('password');

    // Generate random base64-encoded token for password salt
    $salt = Keygen::token(64)->generate();

    $user->password_salt = $salt;

    // Create a password hash with user password and salt
    $user->password_hash = Hash::make($password . $salt . str_rot13($password));

    // Save the user record in the database
    $user->save();

    return $user;
}

In the preceeding snippet, we have used Keygen::token() to generate a random base64-encoded token for our password salt, 64 characters long. We also used Laravel's built-in Hash facade to make a bcrypt password hash using the user password and the password salt.

You can now create a user record through the route POST /api/users. I am using Postman to test the API endpoints. This is the JSON payload of my POST request:

{
    "firstname": "Jack",
    "lastname": "Bauer",
    "email": "jackbauer@movie24.net",
    "password": "f1gHtTerr0rIsts"
}

Here is the screenshot from Postman.

Creating a user

Implementing the remaining methods

Let's write the implementation for the remaining methods in our controller.


// app/Http/Controllers/ApiController.php

public function showAllUsers(Request $request)
{
    // Return a collection of all user records
    return User::all();
}

public function showOneUser(Request $request, $id)
{
    // Return a single user record by ID
    return User::find($id);
}

public function showRandomPassword(Request $request)
{
    // Set length to 12 if not specified in request
    $length = (int) $request->input('length', 12);

    // Generate a random alphanumeric combination
    $password = Keygen::alphanum($length)->generate();

    return ['length' => $length, 'password' => $password];
}

In the showRandomPassword() method implementation, we are using Keygen::alphanum() to create a random combination of alphanumeric characters as the generated password. The length of the generated password is gotten from the length query parameter of the request if provided, else, it defaults to 12 as specified.

Testing the API

Let's create another user record with the endpoint POST /api/users. I am using Postman to test the API endpoints. This is the JSON payload of my POST request:

{
    "firstname": "Glad",
    "lastname": "Chinda",
    "email": "gladxeqs@gmail.com",
    "password": "l0VeKOd1Ng"
}

Here is the screenshot from Postman.

Creating a new user

Now let's get all the user records using the endpoint GET /api/users. Here is the screenshot from Postman.

Getting all users

Next, we would get the record for one user. I want to get the record for the user Glad Chinda, so I will use the endpoint GET /api/user/93411315. Here is the screenshot from Postman.

Getting one user

Finally, we would test the password generation endpoint to generate random passwords. First, we would call the endpoint without a length parameter to generate a password of length 12 i.e GET /api/password. Here is the screenshot from Postman.

Getting password default length

Next, we would call the endpoint with a length parameter, GET /api/password?length=8 to generate a password of length 8. Here is the screenshot from Postman.

Getting password of length 8 chars

Conclusion

In this article, we have been able to explore the basic random key generation techniques of the Keygen package and also wire them into our Laravel application. For a detailed usage guide and documentation of the Keygen package, see the Keygen repository on Github. For a code sample of this tutorial, checkout the laravel-with-keygen-demo repository on Github.

Glad Chinda

Anything PHP, Javascript and NodeJS. With some HTML, CSS and AngularJS to spice it up. Very passionate about an awesome web.