Installing Two-Factor Authentication with Authy

Posted by Adam Engebretson on November 3, 2013

"Authy provides a very simple mobile App that consumers use to protect their digital life as well as an API for developers to add strong Two-Factor Authentication to their websites and applications."

Authy makes it easy to integrate two-factor authentication into your existing Laravel application. It's user-end implementation is versitile, offering support for both smart-phones and standard phones. Furthermore, implmentation with its API is easy and seamless. Finally, its analytics and data tracking can you provide insights into your users and how they interact with your application's authentication system.

This tutorial assumes that you have an Authy developer account (which can be created here), an application (either testing or production), and your application's API key.

Getting Started

First, we'll install Laravel and Authy's PHP developer kit by executing the following commands in the terminal:

mkdir authy && cd authy
composer create-project laravel/laravel .
composer require authy/php:dev-master

Next, we'll create a migration for our users table by executing the following command:

php artisan migrate:make --create --table="users" create_users_table

This will create a migration file in /app/database/migrations containing some boilerplate code to create the users table. We'll add some fields so our migration file looks like this:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration {

  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('users', function(Blueprint $table)
    {
      $table->increments('id');
      $table->string('email');
      $table->string('username');
      $table->string('password');
      $table->string('authy')->nullable();
      $table->timestamps();
    });
  }

  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::drop('users');
  }

}

After you've configured your database, you can execute php artisan migrate to create the table. Laravel comes with a User model out-of-the-box, located at /app/models/User.php that will already work with your new table.

Next, I like to add my Authy API key to the /app/config/auth.php configuration file with the key authy. This will allow us to access the API key from anywhere in our application with Config::get('auth.authy').

Finally, I like to run all of my authentication methods through a controller. Let's create a file in /app/controllers/AuthController.php. It should look like this to start with:

<?php

class AuthController extends BaseController {

  private $authy;

  public function __construct()
  {
    $this->authy = new Authy_Api(Config::get('auth.authy'));
  }

}

Adding Two-Factor Authentication to a User

I'll assume that you understand Laravel's authentication system and let you handle the styling of the front-end forms and routing. Go ahead and create a user using your application's registration method or manually (you can use php artisan tinker to generate a hashed password if you want to add the user manually).

To add Two-Factor Authentication to a user, you'll have to gather some information from them. First, is their country code. For my application, I chose to set this by default for them, since all of my users will be based in the United States. Next, you'll need their full phone number. Collect this in a form, and we'll post to the following AuthController method:

public function postRequestInstallation()
{
    $user = Auth::user();

    $installation = $this->authy->registerUser($user->email, Input::get('phone'));

    if($installation->ok()) {
        return Redirect::route('auth.finishInstallation')->withAuthyId($installation->id());
    } else {
        return Redirect::back()->withError($installation->errors());
    }
}

The registerUser() method will send a text-message and email to the user, suggesting that they install the Authy app to their smartphone, as well as instructions on how to request an SMS token.

It is recommended by the Authy API documentation to save the $installation->id() in the $user model at this stage, however I like to force the user to verify that they can, in fact, receive their authentication token before installation. This prevents a user from getting locked out of their account in the event of a typo or other incident.

In your route named auth.finishInstallation, you should display a form to the user requesting their authentication token. You'll then submit that form to the following controller method, including their entered token, as well as Session::get('authy-id') which was flashed to the route in the successful redirect above.

public function postFinishInstallation()
{
    $user = Auth::user();

    $verification = $this->auth->verifyToken(Input::get('authy-id'), Input::get('token'), array('force' => 'true'));

    if($verification->ok()) {
        $user->authy = Input::get('authy-id');
        $user->save();

        return Redirect::route('auth.profile')->withMessage('Two-Factor Authentication has now been installed!');
    } else {
        return Redirect::back()->withError('Unable to verify your authentication token.');
    }
}

This method will verify the user's token, as well as store their authy-id in the database. It is this field that we will check for when the user is logging in to determine whether or not we should present them with the token form.

Logging In

I like to utilize some AJAX in my login forms to simplify the login process. To do this, I send a POST ajax request to the following controller method after a user has blurred the email field in my login form.

public function postGetAuthyId()
{
    $user = User::whereEmail(Input::get('email'))->first();

    if($user->authy != null)
        return Response::json(array('require' => 'true', 'authy-id' => $user->authy));
    else
        return Response::json(array('require' => 'false'));
}

This will return a JSON object telling you whether or not you should show them the Authy token field. Once they finish this form, you can submit the request to the following method:

public function postLogin()
{
    $user = User::whereEmail(Input::get('email'))->first();

    if($user->authy) {
        $verification = $this->authy->verifyToken($user->authy, Input::get('authy'));
        if(!$verification->ok()) {
            return Redirect::back()->withError('Unable to verify Authy token.');
        }
    }

    $credentials = [
        'email' => Input::get('email'),
        'password' => Input::get('password'),
    ];

    if(Auth::attempt($credentials)) {
        return Redirect::route('auth.profile');
    } else {
        return Redirect::back()->withError('Unable to verify credentials.');
    }
}

Obviously, you can customize your redirections and how you handle errors. But that's the gist of it.

Removing Two-Factor Authentication

Now that you are an Authy pro, it won't be hard for you to figure out how to remove the two-factor authentication from a user. Simply set the $user->authy variable to null, and save the user. Ta-da! I'd recommend that you verify their token before you do this, however. You don't want just anyone removing the two-factor authentication from an account, now do you?

Note: this blog just got incredibly informal because I'm watching the YouTube Music Awards, which were completely unscripted... It threw off my groove... Plus, Lady Gaga is weird. ><

Anyway, have any awesome integrations? Share in the comments!