
Compartilhar:
Ex-educador de desenvolvedores na @Vonage. Tenho formação em PHP, mas não me limito a uma única linguagem. Sou um jogador ávido e entusiasta do Raspberry Pi. Costumo praticar escalada em rocha em centros de escalada indoor.
Verificar registros de usuários com o Symfony
Tempo de leitura: 25 minutos
Usuários que se cadastram com informações falsas podem ser um incômodo, especialmente quando se cadastram com números de telefone pelos quais se espera que possam ser contatados. A Verify API da Vonage oferece uma solução para isso, permitindo que você confirme se o número de telefone está correto e se pertence ao usuário. A API recebe um número de telefone, envia um código PIN para esse número e espera que ele seja repassado pela fonte correta.
Neste tutorial, você vai ampliar um sistema básico de autenticação de usuários já existente, integrado ao no Symfony 5, implementando a autenticação multifatorial com a Verify API do Vonage Verify (anteriormente conhecida como Nexmo Verify API).
Você pode encontrar o código finalizado na end-tutorial ramo neste repositório do GitHub.
Pré-requisitos
Introdução
Clonando o repositório
Clone o repositório existente copiando este comando no Terminal e, em seguida, acesse o diretório do projeto:
Credenciais do banco de dados
Dentro do symfony/ diretório, crie um .env.local arquivo para armazenar suas variáveis de ambiente locais que você não deseja enviar para o repositório. Por exemplo, seu código precisa saber o método e as credenciais para se conectar ao seu banco de dados. Copie a seguinte linha no seu .env.local arquivo:
O exemplo acima contém várias informações necessárias para se conectar ao banco de dados.
postgresqlé o protocolo utilizado para se conectar.userepasswordé o conjunto de credenciais usado para autenticação no banco de dados.postgresé o endereço do domínio.5432é a porta para se conectar ao banco de dados.testé o nome do banco de dados.
Instalação de bibliotecas de terceiros
Há várias bibliotecas de terceiros já definidas neste projeto que precisam ser instaladas, tanto por meio do Composer quanto dos pacotes do yarn.
Execute os três comandos a seguir:
Executando o Docker
Para este tutorial e a fim de garantir que os requisitos do servidor sejam os mesmos para todos, foi configurado um ambiente Docker para utilizar contêineres com configurações predefinidas.
Dentro do docker/ diretório, execute:
Assim que o docker-compose comando for concluído, você deverá ver a seguinte confirmação de que os três contêineres estão em execução:

Executando migrações de banco de dados
No seu terminal, conecte-se ao contêiner do PHP no Docker executando o seguinte comando:
Para criar as tabelas do banco de dados e executar todos os arquivos encontrados em symfony/src/migrations/, execute o seguinte comando:
Este comando cria uma tabela de banco de dados de usuários com as colunas pertinentes.
Teste o processo de cadastro
Acesse: http://localhost:8081/register no seu navegador e você verá uma página de cadastro semelhante à da imagem abaixo:

Digite um número de telefone de teste e uma senha. Ao enviar o formulário, você deverá ver a página de perfil!
Observação: Ao inserir seu número de telefone aqui, será criado um novo usuário; portanto, esteja preparado para excluir esse registro da tabela do banco de dados de usuários.
Se você chegou até aqui, já está tudo configurado e pronto para este tutorial.
Instalando o SDK do Nexmo para PHP
Observação: Os comandos do Composer devem ser executados de dentro do contêiner Docker do PHP. A partir do etapa “Executando as migrações do banco de dados” , você acessou remotamente o terminal do contêiner Docker do PHP. Se você sair da sessão do terminal do contêiner, poderá retornar a ela executando
docker-compose exec php basha partir dodocker/diretório.
O tutorial utiliza a Verify API do Vonage. A maneira mais fácil de utilizá-la em PHP é instalar nosso SDK para PHP.
Para instalar, execute o seguinte comando:
No seu Painel do Desenvolvedor da Vonage, você encontrará “Suas credenciais de API”; anote-as.
Dentro do diretório symfony/, adicione as duas linhas a seguir ao seu .env.local arquivo (substituindo api_key e api_secret pela sua chave e senha):
Crie um novo diretório chamado Util dentro symfony/src/, e dentro desse diretório crie um novo arquivo chamado VonageUtil.php.
Esta classe Utility irá lidar com qualquer código que utilize o SDK PHP da Nexmo. O exemplo abaixo não fará nada além de criar um objeto NexmoClient com as credenciais de autenticação que você salvou em .env.local. Copie o exemplo abaixo para o seu arquivo recém-criado 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']
)
);
}
}
Criação de uma página de verificação
Verify novas colunas
São necessárias novas propriedades dentro da User entidade para processar corretamente a verificação de um novo usuário.
No terminal do Docker, digite
Você vai criar três novas propriedades. Portanto, seguindo as etapas do comando acima, insira os valores conforme listado abaixo:
No campo em que é solicitado o tipo de entidade 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: falseÉ countryCode é necessário para determinar a que país o número de telefone pertence, para que a Verify API consiga realizar a chamada com sucesso.
O verificationRequestId ID que a Verify API retorna inicialmente ao servidor, que, quando combinada com o código de verificação, confirma a identidade do usuário.
A verified propriedade permite que o sistema determine se um usuário foi Verifyado ou não.
Você precisará executar o seguinte comando para gerar um novo arquivo de migração com essas alterações no banco de dados.
O comando acima detecta quaisquer alterações feitas nos Entity arquivos do seu projeto. Em seguida, ele converte essas alterações em consultas SQL que, quando executadas como uma migração, irão persistir as alterações no seu banco de dados.
Os arquivos de migração gerados estão em symfony/src/Migrations se você quiser ver as próximas alterações no banco de dados.
Se você estiver satisfeito com essas alterações, execute o comando abaixo para salvá-las no banco de dados.
Incluir o código do país no cadastro do usuário
Abra sua RegistrationFormType classe em symfony/src/Form/RegistrationFormType.php. Adicione uma nova inclusão para o tipo de formulário da classe ChoiceType no início do arquivo:
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;Nesse mesmo arquivo, faça as seguintes alterações:
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"
+ ]
+ ]
+ )
As únicas duas opções disponíveis nesta demonstração são o Reino Unido e os EUA. No entanto, outros países também são suportados. Você pode encontrar a lista ISO de países, juntamente com seus respectivos códigos, aqui: ISO.org. Certifique-se de que o valor na matriz seja o código ISO de dois caracteres correspondente ao país de sua escolha.
Nessa seção symfony/templates/registration/register.html.twig você encontrará uma linha do formulário para phoneNumber. Acima disso, adicione o countryCode equivalente:
+ {{ form_row(registrationForm.countryCode) }}
{{ form_row(registrationForm.phoneNumber) }}Se você tiver o Docker em execução, pode acessar a página de cadastro em http://localhost:8081/register e verá uma página semelhante à mostrada abaixo:

Você pode usar o formulário de cadastro, mas, por favor, não use o seu número ou esteja preparado para excluir esse cadastro da tabela do banco de dados de usuários.
Verify se o número de telefone é válido
Ao ligar para um número para fornecer um código de verificação, o sistema solicita o nome da marca. Portanto, no symfony/ lista de contatos, abra .env.local e adicione uma nova linha. Substitua VerifyWithVonage pela empresa/marca que você estiver representando para a verificação:
Para verificar se um número é um número de telefone válido, é preciso conferir se ele está no formato correto e se é válido para a região (código do país) que você informou. Para isso, você vai usar a versão em PHP da biblioteca libphonenumber do Google, desenvolvida por Giggsey.
Execute o comando abaixo para instalar essa biblioteca.
Abra seu VonageUtil arquivo, localizado em symfony/src/Util. Nessa classe, você precisa adicionar um método para validar o número de telefone e o código do país. Esse método também verifica se o número de telefone corresponde àquela região. Se for o caso, o método retornará o número de telefone em um formato internacionalizado. Copie o seguinte nessa 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
);
}
O método utiliza o $user objeto para realizar o seguinte:
analisar o número de telefone e o código do país usando a
libphonenumberbiblioteca,verifica se esse é um número válido para a região indicada (pelo código do país).
Se o número e a região forem válidos, ele formata o número de telefone para o formato internacional.
Envio de uma ligação de verificação no momento do cadastro
Crie um método em VonageUtil que utilize esse método privado. Esse novo método público garantirá que a entrada do usuário seja válida e, usando a Verify API, iniciará o processo de verificação.
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 evitar ter que alternar entre arquivos diferentes, você vai adicionar outro método a essa classe utilitária. Esse novo método permitirá que você obtenha o request_id que é retornado dentro do Verification objeto quando necessário.
public function getRequestId(Verification $verification): ?string
{
$responseData = $verification->getResponseData();
if (empty($responseData)) {
return null;
}
return $responseData['request_id'];
}
Esses novos métodos que você criou dentro da VonageUtil classe não fazem nada no momento. Para que sejam úteis, você precisará chamar a funcionalidade de dentro do RegistrationController , então abra este controlador localizado em symfony/src/Controller/.
Primeiro, você precisará injetar o VonageUtil no RegistrationController como um serviço:
+use App\Util\VonageUtil;
class RegistrationController extends AbstractController
{
+ /** @var VonageUtil */
+ protected $vonageUtil;
+
+ public function __construct(VonageUtil $vonageUtil)
+ {
+ $this->vonageUtil = $vonageUtil;
+ }
No seu register método, identifique as três $entityManager linhas e adicione a funcionalidade para setVerified() como “false”, conforme mostrado abaixo:
+$user->setVerified(false);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
Abaixo $entityManager->flush() faça a solicitação para iniciar o processo de verificação. Portanto, adicione as duas linhas a seguir: a primeira chama o VonageUtil método para enviar a solicitação de verificação; a segunda linha analisa a resposta e salva o requestId como uma variável:
$verification = $this->vonageUtil->sendVerification($user);
$requestId = $this->vonageUtil->getRequestId($verification);
Na aula, encontre o seguinte código:
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
Substitua isso por uma funcionalidade que primeiro verifique se você definiu o requestId, salva o requestId no usuário e, em seguida, autentica o usuário:
if ($requestId) {
$user->setVerificationRequestId($requestId);
$entityManager->flush();
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}
Verificar formulário
No terminal do Docker, execute o comando abaixo e, em seguida, siga as instruções, inserindo os valores conforme indicado:
- Name: VerifyFormType
- Entity name: User
Ao enviar isso, você deverá ter uma nova classe dentro de symfony/src/Form/ chamada VerifyFormType.php. São necessárias algumas alterações para que este formulário funcione conforme o esperado:
Substitua as seguintes linhas:
->add('phoneNumber')
->add('roles')
->add('password')
->add('countryCode')
->add('verificationRequestId')
->add('verified')
com:
->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.',
]),
],
])
Você acabou de remover campos de formulário que não deveriam ser atualizados e adicionou um novo campo não mapeado (um campo não mapeado para a tabela do banco de dados) chamado verificationCode. O verificationCode é enviado à API para verificar o número de telefone.
No início do arquivo, adicione mais três instruções `include`. Esses são os nomes completos das classes utilizadas no código de exemplo acima.
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank; Verificação do código
Na sua VonageUtil classe, você precisa de um novo método para chamar a API da Vonage e verificar se o código fornecido pelo usuário é válido. Insira o exemplo abaixo na sua VonageUtil classe:
public function verify(string $requestId, string $verificationCode)
{
$verification = new Verification($requestId);
return $this->client->verify()->check($verification, $verificationCode);
}
Crie um novo arquivo de modelo dentro de symfony/templates/registration chamado 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 do seu RegistrationController é necessário um novo método para exibir o modelo acima e processar o envio do formulário.
Primeiro, inclua a VerifyFormType e uma classe Vonage Verification no início:
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;Em seguida, crie o novo 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(),
]);
}
Nesta etapa do tutorial, o processo de cadastro é o seguinte:
em
/registerdigite um número de telefone e uma senhaé recebida uma ligação telefônica na qual é mencionado um número de quatro dígitos
redirecionado para
/profile
No momento, não há nenhuma verificação para garantir que o usuário esteja autenticado.
Aplicação da verificação
Nesta etapa, você vai implementar um assinante de eventos que verifica se o usuário já foi autenticado antes de permitir que ele acesse páginas protegidas. Se o usuário não estiver autenticado, ele será redirecionado de volta ao formulário de autenticação para inserir seu código de verificação.
No terminal do Docker, digite o comando para criar um novo assinante de eventos e siga as instruções exibidas na tela, utilizando os seguintes valores:
- Class name: `VerifiedUserSubscriber`
- Event to subscribe to: `kernel.controller`A imagem abaixo mostra um exemplo do que deve ser digitado para concluir o comando:

Abrir VerifiedUserSubscriber que pode ser encontrado em symfony/src/EventSubscriber/.
Adicione as verificações e restrições ao método onKernelController.
Primeiro, você deve verificar se o usuário está tentando acessar a URL do perfil ou não. Se não estiver, retorne e permita que ele siga para a página de destino:
if (!preg_match('/^\/profile/i', $event->getRequest()->getPathInfo())) {
return;
}
Agora você deseja verificar se o usuário está autenticado ou não. Para isso, injete o tokenStorage serviço no assinante de eventos. Ao fazer isso, para economizar tempo, injete o router serviço para a funcionalidade após a verificação do usuário.
No início do arquivo, junto com as outras inclusões de classes, adicione o seguinte:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;Em seguida, injete os dois serviços por meio do construtor da classe e defina esses serviços como propriedades dentro da 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;
}
De volta à onKernelController função, abaixo da verificação para saber se o usuário está acessando o perfil ou não, adicione o seguinte, que verifica se o usuário se autenticou e se está Verify:
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 fim, se o usuário estiver nesta etapa, significa que ele já está conectado, está tentando acessar a seção de perfil e ainda não foi verificado. Portanto, redirecione-o para a rota de Verify para garantir que ele verifique sua conta antes de prosseguir.
$route = $this->router->generate('app_register_verify');
$event->setController(function () use ($route) {
return new RedirectResponse($route);
});
Experimente!
A maneira correta de testar este tutorial agora é se cadastrar com um novo Account em registrar-se com um número de telefone válido. Você será direcionado para verificação ao enviar este formulário.
O número de telefone receberá uma ligação com um código de quatro dígitos, que você precisará inserir no formulário da página de verificação. Agora o perfil está acessível.
Você acabou de integrar um processo de cadastro em duas etapas à sua aplicação Symfony usando a Verify API da Vonage. O exemplo apresentado é apenas uma das muitas maneiras de utilizar a Verify API. Seja por meio da autenticação multifatorial durante o login ou da verificação da validade do número de telefone de um usuário para garantir que ele possa ser contatado.
Se este tutorial despertou seu interesse pela nossa Verify API, mas o PHP não é a linguagem de sua preferência, você pode encontrar outros tutoriais em várias linguagens ou sobre diversos serviços aqui no blog da Vonage, tais como:
Como adicionar a autenticação de dois fatores ao WordPress com a Verify API de Nexmo
Criação de um aplicativo de check-in com a Verify API da Nexmo e o Koa.js
O código final deste tutorial pode ser encontrado no repositório do GitHub no end-tutorial ramo.