2 Factor Authentication

Identity verification using two different components, e.g.:

  • Bank card and PIN
  • Password and TAN

Application

  • Security-critical applications
  • Online portals
  • Web-based services

2 Factor Authentication in Laravel

Automatic Creation of Login and Registration

$> composer require laravel/breeze --dev
$> php artisan breeze:install
// blade wählen
$> npm install
$> npm run dev
Loging Image

Database Configuration

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=zwei-faktor-auth
DB_USERNAME=root
DB_PASSWORD=

Database Migration

Schema::create('users', function (Blueprint $table) {
  $table->id();
  $table->string('name');
  $table->string('email')->unique();
  $table->timestamp('email_verified_at')->nullable();
  $table->string('phone_number');
  $table->boolean('phone_verified')->default(false);
  $table->string('password');
  $table->rememberToken();
  $table->timestamps();
});

Customize Registration View in „resources/views/auth/register.blade.php"

// Phone number in das Registrierungsformular hinzufügen
<!-- Phone number -->
<div class="mt-4">
  <x-input-label for="phone_number" :value="__('Phone number')" />
  <x-text-input id="phone_number" class="block mt-1 w-full" type="number" name="phone_number" :value="old('phone_number')" required />
</div>

Adjust phone number validation in registration in
„app/Http/Controllers/Auth/RegisteredUserController.php“

<?php
  $request->validate([
    ...
    'phone_number' => ['required', 'numeric', 'min:10'],
    ...
  ]);
  $user = User::create([
    ...
    'phone_number' => $request->phone_number, // Also add this to the User Model
    ...
  ]);
  ...
  return redirect(route('verify.phone'));
?>

Redirect after login in
„app/Http/Controllers/AuthenticatedSessionController.php “

public function store(LoginRequest $request): RedirectResponse
{
  $request->authenticate();
  $request->session()->regenerate();
  return redirect(route('verify.phone'));
}

Create a view for the verification: verify.blade.php

<html>
  <head>
  </head>
  <body>
    <form action="/verify/phone" method="post">
    @csrf
      <input type="text" name="code">
      <input type="submit">
    </form>
  </body>
</html>
Route::get('/verify/phone', [VerifyPhoneController::class, 'index'])->name('verify.phone');
Route::post('/verify/phone', [VerifyPhoneController::class, ‚verifyCode'])->name('verify.phone');

Create a Controller for the Verification: VerifyPhoneController

public function connect()
{
    $sid = getenv("TWILIO_ACCOUNT_SID");
    $token = getenv("TWILIO_AUTH_TOKEN");
    $sender = new Client($sid, $token);
    return $sender;
}
public function index()
{
    $sender = $this->connect();
    $verification = $sender->verify
        ->v2
        ->services(getenv("TWILIO_VERIFICATION_SID"))
        ->verifications
        ->create("+".str_replace('-', '', Auth::user()->phone_number),
        "sms");
    return view('phone.verify');
}
public function verifyCode(Request $request)
{
    $sender = $this->connect();
    $check_code = $sender->verify
        ->v2
        ->services(getenv('TWILIO_VERIFICATION_SID'))
        ->verificationChecks
        ->create(
            [
            "to" => "+" . str_replace('-', '', Auth::user()->phone_number),
            "code" => $request->code
        ]
    );
    if ($check_code->valid === true) {
        User::where('id', Auth::user()->id)
            ->update([
            'phone_verified' => $check_code->valid
        ]);
        return redirect(route('dashboard'));
    } else {
        session()->flash('error', 'Verification failed');
    }
}

Install Twilio SDK

composer require twilio/sdk

Create Twilio Account and a Service "Verify"

Twilio Image

Configuration in .env

TWILIO_ACCOUNT_SID=<TWILIO_ACCOUNT_SID>
TWILIO_AUTH_TOKEN=<TWILIO_AUTH_TOKEN>
TWILIO_VERIFICATION_SID=<TWILIO_VERIFICATION_SID>

Test It

Test Image