Vérification des numéros de téléphone dans Symfony 4 PHP avec Authy et Twilio SMS

May 29, 2019
Rédigé par
Oluyemi Olususi
Contributeur
Les opinions exprimées par les contributeurs de Twilio sont les leurs

Vérification des numéros de téléphone dans Symfony 4 PHP avec Authy et Twilio SMS

Introduction

L'une des méthodes les plus appropriées pour s'assurer que la base de données de votre application ne contient que des numéros de téléphone valides enregistrés pour chaque utilisateur consiste à vérifier correctement le numéro de téléphone pendant le processus d'enregistrement. Cela permet, entre autres, de garantir l'intégrité de l'application, de réduire le nombre d'enregistrements faux ou frauduleux et de convertir facilement ces données à des fins de marketing.

Dans ce tutoriel, je vais vous montrer comment vérifier les numéros de téléphone dans un projet Symfony 4 en exploitant l'API Verify de Twilio. Ensemble, nous allons créer une application qui va capturer les numéros de téléphone des utilisateurs, puis utiliser Twilio pour envoyer un code à 6 chiffres par SMS. Après réception de ce code, l'utilisateur devra le saisir pour que la vérification soit correcte.

Une fois que nous aurons terminé le processus - étape par étape - de mise en œuvre de cette fonctionnalité, vous saurez structurer un flux d'enregistrement approprié qui prend en compte la vérification du numéro de téléphone.

Conditions préalables

Pour tirer le meilleur parti de ce tutoriel, il est préférable d'avoir des connaissances raisonnables en matière de programmation orientée objet avec PHP. Pour rendre le tutoriel plus accessible aux débutants dans Symfony, je m'efforcerai de décomposer toute logique ou implémentation complexe.

Assurez-vous que Composer est installé globalement sur votre ordinateur. Enfin, si vous avez un compte Twilio existant, vous serez opérationnel en un rien de temps. Si vous n'avez pas encore de compte, ne vous inquiétez pas : nous allons en créer un plus tard dans cet article.

Ce que nous allons construire

Comme mentionné précédemment, l'application que nous allons construire dans cet article permettra de capturer le numéro de téléphone des utilisateurs pendant le processus d'enregistrement. Les détails des utilisateurs ne seront pas conservés dans la base de données tant que leur numéro de téléphone n'aura pas été vérifié.

Pour réussir à mettre en œuvre ce flux, nous allons exploiter un service spécifiquement mis à disposition par Twilio nommé API Authy. Ce service vérifie les numéros de téléphone en envoyant des codes par message vocal et par SMS. Dans le cadre de ce tutoriel, nous enverrons un code à 6 chiffres par SMS.

Voici à quoi ressemble le workflow de vérification :

  • Création d'une nouvelle application Authy afin d'obtenir une clé API
  • Envoi d'un token de vérification par SMS une fois qu'un utilisateur s'enregistre
  • Vérification du token de vérification et enregistrement des détails de l'utilisateur dans la base de données

Une fois cette procédure terminée, vous disposez d'une application capable de capturer et de confirmer de manière transparente les numéros de téléphone des utilisateurs dans le cadre du processus d'intégration, comme illustré ci-dessous :

vérification de numéro de téléphone

Installation et configuration d'un nouveau projet Symfony

Pour commencer, nous allons utiliser Composer pour créer une nouvelle application Symfony. Ouvrez un terminal, accédez au dossier de votre choix pour le développement de projet sur l'ordinateur et exécutez la commande suivante :

$ composer create-project symfony/website-skeleton phone-verify-twilio

La commande ci-dessus crée l'application dans un nouveau dossier nommé phone-verify-twilio et installe les dépendances pertinentes pour le projet. Une fois l'installation terminée, changez de répertoire dans le nouveau dossier de projet et démarrez le serveur Web intégré, comme indiqué ci-dessous :

// Change directory
$ cd phone-verify-twilio

// Start the server
$ php bin/console server:run

Cette opération installe la dernière version de l'outil dans le projet.

Configuration d'un compte Twilio

Pour accéder aux API et autres services offerts par Twilio, un compte Twilio est nécessaire. Cliquez ici pour commencer avec un compte Twilio gratuit.

Vérifiez votre numéro de téléphone et accédez au tableau de bord une fois que vous avez terminé. Vous obtenez une page similaire à celle illustrée ci-dessous :

Cliquez ensuite sur l'icône All Products and Services (Tous les produits et services) et faites défiler la liste pour sélectionner Authy :.

Sur la page de l'API Authy, cliquez sur le bouton Get Started (Commencer) et vérifiez votre numéro de téléphone pour pouvoir utiliser Authy.

Une fois la vérification du numéro de téléphone terminée, cliquez sur Applications dans la barre latérale. Cette opération affiche la liste de toutes les applications Authy que vous avez créées jusqu'à présent. Étant donné qu'aucune application n'a été créée pour le moment, cliquez sur le bouton Create Application (Créer une application).

Ensuite, donnez un nom convivial à votre nouvelle application. Je nomme la mienne verify-app et je clique sur Create (Créer).

création d'une nouvelle application authy

Une fois ce processus terminé, vous avez créé avec succès une application Authy avec les identifiants requis pour une connexion. Remarque : vous devrez revenir sur cette page ultérieurement pour la clé API.

page de settings authy

Création d'une classe d'entité

Au cours du processus d'enregistrement, nous avons l'intention de capturer les détails concernant nos utilisateurs et de vérifier leur numéro de téléphone avant de continuer à enregistrer leurs informations dans la base de données. Commençons par créer un modèle qui représente un utilisateur. Pour ce faire, nous allons utiliser le bundle maker qui est installé avec Symfony afin de générer une nouvelle entité « User ». Exécutez la commande suivante à cet effet :

$ php bin/console make:entity User

La commande ci-dessus entraîne la création de deux nouveaux fichiers, src/Entity/User.php et src/Repository/UserRepository.php. Ouvrez le fichier User.php et configurez-le comme suit :

// ./src/Entity/User.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;



/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity(fields="email", message="Email already taken")
 * @UniqueEntity(fields="username", message="Username already taken")
 */
class User implements UserInterface, \Serializable
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank()
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\NotBlank()
     * @Assert\Email()
     */
    private $email;

    /**
     * @Assert\NotBlank()
     * @Assert\Length(max="4096")
     */
    private $plainPassword;

    /**
     * @ORM\Column(type="string", length=64)
     */
    private $password;


    /**
     * @ORM\Column(type="string", length=255)
     */
    private $phoneNumber;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $countryCode;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $verificationCode;

    /**
     * @ORM\Column(type="boolean")
     */
    private $verified;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getUsername(): ?string
    {
        return $this->username;
    }

    public function setUsername(string $username): self
    {
        $this->username = $username;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getPhoneNumber(): ?string
    {
        return $this->phoneNumber;
    }

    public function setPhoneNumber(string $phoneNumber): self
    {
        $this->phoneNumber = $phoneNumber;

        return $this;
    }

    public function getCountryCode(): ?string
    {
        return $this->countryCode;
    }

    public function setCountryCode(string $countryCode): self
    {
        $this->countryCode = $countryCode;

        return $this;
    }

    public function getVerificationCode(): ?string
    {
        return $this->verificationCode;
    }

    public function setVerificationCode(?string $verificationCode): self
    {
        $this->verificationCode = $verificationCode;

        return $this;
    }

    public function getVerified(): ?bool
    {
        return $this->verified;
    }

    public function setVerified(bool $verified): self
    {
        $this->verified = $verified;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    /**
     * @param mixed $password
     */
    public function setPlainPassword($password)
    {
        $this->plainPassword = $password;
    }


    /**
     * Returns the password used to authenticate the user.
     *
     * @return string The password
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * @param mixed $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }


    /**
     * String representation of object
     * @link http://php.net/manual/en/serializable.serialize.php
     * @return string the string representation of the object or null
     * @since 5.1.0
     */
    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
        ));

    }

    /**
     * Constructs the object
     * @link http://php.net/manual/en/serializable.unserialize.php
     * @param string $serialized <p>
     * The string representation of the object.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function unserialize($serialized)
    {
        list(
            $this->id,
            $this->username,
            $this->password,
            ) = unserialize($serialized);
    }

    /**
     * Returns the roles granted to the user.
     *
     *     public function getRoles()
     *     {
     *         return ['ROLE_USER'];
     *     }
     *
     * Alternatively, the roles might be stored on a ``roles`` property,
     * and populated in any number of different ways when the user object
     * is created.
     *
     * @return (Role|string)[] The user roles
     */
    public function getRoles()
    {
       return array('ROLE_USER');
    }


    /**
     * Returns the salt that was originally used to encode the password.
     *
     * This can return null if the password was not encoded using a salt.
     *
     * @return string|null The salt
     */
    public function getSalt()
    {
        return null;
    }

    /**
     * Removes sensitive data from the user.
     *
     * This is important if, at any given point, sensitive information like
     * the plain-text password is stored on this object.
     */
    public function eraseCredentials()
    {
    }
}

Ici, nous avons créé des champs pour la base de données.

Création d'un contrôleur par défaut (DefaultController)

Ensuite, nous devons générer un nouveau contrôleur pour traiter le rendu de la page par défaut de notre application. Ouvrez le terminal et exécutez la commande suivante :

$ php bin/console make:controller DefaultController

Une fois cette opération effectuée, vous obtenez deux nouveaux fichiers, src/Controller/DefaultController.php et une page de vue correspondante dans templates/default/index.html.twig. Ouvrez le fichier DefaultController.php et remplacez son contenu par ce qui suit :

// ./src/Controller/DefaultController

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends AbstractController
{
    /**
     * @Route("/", name="default")
     */
    public function index()
    {
        return $this->render('default/index.html.twig');
    }
}

Le contrôleur ci-dessus affichera le contenu dans le fichier default/index.html.twig. Ouvrez template/default/index.html.twig et collez-y les éléments suivants :

{# ./template/default/index.html.twig #}

{% extends 'base.html.twig' %}

{% block title %} PHP Phone Verification with Twilio {% endblock %}

{% block body %}
    <div class="example-wrapper">
        <div class="jumbotron text-center">

            <h2> Verify phone numbers with Symfony4 and Twilio</h2>
            <div>
                {% if app.user %}

                    <a href="{{ path('home') }}" class="btn btn-default">Home</a>

                {% else %}
                    <div class="button-links">
                        <a href="{{ path('login') }}" class="btn btn-success">Login</a>
                        <a href="{{ path('user_registration') }}" class="btn btn-primary">Register</a>
                    </div>

                {% endif %}
            </div>
        </div>
    </div>

{% endblock %}

Générer le contrôleur de sécurité (Security Controller)

Dans cette section, nous générons un nouveau contrôleur qui traitera le processus de connexion d'un utilisateur :

$ php bin/console make:controller SecurityController

Ouvrez le contrôleur que vous venez de créer et remplacez son contenu par le code suivant :

// src/Controller/SecurityController.php

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;


class SecurityController extends AbstractController
{
    /**
     * @Route("/login", name="login")
     */
    public function login(Request $request, AuthenticationUtils $authUtils)
    {
        $error = $authUtils->getLastAuthenticationError();

        $lastUsername = $authUtils->getLastUsername();

        return $this->render('security/index.html.twig', [
            'last_name' => $lastUsername,
            'error' => $error,
        ]);
    }

}

Créer la page de connexion

Ouvrez le fichier templates/security/index.html.twig et configurez-le comme indiqué ici :

{# ./templates/security/index.html.twig #}

{% extends 'base.html.twig' %}

{% block title %}Login {% endblock %}

{% block body %}

    <div class="col-sm-12 wrapper">

        <div class="auth-form" style="max-width: 600px;margin: 0 auto;">

            <h2 class="text-center">Login</h2>
            <hr>

            {% if error %}
                <div class="alert alert-danger alert-dismissible">
                    <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
                    <strong>Error!</strong> {{ error.messageKey|trans(error.messageData, 'security') }}
                </div>

            {% endif %}

            {% for message in app.flashes('success') %}
                <div class="flash-notice alert alert-success alert-dismissible">
                    {{ message }}
                </div>
            {% endfor %}

            <form action="{{ path('login') }}" method="post">

                <div class="row">
                    <div class="form-group col-md-12">
                        <label for="username">Username:</label>
                        <input type="text" id="username" class="form-control" name="_username" value="" autocomplete="off"/>
                    </div>
                </div>

                <div class="row">
                    <div class="form-group col-md-12">
                        <label for="password">Password:</label>
                        <input type="password" id="password" class="form-control" name="_password" />
                    </div>
                </div>

                <div class="row">
                    <div class="form-group col-md-12">
                        <button type="submit" class="btn btn-success form-control">Login</button>
                    </div>
                </div>

                    <div class="text-center form-group">
                        Don't have an account? <span><a href="{{ path('user_registration') }}">Register</a></span>
                    </div>
            </form>
        </div>

    </div>

{% endblock %}

Ici, nous avons créé les champs de saisie nécessaires à l'authentification d'un utilisateur.

Générer le contrôleur d'enregistrement (RegistrationController)

Générez un contrôleur pour traiter l'enregistrement des utilisateurs avec :

$ php bin/console make:controller RegistrationController

Ouvrez le fichier src/Controller/RegistrationController.php et collez les éléments suivants :

// ./src/Controller/RegistrationController.php

<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class RegistrationController extends AbstractController
{
      /**
     * @Route("/register/page", name="user_registration", methods={"GET"})
     */
    public function registerAction(Request $request)
    {
        return $this->render('registration/index.html.twig');
    }
}

Créer la page d'enregistrement

Collez le contenu ci-dessous pour la page d'enregistrement. Le fichier se trouve dans le dossier templates/registration :

{# templates/registration/index.html.twig #}

{% extends 'base.html.twig' %}

{% block title %} Register {% endblock %}

{% block body %}

    <div class="col-sm-12 wrapper">
        <div class="auth-form" style="max-width: 600px;margin: 0 auto;">
            <h2 class="text-center">Register</h2>
            <hr>
            <form action="{{ url('register') }}" method="post">
                <div class="col-md-12">
                    <div class="row">
                        <div class="form-group col-md-12">
                            <label for="username"> Username </label>
                            <input type="text" name="username" class="form-control" placeholder="Enter username" id="username">
                        </div>
                        <div class="form-group col-md-12">
                            <label for="email"> Email </label>
                            <input type="text" name="email" class="form-control" placeholder="Enter email" id="email">
                        </div>
                    </div>


                    <div class="row">
                        <div class="form-group col-md-2">
                            <label for="country_code">  Code </label>
                            <input type="text" name="country_code" placeholder="+234" class="form-control" id="country_code">
                        </div>

                        <div class="form-group col-md-10">
                            <label for="phone_number"> Phone Number </label>
                            <input type="text" name="phone_number" class="form-control" id="phone_number">
                        </div>
                    </div>

                    <div class="row">
                        <div class="form-group col-md-12">
                            <label for="password">  Password </label>
                            <input type="password" name="password" class="form-control" id="password">
                        </div>
                    </div>
                </div>
                <div class="form-group col-md-12">
                    <button class="form-control btn btn-success"> Register </button>
                </div>
            </form>

            <div class="text-center">
                Have an account <span><a href="{{ path('login') }}">Login</a></span>
            </div>
        </div>
    </div>

{% endblock %}

Nous avons créé ici des champs de saisie pour le nom d'utilisateur, l'adresse e-mail, l'indicatif du pays, le numéro de téléphone et le mot de passe.

 

 

Ajout d'une feuille de style

Créez un nouveau dossier nommé css dans le dossier public et créez un autre fichier nommé style.css dans le dossier que vous venez de créer. Collez le code suivant dans le nouveau fichier :

// ./public/css/style.css

body {
    background: #e9ecef;
}

nav {
    background: #ffffff;
    height: 70px;
}

.button-links {
    margin-top: 40px;
}

.navbar ul li {
    display: inline-block;
    padding: 10px;
}

.navbar-nav {
    display: inline-block;
}

.welcome_page {
    margin-top: 50px;
}

Mise à jour du modèle de base

Mettez à jour le modèle de base comme suit :

// ./templates/base.html.twig

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{% block title %} PHP Phone Verification with Twilio {% endblock %}</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    {% block stylesheets %}
        <link rel="stylesheet" href="{{ asset('css/style.css') }}">
    {% endblock %}
</head>
<body>

<div id="app">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="{{ path('default') }}"> Phone Verify </a>
            </div>
            <ul class="nav navbar-nav navbar-right">
                {% if app.user %}
                    <li><a class="nav-link" href="{{ path('home') }}">Dashboard</a></li>
                    <li><a class="nav-link" href="{{ path('logout') }}">Logout</a></li>

                {% else %}

                    <li><a class="nav-link" href="{{ path('login') }}">Login</a></li>
                    <li><a class="nav-link" href="{{ path('user_registration') }}">Register</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>


    {% block body %}{% endblock %}
</div>
</body>
</html>

Si vous exécutez l'application à ce moment-là avec php bin/console server:run, vous verrez la page ci-dessous en accédant à http://localhost:8000 :

À ce stade, notre application est prête pour que les utilisateurs commencent à s'enregistrer et à vérifier leur numéro de téléphone de sorte à accéder aux zones sécurisées de notre application.

Mise à jour du fichier DotEnv

Dans le répertoire racine du projet, nous allons mettre à jour le fichier .env avec les identifiants de la base de données, ainsi que nos identifiants Twilio. Ouvrez le fichier et configurez-le comme indiqué ci-dessous :

#DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name

#Twilio Credentials
TWILIO_AUTHY_API_KEY=YOUR_PRODUCTION_API_KEY

Remarque : n'oubliez pas de remplacer les éléments ci-dessous par les identifiants appropriés :

  • db_user : à remplacer par votre nom d'utilisateur de base de données
  • db_password : à remplacer par votre mot de passe de base de données
  • db_name : à remplacer par le nom de votre base de données
  • TWILIO_AUTHY_API_KEY : à remplacer par la clé API de production obtenue à partir du tableau de bord de l'application Authy.

Exécutez ensuite la commande php bin/console doctrine:database:create pour créer une base de données avec la valeur de nom de base de données. Pour l'instant, la base de données ne contient toujours pas de tables. Exécutez la commande suivante pour demander à Doctrine de créer les tables en fonction de l'entité User créée précédemment.

$ php bin/console doctrine:schema:update --force

Capture du numéro de téléphone utilisateur et envoi d'un code de vérification

Revenons au RegistrationController. Mettez à jour le contenu comme suit :

// ./src/Controller/RegistrationController.php

<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use GuzzleHttp\Client;
class RegistrationController extends AbstractController
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;
    /**
     * @var \Doctrine\Common\Persistence\ObjectRepository
     */
    private $userRepository;
 
    public function __construct(EntityManagerInterface $entityManager )
    {
        $this->entityManager = $entityManager;
        $this->userRepository = $entityManager->getRepository('App:User');
    }

    /**
     * @Route("/register/page", name="user_registration", methods={"GET"})
     */
    public function registerAction(Request $request)
    {
        return $this->render('registration/index.html.twig');
    }

    /**
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     * @Route("/register", name="register", methods={"POST"})
     */
    public function registerUsers(Request $request)
    {
        if ( $request->request->get('country_code') ) {

           $authy_api = new \Authy\AuthyApi( getenv('TWILIO_AUTHY_API_KEY') );
           $user      = $authy_api->registerUser( $request->request->get('email'), $request->request->get('phone_number'), $request->request->get('country_code') );

           if ( $user->ok() ) {

               $sms = $authy_api->requestSms( $user->id(), [ "force" => "true" ] );

               if ( $sms->ok() ) {

                   $this->addFlash(
                       'success',
                       $sms->message()
                   );
               }    
              
               $user_params = [
                   'username' => $request->request->get('username'),
                   'email' => $request->request->get('email'),
                   'country_code' => $request->request->get('country_code'),
                   'phone_number' => $request->request->get('phone_number'),
                   'password' => $request->request->get('password'),
                   'authy_id' => $user->id(),
               ];

               $this->get('session')->set('user', $user_params);
           }
       }

       return $this->redirectToRoute('verify_page');

    }

    function updateDatabase($object)
    {
        $this->entityManager->persist($object);
        $this->entityManager->flush();
    }
}

Ce que nous avons ajouté à RegistrationController(), c'est une méthode nommée registerUsers(). Cette méthode utilise le composant HttpFoundation pour capturer les entrées utilisateur à des fins de vérification.

Enfin, nous avons défini les détails des utilisateurs dans la session. L'objectif est d'obtenir ces détails à partir de la session une fois que le numéro de téléphone de l'utilisateur a été vérifié et enregistré dans la base de données. Cela garantit que nous remplissons la base de données uniquement avec les détails des utilisateurs vérifiés.

Création d'un contrôleur d'accueil (HomeController) et validation du code de vérification

Maintenant, nous allons créer un contrôleur qui traite la vérification du numéro de téléphone de l'utilisateur et enregistre les détails dans la base de données. Pour ce faire, exécutez la commande suivante :

$ php bin/console make:controller HomeController

Accédez au nouveau fichier créé par la commande ci-dessus dans /src/Controller/HomeController.php et remplacez son contenu par ce qui suit :

// src/Controller/HomeController.php

<?php

namespace App\Controller;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class HomeController extends AbstractController
{

   

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager )
    {
        $this->entityManager = $entityManager;
    }

    /**
     * @Route("/home", name="home")
     */
    public function index()
    {
        return $this->render('home/index.html.twig', [
            'controller_name' => 'HomeController',
        ]);
    }

    /**
     * @Route("/verify/page", name="verify_page")
     */
    public function verifyCodePage()
    {
        return $this->render('home/verify.html.twig');
    }


    /**
     * @Route("/verify/code", name="verify_code")
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function verifyCode(Request $request, UserPasswordEncoderInterface $encoder)
    {
        try {
            // Get data from session
                       $data = $this->get('session')->get('user');

           $authy_api    = new \Authy\AuthyApi( getenv('TWILIO_AUTHY_API_KEY') );
           $verification = $authy_api->verifyToken( $data['authy_id'], $request->query->get('verify_code') );

           $this->saveUser( $encoder,$data );

           return $this->redirectToRoute('home');


        } catch (\Exception $exception) {
            $this->addFlash(
                'error',
                'Verification code is incorrect'
            );
            return $this->redirectToRoute('verify_page');
        }
    }

    public function saveUser(UserPasswordEncoderInterface $encoder, $data)
    {
        $user = new User();
        $user->setUsername($data['username'])
            ->setEmail($data['email'])
            ->setCountryCode($data['country_code'])
            ->setPhoneNumber($data['phone_number'])
            ->setVerified(true)
            ->setPassword($encoder->encodePassword($user, $data['password']))
            ;

        $this->addFlash(
          'success',
          'You phone number has been verified. Log in here'
        );

        // save user
        $this->entityManager->persist($user);
        $this->entityManager->flush();
    }
}

Nous avons créé une méthode pour vérifier le code saisi par les utilisateurs et nous l'avons enregistré dans la base de données.

Page d'accueil

Mettez également à jour le fichier index.html.twig généré pour HomeController() avec le contenu suivant :

{# ./templates/home/index.html.twig #}

{% extends 'base.html.twig' %}

{% block title %}PHP Phone Verification with Twilio{% endblock %}

{% block body %}

    <div class="container">
        <div class="row">
            <div class="welcome_page">
                <p>Welcome!</p>
                <p>Your phone number has been verified</p>
            </div>
        </div>
    </div>

{% endblock %}

Cette page s'affiche uniquement pour les utilisateurs dont le numéro de téléphone a été vérifié :

Créer une page de vérification

Ici, nous allons créer une page où les utilisateurs peuvent saisir le code à 6 chiffres qu'ils ont reçu et obtenir la réponse appropriée en fonction de l'état de vérification du code. Pour commencer, accédez au dossier template/home et créez un nouveau fichier nommé verify.html.twig, puis collez-y ce qui suit :

{# ./template/home/verify.html.twig #}

{% extends 'base.html.twig' %}

{% block title %}PHP Phone Verification with Twilio{% endblock %}

{% block body %}

    <div class="container">
        <div class="row">
            <div>
                <h2>Verify Phone Number</h2>
            </div>
        </div>

        <div class="row">
            <div class="col-md-6">
                {% for message in app.flashes('success') %}
                    <div class="flash-notice alert alert-success">
                        {{ message }}
                    </div>
                {% endfor %}
                {% for message in app.flashes('error') %}
                    <div class="flash-notice alert alert-danger">
                        {{ message }}
                    </div>
                {% endfor %}

                <div>
                    <p> Enter the 6 digit code here</p>
                </div>

                <form action="{{ url('verify_code') }}" method="get">
                    <div class="form-group">
                        <input type="text" class="form-control" name="verify_code" placeholder="Enter code here">
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-success"> Verify Now</button>
                    </div>
                </form>
            </div>
        </div>
    </div>

{% endblock %}

example de vérification de numéro de téléphone

Si le code de vérification saisi par l'utilisateur est incorrect, un message s'affiche comme indiqué ci-dessous :

erreur lors de la vérification du numéro de téléphone

Si le code est correct, l'utilisateur est redirigé vers la page de connexion et reçoit un message indiquant que le numéro de téléphone a été vérifié. Il peut alors se connecter pour afficher la page protégée :

connexion réussie après vérification

Mise à jour du fichier Security.yaml

Mettez à jour le contenu du fichier ./config/packages/security.yaml comme suit :

security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        our_db_provider:
            entity:
                 class: App\Entity\User
                 property: username
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
#            pattern: ^/
            anonymous: ~
#            http_basic: ~
            provider: our_db_provider
#            anonymous: true

            # activate different ways to authenticate

            # http_basic: true
            # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate

            form_login:
                  login_path: login
                  check_path: login
                  default_target_path: /home
#        secured_area:
#            anonymous: ~
            logout:
                path: /logout
                target: /

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
         - { path: ^/home, roles: ROLE_USER }

 Enfin, ajoutez la route de déconnexion au fichier ./config/routes.yaml :

logout:
    path: /logout

Test de l'application

Nous pouvons désormais tester l'application. N'oubliez pas de redémarrer le serveur s'il ne fonctionne pas avec php bin/console server:run. Accédez à http://localhost:8000 et essayez d'utiliser l'application en enregistrant un utilisateur :

page de sign up Sympfony

Conclusion

Dans ce tutoriel, nous avons appris à configurer un nouveau projet Symfony et à restructurer notre application pour capturer le numéro de téléphone d'un utilisateur, en veillant à ce que le numéro de téléphone soit vérifié avant que les détails de cet utilisateur soient enregistrés dans la base de données. Grâce à l'API Authy de Twilio, notre application peut maintenant inclure des utilisateurs avec des numéros de téléphone vérifiés.

Veuillez noter que le flux d'enregistrement mis en œuvre dans ce tutoriel peut aussi être implémenté pour un projet existant ou un nouveau projet comme décrit ici.

J'espère que vous avez trouvé ce tutoriel utile. N'hésitez pas à explorer le code source ici sur GitHub et à consulter la documentation officielle de Twilio pour en savoir plus sur l'API Authy et les autres ressources.

Olususi Oluyemi est un passionné de technologie, mordu de programmation et accro au développement Web, qui adore découvrir les nouvelles technologies.

Twitter : https://twitter.com/yemiwebby
GitHub : https://github.com/yemiwebby
Site Web : https://yemiwebby.com.ng/