Integrate 2FA with Twilio in Laravel.

Updated: Jan 26, 2025

Integrate 2FA with Twilio in Laravel.

Two-Factor Authentication (2FA) is an essential security feature that adds an extra layer of protection to user accounts. Twilio is a cloud communications platform that provides various APIs for sending and receiving SMS, voice calls, and other communication channels. In this answer, we will discuss how to integrate 2FA with Twilio in Laravel.

To implement 2FA with Twilio in Laravel, we will use the following packages:

  1. Laravel-Nexmo: A Laravel wrapper for Twilio API.
  2. Laravel-OTP: A Laravel package for generating and validating One-Time Passwords (OTP).

Let's get started with the setup.

Step 1: Install Laravel-Nexmo package

First, we need to install the Laravel-Nexmo package using composer. Run the following command in your terminal:

composer require nexmo/client:^3.0

Next, publish the configuration file by running:

php artisan vendor:publish --provider="Nexmo\Laravel\NexmoServiceProvider"

Open the .env file and add your Twilio account SID, auth token, and phone number:

TWILIO_SID=your_account_sid
TWILIO_TOKEN=your_auth_token
TWILIO_FROM=your_twilio_number

Step 2: Install Laravel-OTP package

Install the Laravel-OTP package using composer:

composer require yadakhovskyy/otp-laravel

Publish the configuration file:

php artisan vendor:publish --provider="Yadakhovskyy\OTPLaravel\OTPLaravelServiceProvider"

Open the .env file and add your OTP secret key:

OTP_SECRET=your_secret_key

Step 3: Create OTP model

Create a new model called OTP using the following command:

php artisan make:model OTP

Open the OTP.php file and add the following code:

namespace App;

use Illuminate\Database\Eloquent\Model;
use Yadakhovskyy\OTPLaravel\Contracts\OTP as OTPContract;

class OTP extends Model implements OTPContract
{
    protected $table = 'otps';

    protected $fillable = ['user_id', 'code', 'expires_at'];

    public function user()
    {
        return $this->belongsTo(config('auth.models.user'));
    }
}

Step 4: Create OTP controller

Create a new controller called OTPController using the following command:

php artisan make:controller OTPController

Open the OTPController.php file and add the following code:

namespace App\Http\Controllers;

use App\Models\OTP;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Yadakhovskyy\OTPLaravel\Facades\OTP as OTPFacade;

class OTPController extends Controller
{
    public function send(Request $request)
    {
        $user = Auth::user();

        $code = OTPFacade::generate($user->email);

        OTP::create([
            'user_id' => $user->id,
            'code' => $code,
            'expires_at' => now()->addMinutes(5),
        ]);

        $message = "Your 2FA code is: {$code}";

        $nexmo = app('Nexmo\ClientProvider')->createClient();
        $response = $nexmo->message()->send([
            'to' => $user->phone_number,
            'from' => config('app.name'),
            'text' => $message,
        ]);

        if ($response->status == '0') {
            return response()->json(['message' => 'OTP sent successfully']);
        } else {
            return response()->json(['message' => 'Failed to send OTP'], 500);
        }
    }

    public function verify(Request $request)
    {
        $user = Auth::user();
        $code = $request->input('code');

        $otp = OTP::where('user_id', $user->id)
            ->where('code', $code)
            ->where('expires_at', '>', now())
            ->first();

        if ($otp) {
            $otp->delete();
            return response()->json(['message' => 'OTP verified successfully']);
        } else {
            return response()->json(['message' => 'Invalid OTP'], 401);
        }
    }
}

Step 5: Create routes

Add the following routes in the routes/web.php file:

Route::post('send-otp', 'OTPController@send')->name('send-otp');
Route::post('verify-otp', 'OTPController@verify')->name('verify-otp');

Step 6: Implement 2FA in Laravel

To implement 2FA in Laravel, we need to modify the LoginController. Open the LoginController.php file and add the following code:

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\OTPController;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = '/home';

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    public function showLoginForm()
    {
        return view('auth.login');
    }

    public function login(Request $request)
    {
        $this->validateLogin($request);

        if ($this->hasTwoFactorEnabled()) {
            return $this->sendOTP($request);
        }

        if ($this->attemptLogin($request)) {
            return $this->authenticated($request, $this->guard()->user()) ?: redirect()->intended($this->redirectPath());
        }

        return $this->sendFailedLoginResponse($request);
    }

    protected function hasTwoFactorEnabled()
    {
        return Auth::user()->two_factor_enabled;
    }

    protected function sendOTP(Request $request)
    {
        $otpController = app(OTPController::class);
        return $otpController->send($request);
    }

    protected function authenticated($request, $user, $authenticated = true)
    {
        if ($authenticated && $user->two_factor_enabled) {
            return redirect()->route('verify-otp');
        }

        return parent::authenticated($request, $user, $authenticated);
    }
}

Step 7: Enable 2FA for users

To enable 2FA for users, you can add a checkbox in the registration or profile form. When the user checks the checkbox, update the user's two_factor_enabled field to true.

Step 8: Testing

Now, you can test the 2FA implementation by registering a new user with 2FA enabled and logging in using the OTP.

That's it! You have successfully integrated 2FA with Twilio in Laravel.