Laravel OTP AUTH
This package allows you to authenticate with one time password access (OTP).
https://github.com/cuongnd88/otp-auth
Example Usage:
Route::get("/notify", function(){
return App\Models\User::find(1)->notify(new App\Authentication\SendOtp('mail', 4, 10));
});
Route::get("/auth-otp/{otp}", function(){
return App\Models\User::authByOtp(request()->otp, '84905279285');
});
Route::get("/check-otp/{otp}", function(){
return App\Models\User::find(1)->checkOtp(request()->otp);
});
Contents
Installation
1- Add the package to your dependencies.
$ composer require cuongnd88/otp-auth
2- Run the command:
php artisan auth:otp {ClassName}
Example:
php artisan auth:otp Authentication/SendOtp
SendOtp
class and HasOtpAuth
trait are auto-generated at app/Authentication
directory.
CreateNotificationsTable
class is alseo auto-generated at app/database/migrations
.
3- Apply the migrations:
It will create a table called notifications
to store generated OTP information.
$ php artisan migrate
Usage
Generate OTP
You can generate OTP via email or SMS
Route::get("/notify", function(){
return App\Models\User::find(1)->notify(new App\Authentication\SendOtp(['mail', 'nexmo']));
});
This package allows you to alter OTP length and lifetime
Route::get("/notify", function(){
$length = 4;
$liftime = 10; //minutes
return App\Models\User::find(1)->notify(new App\Authentication\SendOtp(['mail', 'nexmo']), $length, $liftime);
});
OTP default length: The default length is 6
.
OTP default lifetime: The default lifetime is 1
minute.
There is the detail of auto-generate SentOTP
class:
<?php
namespace App\Authentication;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Cuongnd88\DeliveryChannel\Messages\TwilioMessage;
class SendOtp extends Notification
{
use Queueable;
protected $defaultChannels = ['database'];
protected $otp;
protected $lifeTime;
const OPT_LIFETIME = 1;
const OPT_LENGTH = 6;
/**
* Construct
*
* @param array|string $channels
* @param integer|string $otpLength
* @param integer|string $lifeTime
*/
public function __construct($channels = null, $otpLength = null, $lifeTime = null)
{
$this->otp = $this->generateOtp($otpLength ?? self::OPT_LENGTH);
$this->lifeTime = $lifeTime ?? self::OPT_LIFETIME;
$this->defaultChannels = $this->verifyChannels($channels);
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $this->defaultChannels;
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('Your OTP is '.$this->otp)
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'otp' => $this->otp,
'expired_at' => now()->addMinutes($this->lifeTime)->toDateTimeString(),
];
}
/**
* Get the Nexmo / SMS representation of the notification.
*
* @param mixed $notifiable
*
* @return mixed
*/
public function toTwilio($notifiable)
{
return (new TwilioMessage)
->to("+8439xxxxxxx")
->from("+xxxxxxxxxx")
->body('OTP AUTH is '.$this->otp);
}
/**
* Generate OTP
*
* @param integer|string $n
*
* @return string
*/
public function generateOtp($n)
{
$generator = "0987654321";
$result = "";
for ($i = 1; $i <= $n; $i++) {
$result .= substr($generator, (rand()%(strlen($generator))), 1);
}
return $result;
}
/**
* Verify channels
*
* @param string|array $channels
*
* @return array
*/
public function verifyChannels($channels)
{
if ($channels && is_array($channels)) {
return array_merge($this->defaultChannels, $channels);
}
if ($channels && is_string($channels)) {
array_push($this->defaultChannels, $channels);
}
return $this->defaultChannels;
}
}
toTwilio: This method is implemented by importing delivery-channels.
Verify OTP
After sent OTP via your configed methods, you call authByOtp
to authenticate
Route::get("/auth-otp/{otp}", function(){
return App\Models\User::authByOtp(request()->otp, '84905123456');
});
Based on your credentials, you might authenticate with email or phone number
Set up the credentials:
In this case, you can apply User
model which must use HasOtpAuth
trait
. . . .
use App\Authentication\HasOtpAuth;
class User extends Authenticatable
{
use Notifiable;
use HasOtpAuth;
protected $credential = 'mobile';
. . . .
Let see more detail HasOtpAuth
trait
<?php
namespace App\Authentication;
trait HasOtpAuth
{
/**
* Check OTP
*
* @return bool
*/
public function checkOtp($otp)
{
$authenticator = $this->otp();
return $this->validateOtp($authenticator, $otp);
}
/**
* Get OTP data
*
* @return \Illuminate\Notifications\DatabaseNotification
*/
public function otp()
{
return $this->notifications()
->where('type', 'LIKE', '%SendOtp%')
->whereNull('read_at')
->first();
}
/**
* Validate OTP
*
* @param \Illuminate\Notifications\DatabaseNotification $authenticator
* @param mixed $otp
*
* @return void
*/
public function validateOtp($authenticator, $otp)
{
$result = false;
if (is_null($authenticator)) {
return response()->json($result,200);
}
if ($authenticator
&& now()->lte($authenticator->data['expired_at'])
&& $authenticator->data['otp'] == $otp
) {
$result = true;
}
$authenticator->markAsRead();
return response()->json($result,200);
}
/**
* Authenticate by OTP
*
* @param string $otp
* @param string $credentialValue
* @return void
*/
public static function authByOtp($otp, $credentialValue)
{
$model = new static;
$credentialName = property_exists($model,'credential') ? $model->credential : 'email';
$authenticator = $model->where($credentialName, '=', $credentialValue)->first();
if (is_null($authenticator)) {
return response()->json(false,200);
}
$authenticator = $authenticator->notifications()
->where('type', 'LIKE', '%SendOtp%')
->whereNull('read_at')
->first();
return $model->validateOtp($authenticator, $otp);
}
}
Basic identification
In some cases, you just need to identify the right access, you might need to execute checkOtp
method
Route::get("/check-otp/{otp}", function(){
return auth()->user->checkOtp(request()->otp);
});
Demo
This is demo soure code.
Laravel Colab
Credits
- Ngo Dinh Cuong
Top comments (0)