
Compartilhar:
Andrea Chiarelli has over 20 years of experience as a software engineer and technical writer. Throughout his career, he has used various technologies for the projects he was involved in. Currently, he is a software architect at the Italian office of Apparound and contributes to a few online magazines and blogs.
Crie um serviço de senha de uso único (OTP) usando a Dispatch API
Tempo de leitura: 12 minutos
Aviso sobre descontinuação de produto A partir de 31 de agosto de 2025, a Dispatch API será encerrada para novos usuários, embora o produto continue a receber suporte para os usuários existentes. Se você pretende desenvolver um aplicativo de mensagens com funcionalidade de failover, saiba que essa funcionalidade agora é suportada diretamente na Messages API.
Para obter informações gerais sobre o recurso de failover de mensagens, consulte este guia. Para obter orientações sobre a migração da Dispatch API para a Messages API Failover, consulte este guia.
Caso tenha mais alguma dúvida sobre a descontinuação deste produto, por favor, entre em contato conosco no Slack da Comunidade Vonage.
As senhas de uso único (OTPs) tornaram-se bastante comuns nos últimos tempos, principalmente devido a uma exigência de segurança que as senhas tradicionais não garantem. Enquanto a proteção da senha tradicional é de responsabilidade do usuário — que, como bem sabemos, muitas vezes não se preocupa o suficiente com isso —, a OTP é praticamente autoprotegida, pois é gerada aleatoriamente e sua validade é limitada no tempo.
Você pode usar OTPs no lugar das senhas tradicionais ou para reforçar o processo de autenticação tradicional por meio da autenticação de dois fatores (2FA). Na verdade, você pode usar OTPs sempre que precisar de um mecanismo que garanta a identidade do usuário com base em um meio de comunicação de sua propriedade: uma caixa de e-mail, um telefone, um aplicativo específico etc.
Neste artigo, veremos como implementar um serviço básico de OTP com base em duas APIs da Web:
A primeira API permite criar a OTP e enviá-la ao usuário pelo Facebook Messenger como meio principal, ou por SMS como meio alternativo
A segunda API permite que o usuário verifique a senha de uso único (OTP) que recebeu
O serviço OTP não possui interface de usuário. Ele foi concebido como um microsserviço que você pode invocar a partir do seu aplicativo para gerar e verificar OTPs.
Pré-requisitos
Para utilizar o serviço OTP apresentado neste artigo, você precisa de:
Node.js 8.11 ou superior instalado no seu computador
A conta do Messenger e um celular habilitado para receber SMS
Um aplicativo para enviar solicitações HTTP, como curl ou Postman
Configurar o projeto
Como primeiro passo, você precisa clonar ou baixar o projeto do repositório do GitHub.
Depois de ter o código do projeto no seu computador, você precisa instalar as dependências dele acessando a pasta do projeto e digitando o seguinte comando:
Como veremos mais adiante, o aplicativo utiliza o Express como framework web e a biblioteca cliente da Vonage para Node.js, a fim de enviar a OTP ao usuário.
Configurar o aplicativo
Antes de usar o serviço OTP, é necessário realizar algumas configurações no painel da API da Vonage para habilitar o envio de mensagens por meio da Dispatch API do Vonage.
Essa API permite que você envie mensagens aos seus usuários por meio de vários canais, com priorização. Por exemplo, no nosso caso, enviaremos a senha OTP ao usuário por meio de sua conta do Messenger, como primeira tentativa. Se o usuário não ler a mensagem dentro de um determinado período, ela será enviada por SMS para o número de telefone dele.
Então, acesse o painel da API da Vonage, selecione a opção “Mensagens e Envio” no menu e, em seguida, selecione “Criar uma aplicação”, conforme mostrado na imagem abaixo:
Vonage Dashboard
Na terminologia da API da Vonage, uma aplicação é um conjunto de dados que permite utilizar as APIs de Mensagens e Dispatch API. Como podemos ver no formulário acima, os dados mínimos necessários são:
o nome do aplicativo
a URL de um webhook público configurado para receber o status de entrega das mensagens
a URL de um webhook público configurado para receber mensagens
a chave pública usada para assinar a solicitação que você envia à API (você pode gerar um par de chaves pública e privada clicando no link abaixo da área de texto)
As etapas dois e três do processo de criação do aplicativo de mensagens permitem que você atribua números de telefone e/ou links a contas externas de serviços como Messenger, WhatsApp ou Viber, para que você possa enviar SMS ou mensagens por meio da API Message and Dispatch.
Em particular, consulte este documento para entender como vincular sua página do Facebook à sua conta da API da Vonage.
Esteja ciente de que o status e as URLs de entrada a serem fornecidas a um aplicativo da API da Vonage devem estar acessíveis ao público. Se você não tiver um servidor web público ou quiser apenas testar isso no seu computador, use o ngrok, uma ferramenta que permite expor publicamente seu servidor web local.
Você pode encontrar mais detalhes sobre como trabalhar com o Ngrok em nossa documentação. Lembre-se de que, se você usar o plano gratuito do Ngrok, uma URL temporária será gerada toda vez que você executar a ferramenta. Portanto, é necessário atualizar as URLs do aplicativo nas configurações do seu painel de controle de acordo com isso.
Após criar seu aplicativo da API da Vonage, um Application ID será atribuído a ela. Anote-o.
Configurar o serviço OTP
Depois de configurar o lado da Vonage, é preciso configurar o lado do serviço OTP para que ambos possam se comunicar entre si.
Então, abra o nexmo.json arquivo na src pasta do projeto e forneça os dados solicitados:
{
"apiKey": "YOUR_API_KEY",
"apiSecret": "YOUR_API_SECRET",
"applicationId": "YOUR_APPLICATION_ID"
}Você pode recuperar o apiKey e os apiSecret valores na seção de configurações do painel da API da Vonage, enquanto o applicationId é o valor que você anotou na seção anterior.
Em seguida, pegue a chave privada associada à chave pública que você atribuiu ao aplicativo e salve-a no private.key arquivo na pasta src.
Executar o serviço OTP
É hora de executar seu serviço OTP. Digite o seguinte comando na pasta raiz do projeto:
Após alguns instantes, você deverá receber uma mensagem informando que o servidor está em execução na porta 3000. Você pode verificar se ele está funcionando acessando o endereço http://localhost:3000 endereço. Se tudo estiver certo, você deverá ver a mensagem “Este é o serviço OTP”.
Solicitar uma OTP
Agora, imagine que seu aplicativo precise gerar uma OTP para ser enviada a um usuário, a fim de verificar sua identidade. Ele precisa enviar uma POST solicitação ao serviço de OTP, fornecendo uma string que funciona como identificador da sua solicitação e os dados de contato do usuário para quem a OTP deve ser enviada.
Você pode fazer isso enviando uma solicitação HTTP como a seguinte ao serviço OTP em execução:
POST /otp/123456789 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
cache-control: no-cache
{"messengerId": "8192836451", "phoneNumber": "393331234567"}A string 123456789 anexada à URI da API é o identificador da sua solicitação. Nós a chamamos de token, e cabe a você fornecê-la. O corpo da solicitação contém um objeto JSON com o identificador do Messenger e o número de telefone do usuário que receberá a OTP.
Você pode enviar a solicitação pelo Postman, conforme mostrado na imagem a seguir:
Postman
O serviço OTP gerará uma senha de uso único (OTP) composta por 5 dígitos e a enviará para o identificador do Messenger especificado. Como veremos mais adiante, se o usuário não a ler dentro de um determinado prazo, a OTP será enviada por SMS para o número de telefone.
Após a criação bem-sucedida da OTP, você deverá receber um código de status HTTP 201 (Criado).
Verify a senha de uso único (OTP)
Independentemente do meio pelo qual a mensagem foi recebida, o usuário deve verificar a OTP enviando uma solicitação GET à segunda API, conforme o exemplo a seguir:
GET /otp/123456789/63731 HTTP/1.1
Host: localhost:3000
cache-control: no-cacheA URI da API é composta pelo prefixo , pelo token de solicitação (ou seja, o identificador da solicitação fornecido quando você solicitou a criação de um OTP) e pelo próprio OTP.
No Postman, aparece da seguinte forma:
Postman
Ao enviar tal solicitação, você poderá receber como resposta um dos seguintes códigos de status HTTP:
200 OK - Você recebe essa resposta quando sua OTP é válida
404 Não encontrado - Você recebe essa resposta quando sua OTP está incorreta, ou seja, quando ela não foi gerada pelo serviço de OTP
409 O código já foi verificado - Essa resposta significa que você ou outra pessoa já Verifyou a OTP
410 O código está vencido - Você recebe essa resposta se tentar verificar uma OTP após o término do prazo de validade
Você também pode receber o 404 O código é inválido por motivo desconhecido quando o serviço de OTP não conseguir verificar seu código por qualquer outro motivo.
Como funciona
Vamos agora dar uma olhada no código que implementa nosso serviço OTP. A imagem a seguir resume as pastas e os arquivos pertencentes ao projeto:
Project structure
O index.js arquivo na pasta src pasta contém o código inicial do aplicativo e a definição das APIs da Web. As APIs de criação e verificação são implementadas pelo código a seguir:
app.post("/otp/:token", (req, res) => {
const otp = otpManager.create(req.params.token);
otpSender.send(otp, req.body);
res.sendStatus(201);
});
app.get("/otp/:token/:code", (req, res) => {
const verificationResults = otpManager.VerificationResults;
const verificationResult = otpManager.verify(req.params.token, req.params.code);
let statusCode;
let bodyMessage;
switch (verificationResult) {
case verificationResults.valid:
statusCode = 200;
bodyMessage = "OK";
break;
case verificationResults.notValid:
statusCode = 404;
bodyMessage = "Not found"
break;
case verificationResults.checked:
statusCode = 409;
bodyMessage = "The code has already been verified";
break;
case verificationResults.expired:
statusCode = 410;
bodyMessage = "The code is expired";
break;
default:
statusCode = 404;
bodyMessage = "The code is invalid for unknown reason";
}
res.status(statusCode).send(bodyMessage);
});
Como você pode ver, ambas as APIs dependem da otpManager para, de fato, criar e verificar a OTP, e no otpSender para enviá-lo ao usuário. A inicialização delas ocorre algumas linhas acima, no mesmo index.js arquivo:
const OtpManager = require("./OtpManager");
const otpRepository = require("./otpRepository");
const otpSender = require("./otpSender")
const otpManager = new OtpManager(otpRepository, {otpLength: 5, validityTime: 5});Aqui você pode ver que todo o serviço é composto por três componentes:
otpManagerResponsável pela criação e verificação da OTPotpRepositoryResponsável por armazenar o OTPotpSenderResponsável pelo envio da senha de uso único (OTP) ao usuário
A existência desses três componentes permite manter a implementação da criação e verificação, do armazenamento e da entrega independentes umas das outras.
Ao criar a instância do otpManager, você passa o otpRepository e um options objeto que especifica o comprimento da OTP (cinco caracteres) e por quanto tempo ela deve ser considerada válida (cinco minutos).
O otpManager
O otpManager é uma instância da OtpManager classe implementada no OtpManager.js arquivo. Seus principais métodos são create() e verify().
O create() método gera um novo OTP e é implementado da seguinte forma:
create(token) {
const code = Math.floor(Math.random()*Math.pow(10, this.options.otpLength))
.toString()
.padStart(this.options.otpLength, "0");
let otp = new OtpItem(token, code);
this.otpRepository.add(otp);
return otp;
}Ele recebe um token como entrada e gera um número aleatório de cinco dígitos. Ele garante que o código resultante seja composto exatamente por cinco dígitos, mesmo que o primeiro dígito seja um zero, convertendo-o em uma sequência de caracteres e preenchendo-o com caracteres “0”.
É claro que essa é uma implementação muito simples da geração de OTP. Talvez você queira implementar algoritmos mais precisos, mas isso está fora do escopo deste artigo.
Depois de gerar o código, ele cria um objeto otp como instância da OtpItem classe e adiciona a nova otp instância à otpRepository.
A OtpItem classe define a estrutura para representar as informações relevantes para o OTP e está implementada no OtpItem.js arquivo:
class OtpItem {
constructor(token, code) {
this.token = token;
this.code = code;
this.creationDate = new Date();
this.isChecked = false;
this.checkDate = null;
}
}O verify() método verifica se o código passado para um determinado token já foi gerado e se ainda é válido. Aqui está sua implementação:
verify(token, code) {
const id = `${token}-${code}`;
const otp = this.otpRepository.getById(id);
let verificationResult = VerificationResults.notValid;
if (otp) {
switch (true) {
case otp.isChecked:
verificationResult = VerificationResults.checked;
break;
case isOtpExpired(otp, this.options.validityTime):
verificationResult = VerificationResults.expired;
break;
default:
otp.isChecked = true;
otp.checkDate = new Date();
this.otpRepository.update(otp);
verificationResult = VerificationResults.valid;
}
}
return verificationResult;
}
}O método gera um identificador OTP concatenando o token e o código. Esse identificador é usado para obter a otp instância do otpRepository. Caso tal instância exista, o método verifica se ela já foi validada e se ainda não expirou. O valor retornado é um valor de enumeração que representa o status de validade do OTP.
O otpRepository
O otpRepository armazena a instância de um OtpItem no sistema de arquivos como um arquivo JSON simples na otpItems pasta. Essa é uma solução muito simples que funciona para um caso de demonstração. Talvez você queira implementá-la armazenando os dados em um banco de dados.
Aqui está o código de implementação que você pode encontrar no otpRepository.js arquivo:
const fs = require("fs");
const path = require("path");
const baseRepositoryPath = "./otpItems";
function add(otpItem) {
checkBaseFolder();
fs.writeFileSync(path.join(baseRepositoryPath, `${otpItem.token}-${otpItem.code}`), JSON.stringify(otpItem));
}
function getById(id) {
const content = getFileContent(path.join(baseRepositoryPath, id));
let otpItem = null;
if (content) {
otpItem = JSON.parse(content);
}
return otpItem;
}
function update(otpItem) {
fs.writeFileSync(path.join(baseRepositoryPath, `${otpItem.token}-${otpItem.code}`), JSON.stringify(otpItem));
return otpItem;
}
function checkBaseFolder() {
if (!fs.existsSync(baseRepositoryPath)){
fs.mkdirSync(baseRepositoryPath);
}
}
function getFileContent(fileName) {
let content = null;
try {
content = fs.readFileSync(fileName);
} catch (error) {
console.log(error);
}
return content;
}
module.exports = {
getById,
add,
update
};Como você pode ver, ele implementa o getById() método para recuperar uma OtpItem instância, o add() método para armazenar uma OtpItem instância e um update() método para atualizá-la.
O otpSender
O otpSender componente envia a OTP ao usuário usando a Dispatch API. Isso é implementado pelo otpSender.js arquivo da seguinte maneira:
const Nexmo = require('nexmo')
const nexmoConfig =require("./nexmo.json");
const path = require("path");
nexmoConfig.privateKey = path.join(__dirname, "private.key");
const nexmo = new Nexmo(nexmoConfig);
function send(otp, recipientAdresses) {
const message = `Insert the following code: ${otp.code}`;
nexmo.dispatch.create("failover", [
{
"from": { "type": "messenger", "id": "YOUR_MESSENGER_ID" },
"to": { "type": "messenger", "id": recipientAdresses.messengerId },
"message": {
"content": {
"type": "text",
"text": message
}
},
"failover":{
"expiry_time": 120,
"condition_status": "read"
}
},
{
"from": {"type": "sms", "number": "NEXMO"},
"to": { "type": "sms", "number": recipientAdresses.phoneNumber},
"message": {
"content": {
"type": "text",
"text": message
}
}
},
(err, data) => {
console.log(data.dispatch_uuid);
}
])
}
module.exports = {
send
};Ele cria uma configuração ao combinar os dados do nexmo.json arquivo e do private.key arquivo. Essa configuração é passada ao construtor da biblioteca para obter uma nexmo instância. Essa instância será utilizada na implementação da send() função. A função recebe uma OtpItem instância e um recipientAddresses objeto como argumentos e constrói a mensagem a ser enviada ao usuário e a carga útil para a Dispatch API
Por meio do nexmo.dispatch.create() método, você está criando um fluxo de trabalho de entrega com failover. O segundo argumento do método é um array contendo três itens:
O primeiro item é um objeto que especifica o remetente, o destinatário e o texto da mensagem a ser enviada. O tipo do remetente e do destinatário indica que se trata de uma comunicação pelo Messenger. Ele também possui uma
failoverpropriedade que especifica quando a entrega deve ser considerada malsucedida. No nosso caso, ela é considerada malsucedida se a mensagem não for lida em até 120 segundosO segundo item é outro objeto que especifica o remetente, a entrega e o texto da mensagem a ser enviada caso a entrega pelo Messenger falhe. Nesse caso, o tipo do remetente e do destinatário indica que a mensagem deve ser enviada por SMS.
O último item é uma função de retorno de chamada executada após o envio do fluxo de trabalho ao servidor da API da Vonage. No nosso caso, simplesmente exibimos no console o identificador do fluxo de trabalho (dispatch_uuid) retornado pela Vonage.
Isso aumenta as chances de que a OTP gerada seja entregue ao usuário, independentemente do meio de comunicação utilizado.
Compartilhar:
Andrea Chiarelli has over 20 years of experience as a software engineer and technical writer. Throughout his career, he has used various technologies for the projects he was involved in. Currently, he is a software architect at the Italian office of Apparound and contributes to a few online magazines and blogs.