Laravel Login with Mobile Number Send OTP Example
Dec 06, 2022 . Admin
Hello Friends,
Here, I will show you the laravel login with mobile number send otp. step by step explains laravel login with mobile number send otp. if you want to see an example of how to laravel login with mobile number send otp. I explained simply how to login with mobile number send otp. Here, Creating a basic example of how to login with mobile number send otp in laravel.
You can use this example with laravel 6, laravel 7, laravel 8, and laravel 9 versions.
we will install Laravel UI for basic authentication. When the user will register we will ask for a mobile number. Then we will add a button for login with a mobile number OTP in laravel. When the user enters the mobile number and will receive otp on the mobile number. The user will input OTP and login with that. we will send OTP(One Time Password) in SMS using Twilio API
Step 1: Install LaravelThis is optional; however, if you have not created the laravel app, then you may go ahead and execute the below command:
composer create-project laravel/laravel example-appStep 2: Setup Database Configuration
After successfully installing the laravel app then configuring the database setup. We will open the ".env" file and change the database name, username and password in the env file.
.envDB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=Enter_Your_Database_Name DB_USERNAME=Enter_Your_Database_Username DB_PASSWORD=Enter_Your_Database_PasswordStep 3: Install Auth Scaffold
Laravel's laravel/ui package provides a quick way to scaffold all of the routes and views you need for authentication using a few simple commands:
composer require laravel/ui
Next, we need to generate auth scaffold with bootstrap, so let's run the below command:
php artisan ui bootstrap --auth
Then, install npm packages using the below command:
npm install
At last, built bootstrap CSS using the below command:
npm run buildStep 4: Create Migration
In this step, we need to create a new migration to add the avatar field to the users table. so let's run the below code and run migration.
php artisan make:migration add_new_fields_usersdatabase/migrations/2022_11_24_110854_add_new_fields_users.php
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->string('mobile_no')->nullable(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('mobile_no'); }); } };
php artisan make:migration create_user_otps_tabledatabase/migrations/2022_11_24_110854_create_user_otps_table.php
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('user_otps', function (Blueprint $table) { $table->id(); $table->bigInteger('user_id'); $table->string('otp'); $table->timestamp('expire_at')->nullable(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('user_otps'); } };
Now, run migration with the following command:
php artisan migrateStep 5: Create Model
In this step, we will update User.php model and create new model call UserOtp.php. Let's update code for that.
Next, update User.php model file.
app/Models/User.php<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', 'mobile_no' ]; /** * The attributes that should be hidden for serialization. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }app/Models/UserOtp.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Exception; use Twilio\Rest\Client; class UserOtp extends Model { use HasFactory; /** * Write code on Method * * @return response() */ protected $fillable = ['user_id', 'otp', 'expire_at']; /** * Write code on Method * * @return response() */ public function sendSMS($receiverNumber) { $message = "Login OTP is ".$this->otp; try { $account_sid = getenv("TWILIO_SID"); $auth_token = getenv("TWILIO_TOKEN"); $twilio_number = getenv("TWILIO_FROM"); $client = new Client($account_sid, $auth_token); $client->messages->create($receiverNumber, [ 'from' => $twilio_number, 'body' => $message]); info('SMS Sent Successfully.'); } catch (Exception $e) { info("Error: ". $e->getMessage()); } } }Step 6: Create Routes
In this step, we will create new routes for opt and submit otp code. you can see the below routes:
routes/web.php<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home'); Route::controller(App\Http\Controllers\Auth\AuthOtpController::class)->group(function(){ Route::get('otp/login', 'login')->name('otp.login'); Route::post('otp/generate', 'generate')->name('otp.generate'); Route::get('otp/verification/{user_id}', 'verification')->name('otp.verification'); Route::post('otp/login', 'loginWithOtp')->name('otp.getlogin'); });Step 7: Create Controller
Here, we will create AuthOtpController with some methods and update register controller file as well. so let's copy the below code and add to controller file:
app/Http/Controllers/Auth/RegisterController.php<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Models\User; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; class RegisterController extends Controller { /* |-------------------------------------------------------------------------- | Register Controller |-------------------------------------------------------------------------- | | This controller handles the registration of new users as well as their | validation and creation. By default this controller uses a trait to | provide this functionality without requiring any additional code. | */ use RegistersUsers; /** * Where to redirect users after registration. * * @var string */ protected $redirectTo = RouteServiceProvider::HOME; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest'); } /** * Get a validator for an incoming registration request. * * @param array $data * @return \Illuminate\Contracts\Validation\Validator */ protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'mobile_no' => ['required', 'numeric', 'digits:10'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); } /** * Create a new user instance after a valid registration. * * @param array $data * @return \App\Models\User */ protected function create(array $data) { return User::create([ 'name' => $data['name'], 'mobile_no' => $data['mobile_no'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } }app/Http/Controllers/Auth/AuthOtpController.php
<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use App\Models\User; use App\Models\UserOtp; class AuthOtpController extends Controller { /** * Write code on Method * * @return response() */ public function login() { return view('auth.otpLogin'); } /** * Write code on Method * * @return response() */ public function generate(Request $request) { /* Validate Data */ $request->validate([ 'mobile_no' => 'required|exists:users,mobile_no' ]); /* Generate An OTP */ $userOtp = $this->generateOtp($request->mobile_no); $userOtp->sendSMS($request->mobile_no); return redirect()->route('otp.verification', ['user_id' => $userOtp->user_id]) ->with('success', "OTP has been sent on Your Mobile Number."); } /** * Write code on Method * * @return response() */ public function generateOtp($mobile_no) { $user = User::where('mobile_no', $mobile_no)->first(); /* User Does not Have Any Existing OTP */ $userOtp = UserOtp::where('user_id', $user->id)->latest()->first(); $now = now(); if($userOtp && $now->isBefore($userOtp->expire_at)){ return $userOtp; } /* Create a New OTP */ return UserOtp::create([ 'user_id' => $user->id, 'otp' => rand(123456, 999999), 'expire_at' => $now->addMinutes(10) ]); } /** * Write code on Method * * @return response() */ public function verification($user_id) { return view('auth.otpVerification')->with([ 'user_id' => $user_id ]); } /** * Write code on Method * * @return response() */ public function loginWithOtp(Request $request) { /* Validation */ $request->validate([ 'user_id' => 'required|exists:users,id', 'otp' => 'required' ]); /* Validation Logic */ $userOtp = UserOtp::where('user_id', $request->user_id)->where('otp', $request->otp)->first(); $now = now(); if (!$userOtp) { return redirect()->back()->with('error', 'Your OTP is not correct'); }else if($userOtp && $now->isAfter($userOtp->expire_at)){ return redirect()->route('otp.login')->with('error', 'Your OTP has been expired'); } $user = User::whereId($request->user_id)->first(); if($user){ $userOtp->update([ 'expire_at' => now() ]); Auth::login($user); return redirect('/home'); } return redirect()->route('otp.login')->with('error', 'Your Otp is not correct'); } }Step 8: Create Blade File
In this step, we will create new blade file for otpLogin and otpVerification, then we will update login and register page as well. so let's update following file:
resources/views/auth/otpLogin.blade.php@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('OTP Login') }}</div> <div class="card-body"> @if (session('error')) <div class="alert alert-danger" role="alert"> {{session('error')}} </div> @endif <form method="POST" action="{{ route('otp.generate') }}"> @csrf <div class="row mb-3"> <label for="mobile_no" class="col-md-4 col-form-label text-md-end">{{ __('Mobile No') }}</label> <div class="col-md-6"> <input id="mobile_no" type="text" class="form-control @error('mobile_no') is-invalid @enderror" name="mobile_no" value="{{ old('mobile_no') }}" required autocomplete="mobile_no" autofocus placeholder="Enter Your Registered Mobile Number"> @error('mobile_no') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Generate OTP') }} </button> @if (Route::has('login')) <a class="btn btn-link" href="{{ route('login') }}"> {{ __('Login With Email') }} </a> @endif </div> </div> </form> </div> </div> </div> </div> </div> @endsectionresources/views/auth/otpVerification.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('OTP Login') }}</div> <div class="card-body"> @if (session('success')) <div class="alert alert-success" role="alert"> {{session('success')}} </div> @endif @if (session('error')) <div class="alert alert-danger" role="alert"> {{session('error')}} </div> @endif <form method="POST" action="{{ route('otp.getlogin') }}"> @csrf <input type="hidden" name="user_id" value="{{$user_id}}" /> <div class="row mb-3"> <label for="mobile_no" class="col-md-4 col-form-label text-md-end">{{ __('OTP') }}</label> <div class="col-md-6"> <input id="otp" type="text" class="form-control @error('otp') is-invalid @enderror" name="otp" value="{{ old('otp') }}" required autocomplete="otp" autofocus placeholder="Enter OTP"> @error('otp') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Login') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
Now, we need to update login and register view files:
resources/views/auth/login.blade.php@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Login') }}</div> <div class="card-body"> <form method="POST" action="{{ route('login') }}"> @csrf <div class="row mb-3"> <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <div class="col-md-6 offset-md-4"> <div class="form-check"> <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> <label class="form-check-label" for="remember"> {{ __('Remember Me') }} </label> </div> </div> </div> <div class="row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Login') }} </button> OR <a class="btn btn-success" href="{{ route('otp.login') }}"> Login with OTP </a> @if (Route::has('password.request')) <a class="btn btn-link" href="{{ route('password.request') }}"> {{ __('Forgot Your Password?') }} </a> @endif </div> </div> </form> </div> </div> </div> </div> </div> @endsectionresources/views/auth/register.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Register') }}</div> <div class="card-body"> <form method="POST" action="{{ route('register') }}"> @csrf <div class="row mb-3"> <label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="mobile_no" class="col-md-4 col-form-label text-md-end">{{ __('Mobile No') }}</label> <div class="col-md-6"> <input id="mobile_no" type="text" class="form-control @error('mobile_no') is-invalid @enderror" name="mobile_no" value="{{ old('mobile_no') }}" required autocomplete="mobile_no" autofocus> @error('mobile_no') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password"> </div> </div> <div class="row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsectionStep 9: Create Twilio Account & Install
First you need to create and add phone number. then you can easily get account SID, Token and Number.
Create Account from here: www.twilio.com.
Next add Twilio Phone Number
Next you can get account SID, Token and Number and add on .env file as like bellow:
.envTWILIO_SID=XXXXXXXXXXXXXXXXX TWILIO_TOKEN=XXXXXXXXXXXXX TWILIO_FROM=+XXXXXXXXXXX
next, we need to install twilio/sdk composer package to use send SMS using twilio. so let's run bellow command:
composer require twilio/sdk
Run Laravel App:
All the required steps have been done, now you have to type the given below command and hit enter to run the Laravel app:
php artisan serve
Now, Go to your web browser, type the given URL and view the app output:
http://localhost:8000/
You can see all the above screens as output.
Registration Page Preview: Mobile Number Preview: OTP Verify Preview: