🔑 SMS OTP Verification for Laravel

In some occasions you may need to add OTP verification for your logged-in dashboard. Various ways exist for OTP verification (like Laravel Jetstream/Fortify) which will require your end user to have a separate mobile app for this. In my situation, I had the need to send the OTP codes via SMS.

To solve this need, I created the Laravel OTP package. The package provides an easy way to add SMS OTP verification to your app. It provides a middleware you can attach to any route you need, even on Laravel Nova or Filament admin panels.

How it works

The Laravel OTP package by default listens to the Login event to send a notification to the user. Using the middleware provided, you will be able to verify that the user verified their login using the code received. You may customise the notification class itself by override the notification key in the published config. By using this approach you can set any notification channels you wish, or even create your own to send the message.

Installation

To install, run the command below:

composer require chrysanthos/laravel-otp

You may customise how the notification looks by publishing the config and creating your own notification class.

php artisan vendor:publish --tag="laravel-otp-config"
php artisan make:notification OtpMessageNotification

Then update the config to specify your own notification

return [

    'enabled' => true,

    'notification' => \Chrysanthos\LaravelOtp\Notifications\SendOtpToUserNotification::class,

];

Use my own UI

If you want to customise the OTP view, you can publish the view and completely redesign the page. Additionally, the package allows you to use SMS OTP checks manually as well. For example, you may want to display a confirmation modal instead of redirecting to another view.

To do that, you will need to create routes for a custom controller which use the OtpService.

<?php

namespace App\Http\Controllers;

use Chrysanthos\LaravelOtp\Support\OtpService;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

class LoginOtpController
{
    public function send(Request $request)
    {
        app(OtpService::class)->generateOtpAndSend($request->user());

        return response()->noContent();
    }

    public function check(Request $request)
    {
        $request->validate([
            'otp' => ['required', 'numeric'],
        ]);

        $otpValid = app(OtpService::class)->check($request->user(), $request->get('otp'));

        throw_unless($otpValid, ValidationException::withMessages([
            'otp' => 'Invalid otp!',
        ]));

        return back();
    }

    public function resend(Request $request)
    {
        app(OtpService::class)->generateOtpAndSend($request->user());

        return back();
    }
}

You can then make your login form to use these routes and handle the UI yourself.

Only enable OTP to some users

The package checks for a shouldGoThroughOtp method on the User model which must return a boolean value.

<?php

class User extends Authenticatable implements MustVerifyEmail
{
    public function shouldGoThroughOtp(): bool
    {
        return $this->is_super_admin === true;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *