
Compartir:
Antiguo educador de desarrolladores @Vonage. Procedente de PHP, pero no limitado a un solo lenguaje. Un ávido jugador y un entusiasta de Raspberry pi. A menudo se le encuentra practicando escalada en rocódromo.
Verify Registros de usuarios con Symfony
Tiempo de lectura: 26 minutos
Los usuarios que se registran con información falsa pueden ser una plaga, especialmente cuando se registran con números de teléfono que esperas que sean localizables. Verify API de Vonage ofrece una solución para esto al permitirte confirmar que el número de teléfono es correcto y pertenece al usuario. La API toma un número de teléfono, envía un código pin a ese número de teléfono y espera que sea retransmitido a través de la fuente correcta.
En este tutorial, ampliarás un sistema básico de autenticación de usuarios existente, integrado en Symfony 5implementando la autenticación multifactor con la Verify API de Vonage (Anteriormente Nexmo Verify API).
Puede encontrar el código terminado en la rama end-tutorial en este repositorio repositorio GitHub.
Requisitos previos
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.
Primeros pasos
Clonación del repositorio
Clone el repositorio existente copiando este comando en su Terminal, y luego cambie de directorio en el proyecto:
Credenciales de la base de datos
Dentro del directorio symfony/ cree un archivo .env.local para almacenar las variables de entorno local que no desea que se envíen a su repositorio. Por ejemplo, su código necesita saber el método y las credenciales para conectarse a su base de datos. Copie la siguiente línea en su fichero .env.local archivo:
El ejemplo anterior contiene varios datos necesarios para conectarse a la base de datos.
postgresqles el protocolo utilizado para conectarse.userypasswordes el conjunto de credenciales utilizadas para autenticarse en la base de datos.postgreses la dirección del dominio.5432es el puerto para conectarse a la base de datos.testes el nombre de la base de datos.
Instalación de bibliotecas de terceros
Es necesario instalar varias librerías de terceros ya definidas en este proyecto, tanto a través de Composer como de paquetes yarn.
Ejecute los tres comandos siguientes:
Ejecución de Docker
Para este tutorial y para asegurar que los requisitos del servidor son los mismos para todos, se ha configurado un Docker config para utilizar contenedores con configuraciones predefinidas.
Dentro del directorio docker/ ejecute el directorio:
Una vez que el comando docker-compose debería poder ver la siguiente confirmación de que los tres contenedores se están ejecutando:

Ejecución de migraciones de bases de datos
En su terminal, conéctese al contenedor Docker PHP ejecutando el siguiente comando:
Para crear las tablas de la base de datos y ejecutar todos los archivos que se encuentran en symfony/src/migrations/, ejecute el siguiente comando:
Este comando crea una tabla de base de datos de usuarios con las columnas pertinentes.
Probar el registro
Vaya a: http://localhost:8081/register en su navegador, y verá una página de registro similar a la que se ve en la siguiente imagen:

Introduzca un número de teléfono de prueba y una contraseña. Al enviar el formulario, debería ver la página del perfil.
Nota: El uso de su número de teléfono aquí le creará un nuevo usuario, así que prepárese para eliminar ese registro de la tabla de la base de datos de usuarios.
Si has llegado a este punto, ya estás preparado y listo para este tutorial.
Instalación del SDK PHP de Nexmo
Nota: Se requiere que los comandos de Composer se ejecuten desde dentro del contenedor docker PHP. Desde la sección Ejecución de las Migraciones de Bases de Datos usted accedió remotamente a la terminal del contenedor docker PHP. Si abandona la sesión de terminal del contenedor, puede volver a ella ejecutando
docker-compose exec php bashdesde el directoriodocker/directorio.
El tutorial utiliza Verify API de Vonage. La forma más fácil de usarla en PHP es instalar nuestro SDK de PHP.
Para instalarlo ejecuta:
En tu panel de desarrollador de Vonageencontrarás "Tus credenciales de API", anótalas.
Dentro del directorio symfony/añada las dos líneas siguientes a su archivo .env.local (sustituyendo api_key y api_secret por su clave y su secreto):
Cree un nuevo directorio llamado Util dentro de symfony/src/y dentro de ese directorio cree un nuevo archivo llamado VonageUtil.php.
Esta clase Utility manejará cualquier código que utilice el SDK PHP de Nexmo. El ejemplo de abajo no hará nada más que crear un objeto NexmoClient con las credenciales de autenticación que has guardado en .env.local. Copia el ejemplo de abajo en tu recién creado 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']
)
);
}
}
Creación de una página de verificación
Verificar nuevas columnas
Se necesitan nuevas propiedades dentro de la User para procesar correctamente la verificación de un nuevo usuario.
En su terminal Docker, escriba
Vas a crear tres nuevas propiedades. Así que siguiendo los pasos del comando anterior, introduzca los valores como se indica a continuación:
Donde pide un tipo de Entidad 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: falseLa dirección countryCode es necesario para determinar a qué país pertenece el número de teléfono para que Verify API realice la llamada correctamente.
La dirección verificationRequestId ID que la Verify API devuelve inicialmente al servidor y que, junto con el código de verificación, verifica al usuario.
La propiedad verified permite al sistema determinar si un usuario se ha verificado o no.
Deberá ejecutar lo siguiente para generar un nuevo archivo de migración con estos cambios en la base de datos.
El comando anterior detecta cualquier cambio realizado en los archivos Entity de su proyecto. A continuación, convierte estos cambios en consultas SQL que, cuando se ejecutan como una migración, persistirán los cambios en su base de datos.
Los archivos de migración generados se encuentran dentro de symfony/src/Migrations si desea ver los próximos cambios en la base de datos.
Si está satisfecho con estos cambios, ejecute el siguiente comando para guardarlos en la base de datos.
Incluir el código de país en el registro de usuario
Abra su RegistrationFormType clase en symfony/src/Form/RegistrationFormType.php. Añade un nuevo include para el tipo de formulario de la clase ChoiceType en la parte superior del archivo:
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;En el mismo archivo, realice los siguientes cambios:
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"
+ ]
+ ]
+ )
Las dos únicas opciones para elegir, en esta demo, son GB y EE.UU.. No obstante, se admiten otros países. Puede encontrar la lista ISO de países junto con su código de país correspondiente aquí: ISO.org. Por favor, asegúrese de que el valor en la matriz es el estándar ISO de dos caracteres para el país de su elección.
En symfony/templates/registration/register.html.twig encontrará una fila de formulario para phoneNumber. Encima añada el countryCode equivalente:
+ {{ form_row(registrationForm.countryCode) }}
{{ form_row(registrationForm.phoneNumber) }}Si tiene Docker en ejecución, puede consultar la página de registro en http://localhost:8081/register y ver una página similar a la que se muestra a continuación:

Le invitamos a utilizar el formulario de inscripción, pero le rogamos que no utilice su número o esté preparado para borrar esa inscripción de la tabla de la base de datos de usuarios.
Verify Phone Number Is Valid (Verificar la validez del número de teléfono)
Al llamar a un número para proporcionar un código de verificación, el sistema requiere una marca. Para ello, en el symfony/ directorio, abra .env.local y añada una nueva línea. Sustituye VerifyWithVonage por la empresa/marca que estés representando para la verificación:
Para verificar que un número es un número de teléfono válido, tienes que comprobar que el número de teléfono tiene el formato correcto y es válido para la región (código de país) que has proporcionado. Para ello, va a utilizar el puerto PHP de Giggsey de la libphonenumber de Google.
Ejecute el siguiente comando para instalar esta biblioteca.
Abra su VonageUtil que se encuentra en symfony/src/Util. Dentro de esta clase, necesita añadir un método para validar el número de teléfono y el código de país. Este método también comprueba si el número de teléfono coincide con el de esa región. Si lo es, el método devolverá el número de teléfono en un formato internacionalizado. Copia lo siguiente en esta clase:
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
);
}
El método utiliza el objeto $user para hacer lo siguiente:
analizar el número de teléfono y el código de país mediante la biblioteca
libphonenumberbiblioteca,comprueba que se trata de un número válido para la región indicada (por código de país).
Si el número y la región son válidos, formatea el número de teléfono en uno internacionalizado.
Envío de una llamada de verificación al registrarse
Cree un método en VonageUtil que hará uso de este método privado. Este nuevo método público se asegurará de que la entrada del usuario es válida y, utilizando la Verify API, iniciará el proceso de verificación.
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);
}
Para salvar el salto entre diferentes archivos, vas a añadir otro método a esta clase de utilidad. Este nuevo método le permitirá obtener el request_id que se devuelve dentro del objeto Verification objeto cuando sea necesario.
public function getRequestId(Verification $verification): ?string
{
$responseData = $verification->getResponseData();
if (empty($responseData)) {
return null;
}
return $responseData['request_id'];
}
Estos nuevos métodos que has creado dentro de la clase VonageUtil no hacen nada en este momento. Para que sean útiles, necesitarás llamar a la funcionalidad desde dentro de la clase RegistrationController así que abre este controlador que se encuentra dentro de symfony/src/Controller/.
En primer lugar, tendrás que inyectar el archivo VonageUtil en el RegistrationController como un servicio:
+use App\Util\VonageUtil;
class RegistrationController extends AbstractController
{
+ /** @var VonageUtil */
+ protected $vonageUtil;
+
+ public function __construct(VonageUtil $vonageUtil)
+ {
+ $this->vonageUtil = $vonageUtil;
+ }
Dentro de su register encuentre las tres líneas $entityManager líneas y añada la funcionalidad a setVerified() como false como se muestra a continuación:
+$user->setVerified(false);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
A continuación $entityManager->flush() hacer la solicitud para iniciar el proceso de verificación. Para ello, añade las dos líneas siguientes: la primera llama al método VonageUtil para enviar la solicitud de verificación, la segunda línea analiza la respuesta, y guarda el archivo requestId como una variable:
$verification = $this->vonageUtil->sendVerification($user);
$requestId = $this->vonageUtil->getRequestId($verification);
En la clase encuentra el siguiente código:
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
Sustitúyalo por una funcionalidad que primero compruebe si ha configurado el requestId, guarda el requestId al usuario, y luego autentique al usuario:
if ($requestId) {
$user->setVerificationRequestId($requestId);
$entityManager->flush();
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}
Verify Form
En su terminal Docker, ejecute el siguiente comando y, a continuación, siga las instrucciones introduciendo los valores que se indican:
- Name: VerifyFormType
- Entity name: User
Al enviar esto, usted debe tener una nueva clase dentro de symfony/src/Form/ llamada VerifyFormType.php. Se necesitan algunos cambios para que este formulario funcione como se espera:
Sustituya las siguientes líneas:
->add('phoneNumber')
->add('roles')
->add('password')
->add('countryCode')
->add('verificationRequestId')
->add('verified')
con:
->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.',
]),
],
])
Acaba de eliminar campos de formulario que no deberían actualizarse, ha añadido un nuevo campo no mapeado (un campo no mapeado a la tabla de la base de datos) llamado verificationCode. El verificationCode se envía a la API para verificar el número de teléfono.
En la parte superior del archivo, añade tres includes más. Estos son los nombres completos de las clases utilizadas en el código de ejemplo anterior.
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank; Verificación del código
En tu clase VonageUtil necesitas un nuevo método que llame a la API de Vonage para verificar que el código proporcionado por el usuario sea válido. Coloca el siguiente ejemplo en tu clase VonageUtil clase:
public function verify(string $requestId, string $verificationCode)
{
$verification = new Verification($requestId);
return $this->client->verify()->check($verification, $verificationCode);
}
Cree un nuevo archivo de plantilla dentro de symfony/templates/registration llamado 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 %}
Dentro de su RegistrationController se necesita un nuevo método para mostrar la plantilla anterior y gestionar el envío del formulario.
En primer lugar, incluya el VerifyFormType y una clase de Vonage Verification en la parte superior:
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;A continuación, crea el nuevo método:
/**
* @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(),
]);
}
En este punto del tutorial, el proceso de registro es el siguiente:
en
/registerintroduzca un número de teléfono y una contraseñase recibe una llamada telefónica citando un número de cuatro cifras
redirigido a
/profile
Actualmente no se comprueba si el usuario está verificado.
Aplicación de la verificación
En este paso, vas a implementar un suscriptor de eventos que compruebe si el usuario se ha verificado antes de permitirle acceder a las páginas seguras. Si el usuario no está verificado, se le redirige de nuevo al formulario de verificación para que introduzca su código de verificación.
En su Terminal Docker, escriba el comando para hacer un nuevo suscriptor de eventos y siga las instrucciones de la pantalla con los siguientes valores:
- Class name: `VerifiedUserSubscriber`
- Event to subscribe to: `kernel.controller`La siguiente imagen muestra un ejemplo de lo que se introduce para completar el comando:

Abrir VerifiedUserSubscriber que se encuentra en symfony/src/EventSubscriber/.
Añade las comprobaciones y restricciones al método onKernelController.
Primero debe comprobar si el usuario está intentando acceder a la URL del perfil o no. Si no es así, devuélvele el mensaje y permítele acceder a la página de destino:
if (!preg_match('/^\/profile/i', $event->getRequest()->getPathInfo())) {
return;
}
Ahora quiere comprobar si el usuario se ha autenticado o no. Para ello, inyecta el servicio tokenStorage en el suscriptor de eventos. Mientras haces esto, para ahorrar tiempo, inyecta el servicio router para la funcionalidad después de la comprobación del usuario.
En la parte superior del archivo, junto con las otras inclusiones de clase, añada lo siguiente:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;A continuación, inyecta los dos servicios a través del constructor de la clase y establece estos servicios como propiedades dentro de la clase:
/** @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;
}
Dentro de la función onKernelController debajo de la comprobación de si el usuario está accediendo al perfil o no, añade lo siguiente, que comprueba si el usuario se ha autenticado y si está verificado:
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;
}
Por último, si el usuario se encuentra en este punto, es un usuario conectado, está intentando acceder a la sección de perfil y no está verificado. Así que redirigirlos a la ruta de verificación para asegurarse de que tienen que verificar su cuenta antes de continuar.
$route = $this->router->generate('app_register_verify');
$event->setController(function () use ($route) {
return new RedirectResponse($route);
});
Póngalo a prueba
El método completo para probar este tutorial ahora es registrar una nueva Account en regístrese en con un número de teléfono válido. Se le llevará a verificar cuando envíe este formulario.
El número de teléfono recibirá una llamada con un código de cuatro dígitos que deberá introducir en el formulario de la página de verificación. Ahora el perfil es accesible.
Ahora has integrado un proceso de registro de dos pasos en tu aplicación Symfony usando Verify API de Vonage. El ejemplo proporcionado es sólo una de las muchas maneras de usar Verify API. Ya sea a través de la autenticación multifactor durante el inicio de sesión o la verificación, el número de teléfono de un usuario es válido para garantizar que estén localizables.
Si este tutorial ha despertado tu interés en nuestra Verify API, pero PHP no es el lenguaje de tu elección, puedes encontrar otros tutoriales en varios lenguajes o servicios aquí en el blog de Vonage, como por ejemplo:
Añadir autenticación de 2 factores a WordPress con Nexmo Verify API
Verify Phone Numbers with Node-RED (Verificar números de teléfono con Node-RED)
Creación de una aplicación de registro con Verify API de Nexmo y Koa.js
El código completo de este tutorial se encuentra en el repositorio repositorio GitHub en la end-tutorial rama.
