krixon/multi-factor-auth

Multi-factor authentication library for PHP7

0.5.1 2020-02-13 09:24 UTC

README

Build Status Coverage Status Code Climate Latest Stable Version Latest Unstable Version License

SensioLabsInsight

A library for generating and verifying the codes used in multi-factor authentication systems.

Features:

  • Time-based (TOTP) code generation and verification.
  • Event-based (HOTP) code generation and verification.
  • Barcode generation for easy client setup.

This library implements the following RFCs:

It has been tested against the following multi-factor authentication tools:

Prerequisites

  • PHP 7.1+

Installation

Install via composer

To install this library with Composer, run the following command:

$ composer require krixon/multi-factor-auth

You can see this library on Packagist.

Install from source

# HTTP
$ git clone https://github.com/krixon/multi-factor-auth.git
# SSH
$ git clone git@github.com:krixon/multi-factor-auth.git

Quick Start

Let's say you have a server side application which you want to protect using multi-factor authentication.

There are three main steps involved:

  1. Generate a secret which is shared between the server and the user.
  2. Configure a client application (such as Google Authenticator) with the shared secret.
  3. Verify codes generated by the client application whenever the user needs to authenticate.

This library makes these steps easy.

The quickest way to get up and running is to create a new instance of the MultiFactorAuth class. This takes various arguments to its constructor, but there is a static factory provided which creates an instance with sensible and secure defaults. The only thing you need to provide is an "issuer" string. This is just a label which identifies the provider or service managing a user's account - i.e. your application.

<?php

use Krixon\MultiFactorAuth\MultiFactorAuth;

$mfa = MultiFactorAuth::default('Example Issuer');

Next you need to generate the shared secret. By default the code below will generate a 160-bit, base32-encoded string:

$secret = $mfa->generateSecret();

In order for the user to configure their client application, they need to enter the secret that was just generated. Often the user's client application will be running on their mobile phone. Entering a 160-bit secret by hand is certainly possible, but we can make it easier by providing the user with a barcode to scan. This barcode contains all of the information required to configure the client.

When generating a barcode you must also provide an account identifier. This can be any string which allows the user to distinguish between multiple accounts in their client application. A good value for this is the user's email address.

$barcode = $mfa->generateTimeBasedBarcode($secret, 'jane.doe@example.com');

The generateTimeBasedBarcode() method returns a Barcode instance. This can be used to ultimately render the image, for example on a webpage:

<img src="<?= $barcode->dataUri() ?>">

Once the user has scanned the barcode, they should be prompted to enter a code which can be verified to determine that the configuration process was successful.

$verified = $mfa->verifyTimeBasedCode($code, $secret);

If the code is verified successfully, the secret can be securely persisted on the server, for example in a database.

From now on, when the user authenticates they should be prompted to enter a code along with their other credentials such as username and password. This code should be verified using the stored shared secret and authentication denied if verification fails.

Generating Backup Codes

If a user loses their device or otherwise cannot generate codes, you can allow them to login via a pre-generated backup code. Event-based (HOTP) codes are perfect for this.

The following example generates 10 backup codes which the user can write down or otherwise store.

use Krixon\MultiFactorAuth\Codec\Base32Codec;
use Krixon\MultiFactorAuth\MultiFactorAuth;

$mfa = MultiFactorAuth::default('Test Issuer');

// Secrets are expected to be in base32 by default for compatibility with Google Authenticator and similar apps.
// It is possible to use any encoding (including none). See below for more information.
$secret = (new Base32Codec())->encode('12345678901234567890');

 // Retrieve the real counter from the DB or wherever it is stored.
$counter = 42;

// $codes is an array of 10 strings, each 6 digits long.
$codes = $mfa->generateBackupCodes($secret, $counter, 10, 6);

foreach ($codes as $code) {
    // Do something with the backup code.
    // Generally you would salt and hash the code and store it in a database. These codes would be checked
    // against the one entered by the user (in addition to checking the current time or event-based code).
    echo "$code\n";
}

Generating Secrets

By default, secrets are generated using the RandomBytesSecretGenerator. This generates cryptographically secure secrets using PHP's random_bytes function. If a different method is required, simply implement the SecretGenerator interface.

The RandomBytesSecretGenerator takes a Codec instance which determines how generated secrets are encoded. For maximum compatibility with Google Authenticator and similar apps, secrets should be base32 encoded, so the Base32Codec is used if no alternative is specified.

To generate a secret, either use a SecretGenerator directly, or use the MultiFactorAuth facade.

$generator = new RandomBytesSecretGenerator();

// Generates a base32-encoded, 20 byte random secret.
$secret = $generator->generateSecret();

// Generates a base32-encoded, 30 byte random secret.
$secret = $generator->generateSecret(30);

// Generates a raw binary, 20 byte random secret.
$generator = new RandomBytesSecretGenerator(new PassThroughCodec());
$secret    = $generator->generateSecret(30);

// Generates a base32-encoded, 20 byte random secret.
$mfa    = MultiFactorAuth::default('Test Issuer');
$secret = $mfa->generateSecret();

Sandbox

There is a simple sandbox script in the examples directory which can be used to generate secrets and barcodes and to verify codes generated by a client application.

The sandbox can be run with the PHP built-in webserver. Make sure to specify the correct path to the examples directory:

php -S localhost:8080 -t /path/to/krixon/multi-factor-auth/examples

You can now visit http://localhost:8080/sandbox.php to use the sandbox.

TODO