
Partager:
Ancien développeur éducateur @Vonage. Issu d'une formation PHP, mais pas limité à un seul langage. Joueur passionné et adepte du Raspberry pi. On le trouve souvent en train de faire du bloc dans des salles d'escalade.
Verify User Registrations with Symfony (Vérifier les enregistrements d'utilisateurs avec Symfony)
Temps de lecture : 26 minutes
Les utilisateurs qui s'inscrivent avec de fausses informations peuvent être nuisibles, ce qui est particulièrement le cas lorsqu'ils s'inscrivent avec des numéros de téléphone dont vous vous attendez à ce qu'ils soient joignables. L'API Verify de Vonage de Vonage apporte une solution à ce problème en vous permettant de confirmer que le numéro de téléphone est correct et qu'il appartient à l'utilisateur. L'API prend un numéro de téléphone, envoie un code pin à ce numéro de téléphone et s'attend à ce qu'il soit relayé par la bonne source.
Dans ce tutoriel, vous allez étendre un système d'authentification utilisateur de base existant, intégré à Symfony 5en implémentant l'authentification multi-facteurs avec l'API Verify API de Vonage (Anciennement Nexmo Verify API).
Vous pouvez trouver le code terminé sous la branche end-tutorial dans ce dépôt dépôt GitHub.
Conditions préalables
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Pour commencer
Cloner le référentiel
Clonez le dépôt existant en copiant cette commande dans votre Terminal, puis changez de répertoire dans le projet :
Références de la base de données
Dans le répertoire symfony/ créez un fichier .env.local pour stocker vos variables d'environnement locales que vous ne souhaitez pas intégrer à votre référentiel. Par exemple, votre code a besoin de connaître la méthode et les identifiants pour se connecter à votre base de données. Copiez la ligne suivante dans votre fichier .env.local dans votre fichier :
L'exemple ci-dessus contient plusieurs informations nécessaires pour se connecter à la base de données.
postgresqlest le protocole utilisé pour se connecter.useretpasswordest l'ensemble des informations d'identification utilisées pour s'authentifier auprès de la base de données.postgresest l'adresse du domaine.5432est le port de connexion à la base de données.testest le nom de la base de données.
Installation de bibliothèques tierces
Plusieurs bibliothèques tierces déjà définies dans ce projet doivent être installées, à la fois via Composer et les paquets yarn.
Exécutez les trois commandes suivantes :
Exécution de Docker
Pour ce tutoriel et pour s'assurer que les exigences du serveur sont les mêmes pour tout le monde, une configuration Docker a été mise en place pour utiliser des conteneurs avec des configurations prédéfinies.
Dans le répertoire docker/ exécuter :
Une fois la commande docker-compose est terminée, vous devriez voir la confirmation suivante que les trois conteneurs sont en cours d'exécution :

Exécution des migrations de bases de données
Dans votre terminal, connectez-vous au conteneur Docker PHP en exécutant la commande suivante :
Pour créer les tables de la base de données et exécuter tous les fichiers trouvés dans symfony/src/migrations/, exécutez la commande suivante :
Cette commande crée une table dans la base de données des utilisateurs avec les colonnes appropriées.
Test de l'enregistrement
Allez sur : http://localhost:8081/register dans votre navigateur, et vous verrez une page d'enregistrement similaire à celle que vous voyez dans l'image ci-dessous :

Saisissez un numéro de téléphone et un mot de passe de test. Une fois le formulaire envoyé, vous devriez voir la page de profil !
Note : L'utilisation de votre numéro de téléphone ici créera un nouvel utilisateur : L'utilisation de votre numéro de téléphone ici créera un nouvel utilisateur. Soyez donc prêt à supprimer cet enregistrement de la table de la base de données des utilisateurs.
Si vous en êtes là, vous êtes prêt pour ce tutoriel.
Installation du SDK PHP Nexmo
Note : Les commandes de Composer doivent être exécutées à partir du conteneur PHP Docker : Les commandes de Composer doivent être exécutées depuis le conteneur PHP. À partir de la page Exécuter les migrations de bases de données vous avez accédé à distance au terminal du conteneur PHP. Si vous quittez la session du terminal du conteneur, vous pouvez y revenir en exécutant les commandes suivantes
docker-compose exec php bashdepuis le répertoiredocker/dans le répertoire.
Ce tutoriel utilise l'API Verify de Vonage. La façon la plus simple d'utiliser cette API en PHP est d'installer notre SDK PHP.
Pour l'installer, exécutez :
Dans votre tableau de bord du développeur Vonagevous trouverez "Your API credentials", notez-les.
Dans le répertoire symfony/ajoutez les deux lignes suivantes à votre fichier .env.local (en remplaçant api_key et api_secret par votre clé et votre secret) :
Créer un nouveau répertoire appelé Util inside symfony/src/et à l'intérieur de ce répertoire, créez un nouveau fichier appelé VonageUtil.php.
Cette classe utilitaire gère tout code qui utilise le SDK PHP Nexmo. L'exemple ci-dessous ne fera rien d'autre que de créer un objet NexmoClient avec les informations d'authentification que vous avez sauvegardées dans le fichier .env.local. Copiez l'exemple ci-dessous dans votre nouvelle classe VonageUtil.php:
<?php
// symfony/src/Util/VonageUtil.php
namespace App\Util;
use App\Entity\User;
use Nexmo\Client as NexmoClient;
use Nexmo\Client\Credentials\Basic;
use Nexmo\Verify\Verification;
class VonageUtil
{
/** @var NexmoClient */
protected $client;
public function __construct()
{
$this->client = new NexmoClient(
new Basic(
$_ENV['VONAGE_API_KEY'],
$_ENV['VONAGE_API_SECRET']
)
);
}
}
Création d'une page de vérification
Verify New Columns (Vérifier les nouvelles colonnes)
De nouvelles propriétés sont nécessaires à l'intérieur de l'entité User pour traiter correctement la vérification d'un nouvel utilisateur.
Dans votre terminal Docker, tapez
Vous allez créer trois nouvelles propriétés. En suivant les étapes de la commande ci-dessus, entrez les valeurs indiquées ci-dessous :
Lorsqu'il demande un type d'entité User
- New property name: countryCode
- Type: string
- Length: 2
- Is Nullable: false- New property name: verificationRequestId
- Type: string
- Length: 255
- Is Nullable: true- New property name: verified
- Type: boolean
- Is Nullable: falseLe countryCode est nécessaire pour déterminer à quel pays appartient le numéro de téléphone afin que l'API Verify puisse passer l'appel avec succès.
L'identifiant verificationRequestId l'identifiant que l'API Verify renvoie initialement au serveur et qui, associé au code de vérification, permet de vérifier l'identité de l'utilisateur.
Cette propriété permet au système de déterminer si un utilisateur a été vérifié ou non. verified permet au système de déterminer si un utilisateur a procédé à une vérification ou non.
Vous devrez exécuter la procédure suivante pour générer un nouveau fichier de migration contenant les modifications apportées à la base de données.
La commande ci-dessus détecte toutes les modifications apportées aux fichiers Entity de votre projet. Elle convertit ensuite ces modifications en requêtes SQL qui, lorsqu'elles sont exécutées en tant que migration, conservent les modifications dans votre base de données.
Les fichiers de migration générés se trouvent à l'intérieur de symfony/src/Migrations si vous souhaitez voir les changements à venir dans la base de données.
Si vous êtes satisfait de ces modifications, exécutez la commande ci-dessous pour les conserver dans la base de données.
Inclure le code pays dans l'enregistrement de l'utilisateur
Ouvrez votre RegistrationFormType classe dans symfony/src/Form/RegistrationFormType.php. Ajoutez un nouvel include pour le type de formulaire de la classe ChoiceType en haut du fichier :
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;Dans le même fichier, apportez les modifications suivantes :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
+ ->add(
+ 'countryCode',
+ ChoiceType::class,
+ [
+ 'label' => false,
+ 'attr' => [
+ 'class' => 'form-control form-control-lg'
+ ],
+ 'choices' => [
+ "United Kingdom" => "GB",
+ "United States" => "US"
+ ]
+ ]
+ )
Les deux seules options possibles, dans cette démo, sont la Grande-Bretagne et les États-Unis. Cependant, d'autres pays sont pris en charge. Vous trouverez la liste ISO des pays ainsi que le code pays correspondant ici : ISO.org. Veillez à ce que la valeur du tableau corresponde à la norme ISO à deux caractères pour le pays de votre choix.
A l'intérieur symfony/templates/registration/register.html.twig vous trouverez une ligne de formulaire pour phoneNumber. Au-dessus, ajoutez le countryCode équivalent :
+ {{ form_row(registrationForm.countryCode) }}
{{ form_row(registrationForm.phoneNumber) }}Si vous avez Docker en cours d'exécution, vous pouvez consulter la page d'inscription à l'adresse suivante http://localhost:8081/register et voir une page similaire à celle présentée ci-dessous :

Vous pouvez utiliser le formulaire d'inscription, mais n'utilisez pas votre numéro ou soyez prêt à supprimer cette inscription du tableau de la base de données des utilisateurs.
Verify Le numéro de téléphone est valide
Lorsqu'on appelle un numéro pour fournir un code de vérification, le système demande un nom de marque. Ainsi, dans le symfony/ ouvrez .env.local et ajoutez une nouvelle ligne. Remplacez VerifyWithVonage par le nom de l'entreprise ou de la marque que vous représentez pour la vérification :
Pour vérifier qu'un numéro est bien un numéro de téléphone valide, vous devez vous assurer que le format du numéro est correct et qu'il est valide pour la région (indicatif de pays) que vous avez indiquée. Pour cela, vous allez utiliser le portage PHP de Giggsey de la librairie Google libphonenumber.
Exécutez la commande ci-dessous pour installer cette bibliothèque.
Ouvrez votre VonageUtil qui se trouve dans symfony/src/Util. Dans cette classe, vous devez ajouter une méthode pour valider le numéro de téléphone et le code du pays. Cette méthode vérifie également si le numéro de téléphone correspond à la région concernée. Si c'est le cas, la méthode renvoie le numéro de téléphone dans un format internationalisé. Copiez les éléments suivants dans cette classe :
private function getInternationalizedNumber(User $user): ?string
{
$phoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$phoneNumberObject = $phoneNumberUtil->parse(
$user->getPhoneNumber(),
$user->getCountryCode()
);
if (!$phoneNumberUtil->isValidNumberForRegion(
$phoneNumberObject,
$user->getCountryCode())
) {
return null;
}
return $phoneNumberUtil->format(
$phoneNumberObject,
\libphonenumber\PhoneNumberFormat::INTERNATIONAL
);
}
La méthode utilise l'objet $user pour effectuer les opérations suivantes :
analyser le numéro de téléphone et l'indicatif de pays à l'aide de la bibliothèque
libphonenumberbibliothèque,vérifie qu'il s'agit d'un numéro valide pour la région indiquée (par l'indicatif de pays).
Si le numéro et la région sont valides, il formate le numéro de téléphone en un numéro internationalisé.
Envoi d'un appel de vérification lors de l'enregistrement
Créez une méthode dans VonageUtil qui utilisera cette méthode privée. Cette nouvelle méthode publique s'assurera que les données saisies par l'utilisateur sont valides et, à l'aide de l'API Verify, lancera le processus de vérification.
public function sendVerification(User $user)
{
// Retrieves the internationalized number using the previous util method created.
$internationalizedNumber = $this->getInternationalizedNumber($user);
// If the number is not valid or valid for the country code provided, then return null
if (!$internationalizedNumber) {
return null;
}
// Initialize the verification process with Vonage
$verification = new Verification(
$internationalizedNumber,
$_ENV['VONAGE_BRAND_NAME'],
['workflow_id' => 3]
);
return $this->client->verify()->start($verification);
}
Pour éviter de passer d'un fichier à l'autre, vous allez ajouter une nouvelle méthode à cette classe utilitaire. Cette nouvelle méthode vous permettra d'obtenir la valeur request_id qui est retourné dans l'objet Verification lorsque cela est nécessaire.
public function getRequestId(Verification $verification): ?string
{
$responseData = $verification->getResponseData();
if (empty($responseData)) {
return null;
}
return $responseData['request_id'];
}
Ces nouvelles méthodes que vous avez créées dans la classe VonageUtil ne font rien pour l'instant. Pour qu'elles soient utiles, vous devrez appeler la fonctionnalité à partir de la classe RegistrationController Ouvrez donc ce contrôleur qui se trouve à l'intérieur de symfony/src/Controller/.
Tout d'abord, vous devez injecter le VonageUtil dans le RegistrationController en tant que service :
+use App\Util\VonageUtil;
class RegistrationController extends AbstractController
{
+ /** @var VonageUtil */
+ protected $vonageUtil;
+
+ public function __construct(VonageUtil $vonageUtil)
+ {
+ $this->vonageUtil = $vonageUtil;
+ }
Dans votre register trouvez les trois lignes $entityManager et ajoutez la fonctionnalité à setVerified() comme false, comme indiqué ci-dessous :
+$user->setVerified(false);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
Ci-dessous $entityManager->flush() faire la demande pour initier le processus de vérification. Ajoutez donc les deux lignes suivantes : la première appelle la méthode VonageUtil pour envoyer la demande de vérification, la deuxième ligne analyse la réponse et enregistre la variable requestId en tant que variable :
$verification = $this->vonageUtil->sendVerification($user);
$requestId = $this->vonageUtil->getRequestId($verification);
Dans la classe, vous trouverez le code suivant :
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
Remplacez-la par une fonctionnalité qui vérifie d'abord si vous avez défini le paramètre requestIdenregistre l'élément requestId à l'utilisateur, puis l'authentifie :
if ($requestId) {
$user->setVerificationRequestId($requestId);
$entityManager->flush();
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}
Verify Form
Dans votre terminal Docker, exécutez la commande ci-dessous, puis suivez les instructions en entrant les valeurs indiquées :
- Name: VerifyFormType
- Entity name: User
En soumettant ceci, vous devriez avoir une nouvelle classe à l'intérieur de symfony/src/Form/ appelée VerifyFormType.php. Certaines modifications sont nécessaires pour que ce formulaire fonctionne comme prévu :
Remplacer les lignes suivantes :
->add('phoneNumber')
->add('roles')
->add('password')
->add('countryCode')
->add('verificationRequestId')
->add('verified')
avec :
->add('verificationCode', TextType::class, [
'mapped' => false,
'attr' => [
'class' => 'form-control form-control-lg'
],
'constraints' => [
new NotBlank([
'message' => 'Please enter a verification code',
]),
new Length([
'min' => 4,
'max' => 4,
'minMessage' => 'The verification code is a 4 digit number.',
]),
],
])
Vous venez de supprimer des champs de formulaire qui ne doivent pas être mis à jour, d'ajouter un nouveau champ non mappé (un champ non mappé à la table de la base de données) appelé verificationCode. Le champ verificationCode est envoyé à l'API pour vérifier le numéro de téléphone.
En haut du fichier, ajoutez trois includes supplémentaires. Il s'agit des noms pleinement qualifiés des classes utilisées dans l'exemple de code ci-dessus.
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank; Verify the Code (vérification du code)
Dans votre classe VonageUtil vous avez besoin d'une nouvelle méthode pour appeler l'API de Vonage afin de vérifier que le code fourni par l'utilisateur est valide. Insérez l'exemple ci-dessous dans votre VonageUtil classe :
public function verify(string $requestId, string $verificationCode)
{
$verification = new Verification($requestId);
return $this->client->verify()->check($verification, $verificationCode);
}
Créer un nouveau fichier modèle à l'intérieur de symfony/templates/registration appelé verify.html.twig
{% extends 'base.html.twig' %}
{% block title %}Verify{% endblock %}
{% block body %}
<div class="row justify-content-center align-items-center h-100">
<div class="col col-sm-6 col-md-6 col-lg-4 col-xl-3">
<h1 class="h3 mb-3 font-weight-normal">Verify</h1>
{{ form_start(verificationForm) }}
<div class="form-group">
{{ form_row(verificationForm.verificationCode) }}
</div>
<button class="btn btn-info btn-lg btn-block" type="submit">Verify</button>
{{ form_end(verificationForm) }}
</div>
<div>
{% endblock %}
Dans votre RegistrationController une nouvelle méthode est nécessaire pour afficher le modèle ci-dessus et gérer la soumission du formulaire.
Tout d'abord, il faut inclure le VerifyFormType et une classe Vonage Verification au début :
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
+use App\Form\VerifyFormType;
+use Nexmo\Verify\Verification;Ensuite, créez la nouvelle méthode :
/**
* @Route("/register/verify", name="app_register_verify")
*/
public function verify(Request $request): Response
{
$user = $this->getUser();
$form = $this->createForm(VerifyFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$verify = $this->vonageUtil->verify(
$user->getVerificationRequestId(),
$form->get('verificationCode')->getData()
);
if ($verify instanceof Verification) {
$user->setVerificationRequestId(null);
$user->setVerified(true);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();
return $this->redirectToRoute('profile');
}
}
return $this->render('registration/verify.html.twig', [
'verificationForm' => $form->createView(),
]);
}
A ce stade du tutoriel, la procédure d'enregistrement est la suivante :
sur
/registerentrer un numéro de téléphone et un mot de passeun appel téléphonique est reçu citant un numéro à quatre chiffres
redirigé vers
/profile
Il n'y a actuellement aucun contrôle pour s'assurer que l'utilisateur est vérifié.
Mise en œuvre de la vérification
Dans cette étape, vous allez mettre en œuvre un abonné d'événement qui vérifie si l'utilisateur s'est vérifié avant de lui permettre d'accéder aux pages sécurisées. Si l'utilisateur n'est pas vérifié, il est redirigé vers le formulaire de vérification pour saisir son code de vérification.
Dans votre terminal Docker, tapez la commande pour créer un nouvel abonné d'événement et suivez les instructions à l'écran avec les valeurs suivantes :
- Class name: `VerifiedUserSubscriber`
- Event to subscribe to: `kernel.controller`L'image ci-dessous montre un exemple de ce qui est saisi pour compléter la commande :

Ouvrir VerifiedUserSubscriber qui se trouve dans symfony/src/EventSubscriber/.
Ajoutez les contrôles et les restrictions à la méthode onKernelController.
Vous devez d'abord vérifier si l'utilisateur essaie d'accéder à l'URL du profil ou non. Si ce n'est pas le cas, revenez en arrière et permettez-lui de se rendre à sa page de destination :
if (!preg_match('/^\/profile/i', $event->getRequest()->getPathInfo())) {
return;
}
Vous voulez maintenant vérifier si l'utilisateur s'est authentifié ou non. Pour ce faire, injectez le service tokenStorage dans l'abonné à l'événement. Ce faisant, pour gagner du temps, injectez le service router pour la fonctionnalité après la vérification de l'utilisateur.
En tête du fichier, avec les autres inclusions de classe, ajoutez ce qui suit :
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;Ensuite, injectez les deux services via le constructeur de la classe et définissez ces services en tant que propriétés dans la classe :
/** @var RouterInterface */
protected $router;
/** @var TokenStorageInterface */
private $tokenStorage;
/**
* @param RouterInterface $router
* @param TokenStorageInterface $tokenStorage
*/
public function __construct(
RouterInterface $router,
TokenStorageInterface $tokenStorage
) {
$this->router = $router;
$this->tokenStorage = $tokenStorage;
}
Dans la fonction onKernelController sous la vérification de l'accès ou non de l'utilisateur au profil, ajoutez ce qui suit, qui vérifie si l'utilisateur s'est authentifié et s'il est vérifié :
if (null === $user = $this->tokenStorage->getToken()->getUser()) {
return;
}
// Check whether the user is verified, if they are, allow them to continue to their destination.
if ($user->getVerified()) {
return;
}
Enfin, si l'utilisateur se trouve à ce stade, il est connecté, il essaie d'accéder à la section du profil et son compte n'est pas vérifié. Redirigez-le donc vers le chemin de vérification pour vous assurer qu'il doit vérifier son Account avant de continuer.
$route = $this->router->generate('app_register_verify');
$event->setController(function () use ($route) {
return new RedirectResponse($route);
});
Testez-le !
La méthode complète pour tester ce tutoriel maintenant est d'enregistrer un nouveau compte sur s'inscrire avec un numéro de téléphone valide. Vous êtes invité à Verify lorsque vous soumettez ce formulaire.
Le numéro de téléphone recevra un appel citant un code à quatre chiffres, que vous devrez saisir dans le formulaire de la page de vérification. Maintenant le profil est désormais accessible.
Vous avez maintenant intégré un processus d'enregistrement en deux étapes dans votre application Symfony à l'aide de l'API Verify de Vonage. L'exemple fourni n'est qu'une des nombreuses façons d'utiliser l'API Verify. Que ce soit via l'authentification multifactorielle lors de la connexion ou la vérification, le numéro de téléphone d'un utilisateur est valide pour s'assurer qu'il est joignable.
Si ce tutoriel a éveillé votre intérêt pour notre Verify API, mais que PHP n'est pas le langage de votre choix, d'autres tutoriels dans divers langages ou services peuvent être trouvés ici sur le blog de Vonage, comme par exemple :
Ajouter l'authentification à 2 facteurs à WordPress avec Nexmo Verify API
Construire une application de check-in avec l'API Verify de Nexmo et Koa.js
Le code complet de ce tutoriel est disponible sur le dépôt dépôt GitHub dans la branche end-tutorial branche.
