srmklive / laravel-twofactor-authentication by srmklive

Plugin for enabling two-factor authentication in Laravel applications
40,125
66
7
Package Data
Maintainer Username: srmklive
Maintainer Contact: srmk@outlook.com (Raza Mehdi)
Package Create Date: 2015-12-04
Package Last Update: 2022-08-27
Home Page:
Language: PHP
License: MIT
Last Refreshed: 2024-04-19 15:05:35
Package Statistics
Total Downloads: 40,125
Monthly Downloads: 138
Daily Downloads: 5
Total Stars: 66
Total Watchers: 7
Total Forks: 13
Total Open Issues: 0

Laravel Two-Factor Authentication

Software License Latest Version on Packagist Total Downloads StyleCI Scrutinizer Code Quality SensioLabsInsight

Introduction

This plugins allows you to enable two-factor authentication in your Laravel applications.

Only Laravel 5.1 or greater supported

Installation

  • Use following command to install:
composer require srmklive/authy
  • Add the service provider to your $providers array in config/app.php file like:
Srmklive\Authy\Providers\AuthyServiceProvider::class
  • Add the alias to your $aliases array in config/app.php file like:
'Authy' => Srmklive\Authy\Facades\Authy::class
  • Run the following command to publish configuration:
php artisan vendor:publish --provider "Srmklive\Authy\Providers\AuthyServiceProvider"
  • Run the following command to migrate user table changes to database:
php artisan migrate
  • Add the following lines in your User model (e.g App\User.php)

    • Before the class declaration, add these lines:
use Srmklive\Authy\Auth\TwoFactor\Authenticatable as TwoFactorAuthenticatable;
use Srmklive\Authy\Contracts\Auth\TwoFactor\Authenticatable as TwoFactorAuthenticatableContract;
  • Now the change the class declaration. For example, if your class declaration is
class User extends Model implements AuthenticatableContract,
                                    AuthorizableContract,
                                    CanResetPasswordContract

then change it to this:

class User extends Model implements AuthenticatableContract,
                                    AuthorizableContract,
                                    CanResetPasswordContract,
                                    TwoFactorAuthenticatableContract
  • Now change the import traits line accordingly in user model file. For example if the line is:
use Authenticatable, Authorizable, CanResetPassword;

to

use Authenticatable, Authorizable, CanResetPassword, TwoFactorAuthenticatable;
  • Lastly, add/update $hidden variable to hide 'two_factor_options' field from any DB call for user detail:
protected $hidden = [
	'two_factor_options'
];

Modifying Login Workflow

  • You need to add the following code to your app\Http\Controllers\Auth\AuthController.php.

    /**
     * Send the post-authentication response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @return \Illuminate\Http\Response
     */
    protected function authenticated(Request $request, Authenticatable $user)
    {
        if (Authy::getProvider()->isEnabled($user)) {
            return $this->logoutAndRedirectToTokenScreen($request, $user);
        }

        return redirect()->intended($this->redirectPath());
    }
    
    /**
     * Generate a redirect response to the two-factor token screen.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @return \Illuminate\Http\Response
     */
    protected function logoutAndRedirectToTokenScreen(Request $request, Authenticatable $user)
    {
        // Uncomment this line for Laravel 5.2+
        //auth($this->getGuard())->logout();

        // Uncomment this line for Laravel 5.1
        // auth()->logout();

        $request->session()->put('authy:auth:id', $user->id);

        return redirect(url('auth/token'));
    }

    /**
     * Show two-factor authentication page
     *
     * @return \Illuminate\Http\Response|\Illuminate\View\View
     */
    public function getToken()
    {
        return session('authy:auth:id') ? view('auth.token') : redirect(url('login'));
    }

    /**
     * Verify the two-factor authentication token.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function postToken(Request $request)
    {
        $this->validate($request, ['token' => 'required']);
        if (! session('authy:auth:id')) {
            return redirect(url('login'));
        }

        // Uncomment these lines for use in Laravel 5.2+ 
        //$guard = config('auth.defaults.guard');
        //$provider = config('auth.guards.' . $guard . '.provider');
        //$model = config('auth.providers.' . $provider . '.model');

        // Uncomment the line below for use in Laravel 5.1
        // $model = config('auth.model');

        $user = (new $model)->findOrFail(
            $request->session()->pull('authy:auth:id')
        );

        if (Authy::getProvider()->tokenIsValid($user, $request->token)) {
            // Uncomment this line for Laravel 5.2+
            //auth($this->getGuard())->login($user);

            // Uncomment this line for Laravel 5.1
	        //auth()->login($user);

            return redirect()->intended($this->redirectPath());
        } else {
            return redirect(url('login'))->withErrors('Invalid two-factor authentication token provided!');
        }
    }        
  • Add route to verify two-factor authentication token
Route::get('auth/token','Auth\AuthController@getToken');
Route::post('auth/token','Auth\AuthController@postToken');
  • Create view file in resources/views/auth/token.blade.php. Change this accordingly for your application. I have used code from AdminLTE theme here.
@extends('layouts.app')

@section('content')
    <div class="register-logo">
        Two-factor Authentication
    </div>

    <div class="register-box-body">
        <p class="login-box-msg">Validate your two-factor authentication token</p>
        <form method="POST" action="{{url('auth/token')}}">
            {!! csrf_field() !!}

            @if (count($errors) > 0)
                <div class="alert alert-danger">
                    <ul>
                        @foreach ($errors->all() as $error)
                            <li>{{ $error }}</li>
                        @endforeach
                    </ul>
                </div>
            @endif

            <div class="form-group has-feedback">
                <input type="type" name="token" class="form-control" placeholder="Token">
                <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
            </div>
            <div class="row">
                <div class="col-xs-7"></div><!-- /.col -->
                <div class="col-xs-5">
                    <button type="submit" class="btn btn-primary btn-block btn-flat">Verify Token</button>
                </div><!-- /.col -->
            </div>
        </form>
    </div><!-- /.form-box -->
@endsection

Usage

  • Registering User
$phone = '405-342-5699';
$code = 1;

$user = User::find(1);

$user->setAuthPhoneInformation(
    $code, $phone
);

try {
   Authy::getProvider()->register($user);

   $user->save();
} catch (Exception $e) {
   app(ExceptionHandler::class)->report($e);

   return response()->json(['error' => ['Unable To Register User']], 422);
}
  • Send token via SMS
$user = User::find(1);

try {
   Authy::getProvider()->sendSmsToken($user);
} catch (Exception $e) {
   app(ExceptionHandler::class)->report($e);

   return response()->json(['error' => ['Unable To Send 2FA Login Token']], 422);
}
  • Send token via phone call
$user = User::find(1);

try {
   Authy::getProvider()->sendPhoneCallToken($user);
} catch (Exception $e) {
   app(ExceptionHandler::class)->report($e);

   return response()->json(['error' => ['Unable To Send 2FA Login Token']], 422);
}
  • Validating two-factor token
$user = User::find(1);

try {
   Authy::getProvider()->tokenIsValid($user, $token);
} catch (Exception $e) {
   app(ExceptionHandler::class)->report($e);

   return response()->json(['error' => ['Invalid 2FA Login Token Provided']], 422);
}
  • Deleting User
$user = User::find(1);

try {
   Authy::getProvider()->delete($user);

   $user->save();
} catch (Exception $e) {
   app(ExceptionHandler::class)->report($e);

   return response()->json(['error' => ['Unable to Delete User']], 422);
}

Add a new TwoFactor Authentication Provider

Currently this package uses two-factor authentication services from Authy. You can also implement another two-factor authentication provider by doing the following:

<?php

namespace App\Services;

use Exception;
use GuzzleHttp\Client as HttpClient;
use Srmklive\Authy\Contracts\Auth\TwoFactor\Provider as BaseProvider;
use Srmklive\Authy\Contracts\Auth\TwoFactor\Authenticatable as TwoFactorAuthenticatable;

class MyAuthProvider implements BaseProvider
{
    /**
     * Array containing configuration data.
     *
     * @var array $config
     */
    private $config;

    /**
     * Authy constructor.
     */
    public function __construct()
    {
    	// Add your configuration code here
    }

    /**
     * Determine if the given user has two-factor authentication enabled.
     *
     * @param  \Srmklive\Authy\Contracts\Auth\TwoFactor\Authenticatable $user
     * @return bool
     */
    public function isEnabled(TwoFactorAuthenticatable $user)
    {
    	// Add your code here
    }

    /**
     * Register the given user with the provider.
     *
     * @param  \Srmklive\Authy\Contracts\Auth\TwoFactor\Authenticatable $user
     * @param boolean $sms
     * @return void
     */
    public function register(TwoFactorAuthenticatable $user, $sms = false)
    {
    	// Add your code here
    }

    /**
     * Determine if the given token is valid for the given user.
     *
     * @param  \Srmklive\Authy\Contracts\Auth\TwoFactor\Authenticatable $user
     * @param  string  $token
     * @return bool
     */
    public function tokenIsValid(TwoFactorAuthenticatable $user, $token)
    {
    	// Add your code here
    }

    /**
     * Delete the given user from the provider.
     *
     * @param  \Srmklive\Authy\Contracts\Auth\TwoFactor\Authenticatable $user
     * @return bool
     */
    public function delete(TwoFactorAuthenticatable $user)
    {
    	// Add your code here
    }
}

Demo Application

I have also implemented this package in a simple laravel application. You can view installation instructions here. Through this application, you can do:

  • User login & registration.
  • Enable/Disable two-factor authentication for a user.