Some weeks back, I needed to use otp for verificarion and validation in a project, instead of email and phone number, which has always been the default method for user verification. I found a way around it and I'd love to show how it's done.
Let's dive right into it… Create a new project using the command
Create a new project
laravel new otpservice
cd otpservice
php artisan serve
This creates a new laravel project called otpservice.
There are couple of files to create, a model, migration and controller that we will use throughout the project.
Create OTP model, migration and controller
php artisan make:model Otp -mc
Note: the command creates three files, a model, migration and controller.
The Otp migration file
Open up the newly generated migration file and add the following column to the up function
$table->id();
$table->foreignId('user_id')->unique()->nullable();
$table->string('otp');
$table->boolean('is_used')->default(false);
$table->timestamp('expires_at')->default(now()->addMinutes(10));
$table->timestamps();
Note: You can add as many as possible foreign ids you want to use to reference the otp table and make them unique and nullable. Here we will be using it for user verification thereby using the 'user_id'. Also the expires_at set a default of 10 minutes, you can make it whatever time you wish.
Run the migrate command
php artisan migrate
The Otp.php model file
Add the following fillable, casts, hidden properties to the model file
protected $fillable = [
'otp',
'is_used',
'expires_at'
];
protected $casts = [
'expires_at' => 'datetime',
'is_used' => 'boolean'
];
protected $hidden = [
'otp'
];
In the Otp.php model class, add a one to one relationship which relates to the users table
public function user() {
return $this->belongsTo(User::class);
}
Also, in the User.php model class, add the corresponding hasOne relationship.
public function otp() {
return $this->hasOne(Otp::class);
}
The OtpController.php Controller File
Here is where we create all the methods needed to use the otp service, let's dive right into it.
Two methods are essential here, the generateOtp, regenerateOtp and verifyOtp.
<?php
namespace App\Http\Controllers;
use App\Models\Otp;
use Illuminate\Http\Request;
class OtpController extends Controller
{
public function generateOtp(Request $request)
{
$user = $request->user();
$otp = rand(1000, 9999);
$userOtp = $user->otp;
if ($userOtp) {
$userOtp->update([
'user_id' => $user->id,
'is_used' => false,
'otp' => $otp,
'expires_at' => now()->addMinutes(10)
]);
} else {
Otp::create([
'user_id' => $user->id,
'is_used' => false,
'otp' => $otp,
'expires_at' => now()->addMinutes(10)
]);
}
return response()->json([
'status' => true,
'message' => "Otp code created"
], 201);
}
public function verifyOtp(Request $request, $otp)
{
$user = $request->user();
$userOtp = $user->otp;
if (!$userOtp || $userOtp->is_used || $userOtp->expires_at < now()) {
return response()->json([
'status' => false,
'message' => "An error occurred",
'error' => "Invalid OTP, kindly request for another one"
], 400);
}
if ($userOtp->otp == $otp) {
// perform whatever operation you want to do when otp is verified
$userOtp->update([
'is_used' => true
]);
return response()->json([
'status' => true,
'message' => "Otp verified"
], 200);
}
return response()->json([
'status' => false,
'message' => "An error occurred",
'error' => "Otp is incorrect, please try again"
], 400);
}
}
Note: You can used the generateOtp method to regenerateOtp incase otp expires or otp has been used
API routes
Route::middleware('auth:api')->group(function () {
Route::get('/otp/generate', [OtpController::class, 'generateOtp']);
Route::get('/otp/verify/{otp}', [OtpController::class, 'verifyOtp']);
});
Note: The auth:api middleware is used to get authenticated user
I will create a package for this and create a blog on how to use this soon!.
Follow me on Twitter
Follow me on LinkedIn
Conclusion
This is it!!!
If you have any questions or need to make a clarification, please drop a comment.