Giter Site home page Giter Site logo

twilio-verify-phone-number-verification's Introduction

Verifying users phone numbers in a Laravel application with Twilio verify

In this tutorial, we will look at how to verify phone numbers using Twilio Verify by building a simple authentication system in Laravel.

Twilio Verify makes it easier and safer than custom verification systems to verify a user’s phone number. It ensures that the phone number is valid by sending an SMS short code to the number during registration. This can help reduce the amount of fake accounts created and failure rates when sending SMS notifications to users.

Prerequisite

In order to follow this tutorial, you will need:

Getting started

Create a new Laravel project using the Laravel Installer. If you don’t have it installed or prefer to use Composer, you can check how to do so from the Laravel documentation. Run this command in your console window to generate a fresh Laravel project:

$ laravel new twilio-phone-verify  

Now change your working directory to twilio-phone-verify and install the Twilio PHP SDK via composer:

$ cd twilio-phone-verify
$ composer require twilio/sdk 

If you don’t have Composer installed on your computer you can do so by following the instructions here.

You will need your Twilio credentials from the Twilio dashboard to complete the next step. Head over to your dashboard and grab your account_sid and auth_token.

Navigate to the Verify section to create a new Twilio Verify Service. Take note of the sid generated for you after creating the Verify service as this will be used for authenticating the instance of the Verify sdk.

Update the .env file with your Twilio credentials. Open up .env located at the root of the project directory and add these values:

TWILIO_SID="INSERT YOUR TWILIO SID HERE"
TWILIO_AUTH_TOKEN="INSERT YOUR TWILIO TOKEN HERE"
TWILIO_VERIFY_SID="INSERT YOUR TWILIO SYNC SERVICE SID"

Setting up Database

This tutorial will require a MySQL database for your application. If you use a MySQL client like phpMyAdmin to manage your databases, then go ahead and create a database named phone-verify and skip this section. If not, install MySQL from the official site for your platform of choice. After successful installation, fire up your terminal and run this command to login to MySQL:

$ mysql -u {your_user_name}

NOTE: Add the -p flag if you have a password for your mysql instance.

Once you are logged in, run the following command to create a new database

mysql> create database phone-verify;
mysql> exit;

Update your environmental variables with your database credentials. Open up .env and make the following adjustments:

DB_DATABASE=phone-verify
DB_USERNAME={your_user_name}
DB_PASSWORD={password if any}

Updating User Migration and Model

Now that your database is successfully set up, update your user migrations to create the necessary columns for your users. By default Laravel creates a user migration and Model when a new project is generated. Only a few adjustments will be needed to fit the needs of this tutorial.

Open up the project folder in your favourite IDE/text editor to start updating the needed fields in the users table. Open up the users migration file (database/migrations/2014_10_12_000000_create_users_table.php) and make the following adjustments to the up() method:

   public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('phone_number')->unique();
            $table->boolean('isVerified')->default(false);
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

The phone_number and isVerified fields were added for storing a user’s phone number and checking whether the phone number has been verified respectively.

Run the following command in the project directory root to add this table to your database:

$ php artisan migrate

If the file get migrated successfully, you will see the file name ({time_stamp}_create_users_table) printed out in the console window.

Now update the [fillable](https://laravel.com/docs/6.x/eloquent#mass-assignment) properties of the User model to include the phone_number and isVerified fields. Open up app/User.php and make the following changes to the $fillable array:

 /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password', 'phone_number', 'isVerified'
    ];

Implementing Authentication Logic

At this point, you have successfully set up your Laravel project with the Twilio PHP SDK and created your database. Next you'll write out our logic for authenticating a user. First, generate an AuthController which will house all needed logic for each authentication step. Open up a new console window in the project root directory and run the following command to generate a Controller:

$ php artisan make:controller AuthController

The above command will generate a controller class file in app/Http/Controllers/AuthController.php.

Registering Users

It's now time to implement your authentication logic. You will implement the registration logic first. Let’s assume that you are going to be sending out SMS notifications to registered users from your application. You will need to ensure that the phone numbers stored in your database are correct. There’s no better place to enforce this validation than at the point of registration. To accomplish this, you will make use of Twilio Verify to check if the phone number entered by your user is a valid phone number.

Open up app/Http/Controllers/AuthController.php and add the following method:

 /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(Request $request)
    {
        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'phone_number' => ['required', 'numeric', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
        /* Get credentials from .env */
        $token = getenv("TWILIO_AUTH_TOKEN");
        $twilio_sid = getenv("TWILIO_SID");
        $twilio_verify_sid = getenv("TWILIO_VERIFY_SID");
        $twilio = new Client($twilio_sid, $token);
        $twilio->verify->v2->services($twilio_verify_sid)
            ->verifications
            ->create($data['phone_number'], "sms");
        User::create([
            'name' => $data['name'],
            'phone_number' => $data['phone_number'],
            'password' => Hash::make($data['password']),
        ]);
        return redirect()->route('verify')->with(['phone_number' => $data['phone_number']]);
    }

Take a closer look at the code above. After validating the data coming in via the $request property, your Twilio credentials stored in the .env file are retrieved using the built-in PHP getenv() function. They are then passed into the Twilio Client to create a new instance. After which, your verify service is accessed from the instance the Twilio client using:

$twilio->verify->v2->services($twilio_verify_sid)
            ->verifications
            ->create($data['phone_number'], "sms");

The Twilio Verify service sid was also passed to the service which allows access to the Twilio Verify service you created earlier in this tutorial. Next you called the ->verifications->create() method by passing in the phone number to be verified and a channel for delivery. The OTP can be either mail, sms or call. You are currently making use of the sms channel which means your OTP code will be sent to the user via SMS. Next, the user’s data is stored in the database using the Eloquent create method:

User::create([
            'name' => $data['name'],
            'phone_number' => $data['phone_number'],
            'password' => Hash::make($data['password']),
        ]);

After that the user is redirected to a verify page sending their phone_number as data for the view.

Verifying Phone number OTP

After successful registration of the user, you will need to create a way for verifying the OTP sent to them via your channel of choice. Create a verify method to be used to verify the user’s phone number against the OTP code entered in your form. Open app/Http/Controllers/AuthController.php and add the following method:

  protected function verify(Request $request)
    {
        $data = $request->validate([
            'verification_code' => ['required', 'numeric'],
            'phone_number' => ['required', 'string'],
        ]);
        /* Get credentials from .env */
        $token = getenv("TWILIO_AUTH_TOKEN");
        $twilio_sid = getenv("TWILIO_SID");
        $twilio_verify_sid = getenv("TWILIO_VERIFY_SID");
        $twilio = new Client($twilio_sid, $token);
        $verification = $twilio->verify->v2->services($twilio_verify_sid)
            ->verificationChecks
            ->create($data['verification_code'], array('to' => $data['phone_number']));
        if ($verification->valid) {
            $user = tap(User::where('phone_number', $data['phone_number']))->update(['isVerified' => true]);
            /* Authenticate user */
            Auth::login($user->first());
            return redirect()->route('home')->with(['message' => 'Phone number verified']);
        }
        return back()->with(['phone_number' => $data['phone_number'], 'error' => 'Invalid verification code entered!']);
    }

Just like in the register() method, the data above is retrieved from the request and instantiates the Twilio SDK with your credentials before accessing the verify service. Let’s take a look at how this is structured:

$verification = $twilio->verify->v2->services($twilio_verify_sid)
            ->verificationChecks
            ->create($data['verification_code'], array('to' => $data['phone_number']));

From the above you can tell that you are accessing the Twilio Verify service as earlier, but this time you are making use of another method made available via the service:

->verificationChecks->create($data['verification_code'], array('to' => $data['phone_number']));

The create() function takes in two parameters, a string of the OTP code sent to the user and an array with a to property whose value is the user’s phone number which the OTP was sent to. The verificationChecks->create() method returns an object which contains several properties including a boolean property valid, which is either true or false depending on whether the OTP entered is valid or not:

if ($verification->valid) {
            $user = tap(User::where('phone_number', $data['phone_number']))->update(['isVerified' => true]);
            /* Authenticate user */
            Auth::login($user->first());
            return redirect()->route('home')->with(['message' => 'Phone number verified']);
        }
        return back()->with(['phone_number' => $data['phone_number'], 'error' => 'Invalid verification code entered!']);

Next the code checks to see if the valid property is true and then procees to update the isVerified field of the user to true. The application then proceeds to manually authenticate the user using Laravel’s Auth::login method which will login and remember the given User model instance.

Note: The User model must implement the Authenticatable interface before it can be used with the Laravel Auth::login method.

After successful verification of the user, they are redirected to the application dashboard.

Building The Views

All logic for registering and verifying a user has been written. Now let’s build the view that the user will use to interact with your application. A layout serving as the main interface of your application will be needed. Create a folder named layouts in resources/views/. Next create a file named app.blade.php in the layouts folder. Now open up the newly created file (resources/views/layouts/app.blade.php) and add the following:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>{{ config('app.name', 'Laravel') }}</title>
    <!-- Styles -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse"
                    data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
                    aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">
                    </ul>
                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @guest
                        <li class="nav-item">
                            <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                        </li>
                        @else
                        <li class="nav-item dropdown">
                            <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button"
                                data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                {{ Auth::user()->name }} <span class="caret"></span>
                            </a>

                        </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>
        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

Note: To expedite the creation of this application, Bootstrap is being utilized for styling your application and forms.

Next create a folder called auth in resources/views/. Now create the following files and paste in their respective content. In resources/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="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('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="form-group row">
                            <label for="phone_number" class="col-md-4 col-form-label text-md-right">{{ __('Phone Number') }}</label>
                            <div class="col-md-6">
                                <input id="phone_number" type="tel" class="form-control @error('phone_number') is-invalid @enderror" name="phone_number" value="{{ old('phone_number') }}" required>
                                @error('phone_number')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('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="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('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="form-group 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>
@endsection

and now in resources/views/auth/verify.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">{{ __('Verify Your Phone Number') }}</div>
                <div class="card-body">
                    @if (session('error'))
                    <div class="alert alert-danger" role="alert">
                        {{session('error')}}
                    </div>
                    @endif
                    Please enter the OTP sent to your number: {{session('phone_number')}}
                    <form action="{{route('verify')}}" method="post">
                        @csrf
                        <div class="form-group row">
                            <label for="verification_code"
                                class="col-md-4 col-form-label text-md-right">{{ __('Phone Number') }}</label>
                            <div class="col-md-6">
                                <input type="hidden" name="phone_number" value="{{session('phone_number')}}">
                                <input id="verification_code" type="tel"
                                    class="form-control @error('verification_code') is-invalid @enderror"
                                    name="verification_code" value="{{ old('verification_code') }}" required>
                                @error('verification_code')
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $message }}</strong>
                                </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Verify Phone Number') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Lastly, create a page where verified users will be taken to by creating a file called home.blade.php in resources/views/. Add the following content (resources/views/home.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">Dashboard</div>
                <div class="card-body">
                    @if (session('message'))
                        <div class="alert alert-success" role="alert">
                            {{ session('message') }}
                        </div>
                    @endif
                    You are logged in!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Updating Our Routes

Awesome! Now that you have completed creating the view, let’s update your routes/web.php file with the needed routes for your application. Open up routes/web.php and make the following changes:

<?php
/*
|--------------------------------------------------------------------------
| 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('auth.register');
})->name('register');

Route::get('/verify', function () {
    return view('auth.verify');
})->name('verify');

Route::get('/home', function () {
    return view('home');
})->name('home');

Route::post('/', 'AuthController@create')->name('register');
Route::post('/verify', 'AuthController@verify')->name('verify');

Testing Our Application

Now that you are done with building the application, let’s test it out. Open up your console window and navigate to the project directory and run the following command:

$ php artisan serve

This will serve your Laravel application on a localhost port, normally 8000. Open up the localhost link printed out after running the command on your browser and you should be greeted with registration page similar to this:

Fill out the registration form to trigger a OTP code to be sent. You will use this code in filling out the form in the page you were redirected to.

Conclusion

Great! By completing this tutorial, you have learned how to make use of Twilio's Verify Service for validating phone number(s) in a Laravel application. Also, we learned how to manually authenticate a user in a Laravel application. If you would like to take a look at the complete source code for this tutorial, you can find it on Github.

I’d love to answer any question(s) you might have concerning this tutorial. You can reach me via:

twilio-verify-phone-number-verification's People

Contributors

thecodearcher avatar themarcusbattle avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.