
Compartilhar:
Educador de tecnologia com um jeito amigável, pai de família, defensor da diversidade, provavelmente discuto um pouco demais. Ex-engenheiro de backend. Fale comigo sobre JavaScript (front-end ou back-end), o incrível Vue.js, DevOps, DevSecOps e tudo relacionado ao JamStack. Escritor no DEV.to
Cadastre-se para conversar com o Typeform
Tempo de leitura: 45 minutos
Neste artigo, você aprenderá como configurar o Typeform e capturar dados de um webhook no Node.js framework Express.js. Você usará o Passport.js para autenticar um usuário e utilizará o o SDK do servidor Node.js da Nexmo para registrar um usuário e gerar um JWT para usar com o Client SDK de JavaScript da Nexmo.
Você começará com um aplicativo de chat já pronto, desenvolvido usando Client SDK do cliente JavaScript da Nexmo e Bootstrap.
Este tutorial começa a partir do ramo e termina no tutorial-finish . Você pode pular para o final fazendo o check-out tutorial-finish e seguir o arquivo README para começar a usar rapidamente.
Pré-requisitos
Node e NPM
Para seguir este guia, você precisará ter o Node.js e o NPM instalados. Este guia utiliza o Node.js 13.1 e o NPM 6.12. Verifique se você tem instaladas, no mínimo, versões estáveis ou com suporte de longo prazo do Node.js.
Se você não tiver o Node.js ou o NPM, ou se tiver versões mais antigas, acesse nodejs.org e instale a versão correta , caso ainda não a tenha.
Nexmo CLI
Para configurar seu aplicativo, você precisará instalar o Nexmo CLI. Instale-o usando o NPM no terminal.
Agora, configure a CLI usando sua chave e seu segredo de API, que podem ser encontrados no painel do seu account Nexmo.
MongoDB
Vamos armazenar informações no MongoDB. Se você ainda não tiver o MongoDB instalado, siga o guia de instalação do MongoDB Community Edition para o seu sistema.
Ngrok
Como você receberá informações de um terceiro, será necessário expor o aplicativo em execução na sua máquina local, mas de forma segura. O Ngrok é uma maneira segura de usar um único comando para gerar uma URL instantânea e segura que permite acessar sua máquina local, mesmo através de um NAT ou firewall.
Cadastre-se e configure o ngrok seguindo as instruções no site deles.
Typeform
Você vai usar o Typeform para coletar informações dos usuários, então cadastre-se agora para obter um Account gratuito no Typeform.
Provedor de SMTP para e-mail
Você vai enviar e-mails. Vai precisar do nome do servidor, da porta, de um nome de usuário e de uma senha de um provedor de SMTP.
Você pode usar o Gmail para enviar e-mails a partir de um aplicativo.
Git (opcional)
Você pode usar o Git para clonar o aplicativo de demonstração do GitHub.
Se você não se sente à vontade com o Git, este guia também traz instruções sobre como baixar o projeto como um arquivo ZIP.
Siga este guia para instalar o git
Começando
O aplicativo com o qual você está começando é um aplicativo de bate-papo desenvolvido com o Bootstrap e o Client SDK do cliente JavaScript da Nexmo. Ele pode ser configurado editando-se arquivos estáticos, mas é iniciado usando o Express.js, um servidor HTTP leve baseado em Node.js.
Instalação básica
Clone o aplicativo de demonstração diretamente do GitHub.
Ou, para quem não se sente à vontade com os comandos do Git, é possível baixar o aplicativo de demonstração como um arquivo zip e descompactá-lo localmente.
Depois de clonada ou descompactada, acesse o novo diretório do aplicativo de demonstração.
Instale as dependências do npm.
Junto com o Node.js, é instalado um pacote chamado nodemon, que recarregará automaticamente seu servidor caso você edite algum arquivo.
Inicie o aplicativo da maneira usual.
Inicie o aplicativo, mas usando o nodemon.
Dica: Se você estiver executando o aplicativo com
nodemonpelo restante deste tutorial, sempre que eu sugerir reiniciar o aplicativo, você não precisará fazer isso, poisnodemono sistema já faz isso por você. No entanto, se precisar se autenticar novamente no aplicativo, ainda será necessário fazer isso, já que as informações da sessão são armazenadas na memória e não estão configuradas para usar nenhum outro tipo de armazenamento.
Seja qual for a forma que você escolher para executar o aplicativo, assim que ele estiver em execução, você poderá testá-lo no seu navegador preferido, que deverá conseguir localizá-lo rodando localmente: http://0.0.0.0:3000/.

Como o aplicativo não está configurado, você verá um aplicativo de bate-papo vazio e muito simples, no qual não é possível enviar mensagens. Na prática, com o tratamento de erros, você poderia exibir uma mensagem de erro de conexão para o usuário.
Mas, se você verificar o console do navegador agora, verá apenas um erro da API da Nexmo relacionado à falta de um token. Isso significa que o aplicativo tentou se conectar, mas não forneceu um token de usuário que permitisse o acesso à API.
Verifique se o ngrok está configurado corretamente, executando o ngrok em uma aba ou janela separada para npm.

Você precisa executar este ngrok comando e npm ao mesmo tempo. Isso significa que você precisa ter duas janelas ou abas do terminal disponíveis, ambas no diretório do aplicativo.
Dica: Se precisar repetir alguma tarefa mais tarde, como enviar dados do Typeform para o webhook, você pode abrir a interface web do ngrok em http://127.0.0.1:4040 enquanto ele estiver em execução e reproduzir uma solicitação.
É importante lembrar que, até você pagar pelo ngrok, sua URL será diferente a cada vez que você o iniciar. Lembre-se disso ao configurar o webhook do Typeform mais tarde. Se você encerrar o ngrok, precisará reconfigurar o Typeform com a nova URL quando o reiniciar.
Dica: Se você já tem experiência com uma ferramenta como o Postman ou escrever solicitações cURL manualmente, e assim que receber sua primeira solicitação de webhook do Typeform, você pode criar uma solicitação para poder repeti-la mais tarde.
Comece a conversar
Nos pré-requisitos, você configurou sua CLI usando sua chave e seu segredo da API da Nexmo. Agora, você pode executar comandos da CLI para criar um aplicativo Nexmo, um usuário e uma conversa, adicionar o usuário à conversa e gerar um JWT para que seu usuário possa conversar.
Configuração do Nexmo
Você precisará usar alguns dos IDs gerados depois de executar alguns dos comandos. Anote-os, copiando e colando os IDs do aplicativo, da conversa e do usuário.
Criar uma aplicação Nexmo
Este comando cria um novo aplicativo Nexmo com recursos de RTC (comunicação em tempo real). Como você não irá capturar os eventos em seu aplicativo, é possível fornecer um endereço da web de exemplo para a URL do evento. A chave privada será salva no caminho de arquivo de sua escolha.
Dica: Seu aplicativo também é gravado em um arquivo de configuração (
.nexmo-app) no diretório em que você executou este comando. Isso significa que alguns comandos adicionais executados a partir desse diretório estarão relacionados a este aplicativo, como a criação de usuários e conversas.
Criar uma conversa no Nexmo
Depois de criar uma aplicação, você pode criar uma conversa. A conversa será o espaço onde seus usuários participarão para trocar mensagens.
Crie sua conta
Agora, crie um usuário. Esse será o usuário com o qual você fará a autenticação. Por enquanto, basta um nome de usuário e um nome de exibição.
Adicionar usuário à conversa
Com o ID da conversa e o ID do usuário, execute este comando para participar da conversa com esse usuário.
Gerar token de usuário
Use este comando para gerar um token de usuário no formato JWT, que pode ser utilizado tanto pela API quanto pelo Client SDK do JavaScript da Nexmo. Ele retornará um JWT para você usar, com validade de 24 horas, ou 86.400 segundos.
Configurar o aplicativo
Para configurar seu aplicativo, edite o views/layout.hbs arquivo e localize a configuração em JavaScript por volta da linha 61.
<script>
var userName = '';
var displayName = '';
var conversationId = '';
var clientToken = '';
</script>Primeiramente, configure o aplicativo desta forma, mas, ao final deste guia, você poderá se autenticar com um link mágico, e o aplicativo do lado do cliente obterá seu token de usuário a partir da sua sessão autorizada.
Edite o arquivo de configuração com os valores que você gerou nos comandos acima.
<script>
var userName = 'luke.oliff@vonage.com';
var displayName = 'Luke Oliff';
var conversationId = 'CON-123...y6346';
var clientToken = 'eyJhbG9.eyJzdWIiO.Sfl5c';
</script>Agora, você pode reiniciar o aplicativo e começar a conversar... consigo mesmo... porque ninguém mais consegue fazer login.

Criação de um Typeform
Você pode coletar quantos dados quiser no seu Typeform. No entanto, para este guia, certifique-se de incluir pelo menos um campo de e-mail no formulário.
Depois de criar seu Typeform, clique no guia “Conectar” na página de edição do seu Typeform e clique em Webhooks.
Clique em Adicionar um webhook e insira a URL como https://<your_url>.ngrok.io/webhooks/magiclink. Em seguida, clique em Salvar webhook.

Depois de criado, você pode voltar e adicionar um segredo para Verify se as solicitações que chegam ao seu webhook realmente vêm do Typeform.
Se você preencher seu Typeform agora e enviá-lo enquanto sua aplicação estiver em execução, o Typeform receberá um 404 Not Found erro e tentará novamente. Se uma solicitação de webhook falhar por qualquer motivo, o Typeform tentará novamente enviar a solicitação para o seu endpoint três vezes, utilizando um mecanismo de recuo após 5, 10 e 20 minutos.
Variáveis de ambiente
A partir de agora, você vai configurar seu aplicativo com credenciais que não só podem variar de um ambiente para outro, mas também que você não vai querer incluir no código-fonte.
dotenv já era uma dependência do projeto inicial; portanto, consulte o .env arquivo, que já contém a porta padrão para o aplicativo. Você voltará a esse arquivo em breve para adicionar mais variáveis de ambiente.
Adicionar um webhook
Agora, para corrigir seu possível 404 Not Found erro, adicione o webhook criando um novo arquivo no aplicativo chamado routes/webhook.js. No novo arquivo, adicione o código a seguir.
var express = require('express');
var router = express.Router();
/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
console.log(req.body);
// always return a response...
res.sendStatus(200);
});
module.exports = router;
Editar app.js e adicione o roteador de webhooks.
// ...
var indexRouter = require('./routes/index');
var webhookRouter = require('./routes/webhook');
// ...
app.use('/', indexRouter);
app.use('/webhooks', webhookRouter);
// ...Com o npm e o ngrok em execução, você já deve conseguir preencher seu Typeform e receber uma solicitação de webhook. A carga útil conterá dados semelhantes a estes e será exibida na janela em que você iniciou o aplicativo com o npm.
{
...
"form_response": {
...
"answers": [
{
"type": "email",
"email": "email@example.com",
"field": {
"type": "email",
}
}
]
}
} Descubra a resposta
Antes de editar o webhook, configure algumas variáveis para o Typeform e a pergunta no seu arquivo de ambiente .env. Para FORM_FIELD_REF, você precisará editar sua pergunta no Typeform e localizar a referência da pergunta nas configurações da pergunta. FORM_URL é a URL pública para preencher o formulário.
Agora, voltando à sua rota de webhook em routes/webhook.js e edite-a para incluir o código que extrairá o endereço de e-mail.
//...
require('dotenv').config();
/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
// find answers from the typeform response
let { answers } = req.body.form_response;
const answer = answers
.find(answer => process.env.FORM_FIELD_TYPE === answer.type && answer.field.ref === process.env.FORM_FIELD_REF);
// it'll probably be an email
const email = answer[process.env.FORM_FIELD_TYPE];
console.log(email);
// always return a response...
res.sendStatus(200);
});
Este código encontrará uma resposta do tipo email tipo com a referência da pergunta (caso você capture mais de um endereço de e-mail no seu formulário!) e, por fim, retorna o valor da resposta. O tipo e a referência foram definidos no .env arquivo.
O resultado disso será a sequência de caracteres enviada para a pergunta do Typeform.
Usuários da loja
Este tutorial continuará partindo do princípio de que você está capturando apenas um único campo de e-mail do Typeform e nenhuma outra informação do usuário. Ele armazenará outras informações derivadas sobre o usuário à medida que forem criadas.
Você vai usar o Mongoose para armazenar seus usuários no banco de dados. O Mongoose oferece uma solução simples e baseada em esquema para modelar os dados do seu aplicativo. Ele inclui conversão de tipos, validação, construção de consultas, ganchos de lógica de negócios e muito mais, tudo pronto para uso.
Instalar o Mongoose
Para registrar a criação e os detalhes do usuário, instale mongoose no seu projeto.
Configurar a conexão com o MongoDB
Configure o projeto para que o Mongoose consiga se conectar ao banco de dados MongoDB. Este guia utiliza as configurações padrão do MacOS , que podem diferir do que você precisa, dependendo do ambiente de desenvolvimento que estiver utilizando.
Editar .env e adicione a seguinte configuração.
Você pode decidir your-database-name aqui, pois ele será criado caso ainda não exista.
Conectar-se ao MongoDB
Agora, configure seu aplicativo para se conectar ao Mongoose quando for executado, editando o bin/www arquivo e inserindo este código no final.
/**
* Database config
*/
const mongoose = require('mongoose');
// Set mongoose promises to global
mongoose.Promise = global.Promise
// Set up default mongoose connection
mongoose.connect(process.env.MONGO_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });
// Get the default connection
const db = mongoose.connection;
// Bind connection to error event (to get notification of connection errors)
db.on('error', onError); Esquema e modelo do usuário
Tudo no Mongoose começa com um esquema. Cada esquema corresponde a uma coleção do MongoDB e define a estrutura dos documentos dentro dessa coleção. Embora o MongoDB seja sem esquema, o Mongoose utiliza esquemas para formalizar o objeto padrão antes de qualquer modificação.
Crie um novo arquivo para o esquema em schemas/user.js e adicione o código a seguir.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true
},
display_name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
user_id: {
type: String
},
member_id: {
type: String
}
});
module.exports = UserSchema;Um modelo é o que se usa para criar documentos que permitem criar, editar, atualizar e excluir itens em uma coleção do MongoDB. Crie um novo arquivo para o modelo em models/user.js e adicione o código a seguir.
const mongoose = require('mongoose');
const UserSchema = require('../schemas/user');
const User = mongoose.model('User', UserSchema);
module.exports = User;Observe como o modelo inclui o esquema para retornar um User documento.
Localizar e salvar usuários
Nesse caso, você vai usar o e-mail como identificador de cadeia de caracteres dos usuários, ou seja, como nome de usuário. O endereço de e-mail deles acabará se tornando também o nome de exibição. Se desejar, você pode optar por coletar essas duas informações separadamente no seu Typeform.
Editar routes/webhook.js e adicione o código a seguir para localizar usuários pelo nome de usuário e criá-los caso ainda não existam.
//...
var User = require('../models/user');
/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
// ...
User.findOne({ name: email }, (err, user) => {
// error handling here
// if our user is new, save it and output it
if (null === user) {
user = new User({
name: email,
email: email,
display_name: email
});
user.save((err) => {
// error handling here
console.log(user);
res.sendStatus(200);
});
// otherwise, just output it
} else {
console.log(user);
res.sendStatus(200);
}
});
});
Este código tentará localizar um usuário pelo endereço de e-mail, criando uma conta caso ainda não exista. Ele não permite a atualização de um usuário já existente. Se o usuário já existir, poderá ocorrer um erro. Mais tarde, geraremos um link especial para o login, em vez de exibir uma mensagem de erro.
Gerar um link mágico
Seu webhook enviará ao usuário um link mágico por e-mail, que poderá ser usado para autenticá-lo no serviço.
Instalar jsonwebtoken usando o npm.
Editar .env para criar uma chave secreta que possa ser usada para a geração de tokens.
Então, agora edite routes/webhook.js para gerar o link mágico e enviá-lo ao servidor.
//...
var jwt = require('jsonwebtoken');
var createMagicLink = (req, payload) => {
var token = jwt.sign(payload, process.env.SECRET);
return `${req.protocol}://${req.get('host')}/auth?token=${token}`;
}
/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
// ...
// ...
if (null === user) {
// ...
user.save((err) => {
// ...
console.log(createMagicLink(req, user.toObject()));
res.sendStatus(200);
});
// otherwise, just output it
} else {
console.log(createMagicLink(req, user.toObject());
res.sendStatus(200);
}
// ...
});
Estamos adicionando um JWT a uma URL de link mágico como forma de identificar o usuário quando ele tentar acessar o site.
É importante observar que um JWT garante a propriedade dos dados, não a criptografia. Trata-se de uma forma segura para URLs de representar reivindicações, codificando-as como objetos JSON que podem ser assinados digitalmente ou criptografados. A assinatura digital de um JWT permite a validação contra modificações. A criptografia, por outro lado, garante que o conteúdo do JWT seja legível apenas por determinadas partes.
Nesse caso, o guia não utiliza RSA nem qualquer outra criptografia assimétrica, optando apenas por assinar os dados por meio da assinatura síncrona HMAC SHA256 padrão da biblioteca JWT.
Usar um JWT dessa forma verifica se o link mágico se originou do seu aplicativo, foi assinado por você SECRET e não pode ser modificado.
Agora, quando você enviar dados para o webhook a partir do Typeform, o resultado deverá ser um link para o aplicativo que se parece com uma versão bem mais longa deste:
https://<your_url>.ngrok.io/webhooks/auth?token=eyJhbCJ9.eyEflLxN.N9eq6b5o
Clique no link para ver um erro 404. Vamos corrigir isso.

Autenticar com o Passport.js
Passport.js se autodescreve como uma autenticação discreta para o Node.js. É incrivelmente flexível e modular e pode ser facilmente integrado a um aplicativo, como este.
Instalar o Passport.js
Instale passport, a passport-jwt estratégia e express-session para que possa ser usado para autenticação e manutenção de uma sessão.
Criar um ponto de extremidade de autenticação
Crie um novo arquivo chamado routes/auth.js com este código-fonte.
var express = require('express');
var router = express.Router();
/* GET authenticate user with magic link and direct to home */
router.get('/', (req, res, next) => {
res.redirect(req.protocol + '://' + req.get('host') + '/');
});
module.exports = router;
Este roteador irá redirecioná-lo para a página inicial. No entanto, você só terá acesso a este roteador se estiver autorizado pelo JWT no momento em que solicitar a página.
Editar app.js e adicione este código para incluir a autenticação do Passport em uma nova rota de autenticação.
// ...
var indexRouter = require('./routes/index');
var webhookRouter = require('./routes/webhook');
var authRouter = require('./routes/auth');
// ...
var User = require('./models/user');
var session = require('express-session');
var passport = require('passport');
var jwtStrategy = require('passport-jwt').Strategy;
var jwtExtractor = require('passport-jwt').ExtractJwt;
app.use(session({
secret: process.env.SECRET,
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use(new jwtStrategy({
jwtFromRequest: jwtExtractor.fromUrlQueryParameter('token'),
secretOrKey: process.env.SECRET
}, (payload, done) => {
return done(null, payload);
}))
app.use('/', indexRouter);
app.use('/webhooks', webhookRouter);
app.use('/auth', passport.authenticate('jwt', { session: true }), authRouter);
// ...
Este código autenticará qualquer solicitação ao /auth ponto de extremidade usando o extrator JWT da passport-jwt estratégia. Ele tentará validar o token a partir de um parâmetro da string de consulta.
Após a autenticação, o aplicativo criará uma sessão e os dados do usuário ficarão disponíveis como req.user.
Para testar isso, edite routes/index.js e adicione este código antes da res.render() linha.
console.log(req.user);Agora, reinicie o aplicativo e gere um link mágico usando sua solicitação do Typeform. Ao clicar no link, você será redirecionado de volta para o chat após a autenticação. Mas, no seu console, você verá alguns dados do usuário exibidos mais ou menos assim:

Certifique-se de que ninguém possa acessar o chat, a menos que esteja autenticado, editando o routes/index.js para que fique exatamente assim.
var express = require('express');
var router = express.Router();
require('dotenv').config();
var isAuthenticated = (req, res, next) => {
if(req.isAuthenticated()){
next();
} else{
res.redirect(process.env.FORM_URL);
}
}
/* GET home */
router.get('/', isAuthenticated, (req, res, next) => {
res.render('index', { title: 'Nexmo Typeform Chat', user: req.user.display_name });
});
module.exports = router;
Removendo a saída do `console.log` que você acabou de adicionar acima; o chat não registrará mais os dados do usuário atual no console. Em vez disso, o nome de exibição é adicionado ao escopo dos modelos a serem renderizados. Essa alteração também redirecionará para o Typeform caso o usuário não esteja conectado.
Editar views/layout.hbs e exiba o nome de exibição. Encontre username e substitua-o por {{user}}, o código ao redor deve ficar assim.
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#">
<span data-feather="home"></span>
{{user}}
</a>
</li>
</ul>Quando estiverem conectados, vamos também exibir os participantes do chat (a partir do banco de dados) na página. Editar routes/index.js e coloque o res.render dentro do User.find que retorna todos os usuários registrados.
// ...
var User = require('../models/user');
// ...
/* GET home */
router.get('/', isAuthenticated, (req, res, next) => {
User.find((err, users) => {
res.render('index', { title: 'Nexmo Typeform Chat', members: users, user: req.user.display_name });
})
});
Editar views/layout.hbs novamente e localize este bloco inteiro:
{{!-- {{#each members}} --}}
<li class="nav-item">
<a class="nav-link text-muted" href="#">
<span data-feather="file-text"></span>
other member
</a>
</li>
{{!-- {{/each}} --}}Substitua por este código funcional.
{{#each members}}
<li class="nav-item">
<a class="nav-link text-muted" href="#">
<span data-feather="file-text"></span>
{{this.display_name}}
</a>
</li>
{{/each}}Reinicie o aplicativo e acesse-o novamente por meio do seu link mágico. Agora, você deve ver algumas informações do usuário na página.

Você ainda está acessando o chat usando os dados de teste predefinidos. É hora de cadastrar seus usuários no Nexmo e permitir que eles também acessem a conversa.
Faça com que os usuários registrados conversem no Nexmo
No momento, você tem usuários se cadastrando, mas eles só estão usando o chat por meio das informações de usuário definidas estaticamente.
Instalar e configurar o Nexmo Node
Neste momento, você vai começar a interagir com o serviço Nexmo a partir do seu aplicativo Node.js pela primeira vez.
Instale nexmo agora com este comando.
Configure algumas variáveis para o Nexmo no seu arquivo de configuração .env. Você precisará da mesma chave de API e do mesmo segredo que usou para configurar nexmo-cli logo no início. Você também precisará do ID do aplicativo e do caminho da chave privada de quando executou nexmo app:create, bem como o ID da conversa de quando você executou nexmo conversation:create.
Crie um arquivo de utilitário em util/nexmo.js que irá configurar a nexmo biblioteca.
const Nexmo = require('nexmo');
require('dotenv').config();
let options = {};
module.exports = new Nexmo({
apiKey: process.env.NEXMO_API_KEY,
apiSecret: process.env.NEXMO_API_SECRET,
applicationId: process.env.NEXMO_APP_ID,
privateKey: process.env.NEXMO_PRIVATE_KEY_PATH
}, options); Criar usuário do Nexmo
Antes de mais nada, você precisa criar um usuário Nexmo paralelamente ao seu usuário local no momento em que ele se cadastrar.
Editar routes/webhook.js e substitua o arquivo inteiro por este código:
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
require('dotenv').config();
var User = require('../models/user');
var nexmo = require('../util/nexmo');
var createMagicLink = (req, payload) => {
var token = jwt.sign(payload, process.env.SECRET);
return `${req.protocol}://${req.get('host')}/auth?token=${token}`;
}
/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
// find answers from the typeform response
let { answers } = req.body.form_response;
const answer = answers
.find(answer => process.env.FORM_FIELD_TYPE === answer.type && answer.field.ref === process.env.FORM_FIELD_REF);
// it'll probably be an email
const email = answer[process.env.FORM_FIELD_TYPE];
User.findOne({ name: email }, (err, user) => {
// error handling here
// if we can't find an existing user, prepare a new user document
if (null === user) {
user = new User({
name: email,
email: email,
display_name: email
});
}
if (null === user.user_id) {
nexmo.users.create(user.toObject(), (err, nexmoUser) => {
// error handling here
user.user_id = nexmoUser.id;
nexmo.conversations.members.create(process.env.NEXMO_CONVERSATION_ID, {
action: 'join',
user_id: nexmoUser.id,
channel: { type: 'app' }
}, (err, member) => {
// error handling here
user.member_id = member.id;
user.save((err) => {
// error handling here
console.log(createMagicLink(req, user.toObject()));
res.sendStatus(200);
});
});
});
} else {
console.log(createMagicLink(req, user.toObject()));
res.sendStatus(200);
}
});
});
module.exports = router;
Esse novo código de webhook verificará se já existe um usuário no banco de dados e, caso não exista, criará um, exatamente como fazia antes. Mas agora, ele criará um usuário Nexmo e o associará à conversa, atualizando o registro no banco de dados com o ID do usuário Nexmo e um ID de membro.
Reinicie o aplicativo e gere um novo link mágico para o seu usuário. Clique nele para se autenticar. Agora, o sistema perceberá que não há nenhum usuário Nexmo, criará um, o adicionará à conversa e o salvará no registro do usuário.
Ao ser redirecionado para o aplicativo de bate-papo, você verá agora que o usuário que criou entrou na conversa. No entanto, você ainda está conversando como o usuário predefinido.

Gerar um token para o Client SDK
Seus usuários podem se cadastrar, fazer login e até mesmo participar da conversa. Mas, no momento, eles só conversam usando dados de usuário predefinidos. É hora de corrigir isso e permitir que eles conversem usando suas próprias identidades.
Abrir routes/index.js e crie uma nova rota /jwt, pois, basicamente, você irá expor um novo JWT específico para o serviço Nexmo, que poderá ser utilizado pelo Client SDK.
// ...
var nexmo = require('../util/nexmo');
/* GET home */
// ...
/* GET user data and jwt */
router.get('/jwt', isAuthenticated, (req, res, next) => {
const aclPaths = {
"paths": {
"/*/users/**": {},
"/*/conversations/**": {},
"/*/sessions/**": {},
"/*/devices/**": {},
"/*/image/**": {},
"/*/media/**": {},
"/*/push/**": {},
"/*/knocking/**": {}
}
};
const expires_at = new Date();
expires_at.setDate(expires_at.getDate() + 1);
const jwt = nexmo.generateJwt({
application_id: process.env.NEXMO_APP_ID,
sub: req.user.name,
exp: Math.round(expires_at/1000),
acl: aclPaths
});
res.json({
user_id: req.user.user_id,
name: req.user.name,
member_id: req.user.member_id,
display_name: req.user.display_name,
client_token: jwt,
conversation_id: process.env.NEXMO_CONVERSATION_ID,
expires_at: expires_at
});
})
// ...Essa nova rota utiliza a sessão existente do usuário para fornecer dados ao navegador. A página inicial fornece esses dados em HTML, mas esse novo endpoint retorna JSON.
Reinicie o aplicativo, acesse o link mágico e, em seguida, acesse https://<your_url>.ngrok.io/jwt. Você verá informações baseadas no seu usuário atual, incluindo um client_token para usar no Client SDK.

Remova a configuração codificada
É hora de parar de codificar manualmente a configuração dentro do aplicativo. Edite o views/layout.hbs arquivo, localizando a configuração que você adicionou dentro das <script> . Era algo parecido com isto.
<script>
var userName = 'luke.oliff@vonage.com';
var displayName = 'Luke Oliff';
var conversationId = 'CON-123...y6346';
var clientToken = 'eyJhbG9.eyJzdWIiO.Sfl5c';
</script>Exclua as tags de script e seu conteúdo, por completo.
Se você quiser ver o que isso causou no seu aplicativo, reinicie-o e faça a autenticação para descobrir que ele está praticamente de volta ao ponto de partida, com o chat fora de funcionamento. Pelo menos você ainda está conectado!

Solicitar o token de cliente do usuário
É possível acessar o token de cliente do usuário a partir de uma URL como dados JSON. Portanto, edite public/javascripts/chat.js e altere o authenticateUser método para que ele busque esses dados, a fim de utilizá-los ao se conectar à conversa.
// ...
authenticateUser() {
var req = new XMLHttpRequest();
req.responseType = 'json';
req.open('GET', '/jwt', true);
var obj = this;
req.onload = function() {
obj.joinConversation(req.response);
};
req.send(null);
}
// ...Reinicie o aplicativo, faça a autenticação e divirta-se com uma partida rápida de “Encontre as diferenças”!

Veja bem, agora você está conectado como um usuário diferente. As mensagens de outros usuários têm um formato diferente. Portanto, quando você entrar na conversa, ficará assim.

Enviar o link mágico por e-mail
Você tem um link mágico, mas ele ainda é exibido no console. É hora de enviá-lo por e-mail.
Instalar e configurar uma biblioteca SMTP
Instale nodemailer agora com este comando.
Configure algumas variáveis para a nodemailer biblioteca dentro do seu arquivo de ambiente .env.
Se você estiver usando o Google ou outro provedor de e-mail conhecido com a verificação em duas etapas ativada, provavelmente precisará configurar uma senha de aplicativo. Isso permitirá que você se autentique pelo aplicativo sem a necessidade da verificação em duas etapas.
Crie um novo arquivo de utilitário que irá configurar nodemailer em util/mailer.js com este código:
const mailer = require('nodemailer');
require('dotenv').config();
let options = {
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true,
auth: {
user: process.env.SMTP_AUTH_USER,
pass: process.env.SMTP_AUTH_PASS
}
};
module.exports = mailer.createTransport(options); Enviar links mágicos por e-mail
A edição final routes/webhook.js será adicionar a sendEmail função e usá-la para substituir os console.log comandos completamente.
// ...
var mailer = require('../util/mailer');
// ...
var sendEmail = (magicLink, email) => {
var mailOptions = {
to: email,
subject: 'Magic Link',
text: 'Click to login: ' + magicLink,
html: `<a href="${magicLink}">Click to Login</a>`
};
mailer.sendMail(mailOptions);
}
/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
// ...
if (null === user.user_id) {
// ...
// ...
user.save((err) => {
// ...
sendEmail(createMagicLink(req, user.toObject()), user.email);
res.sendStatus(200);
});
// ...
// ...
} else {
sendEmail(createMagicLink(req, user.toObject()), user.email);
res.sendStatus(200);
}
// ...
});
// ...
Para o último tipo, reinicie o aplicativo e envie uma solicitação de webhook usando os dados do Typeform.
Se tudo estiver funcionando conforme o esperado, você receberá um e-mail no endereço que você informou ao Typeform com um link mágico. Clique no link mágico para se autenticar no aplicativo e participar da conversa.
É hora de convidar alguns amigos!

É isso aí, pessoal!
Se você estiver interessado em saber como a interface do usuário deste tutorial foi criada, confira meu post mais recente Crie uma interface de usuário simples para mensagens com o Bootstrap.
Além disso, aqui estão alguns pontos a serem considerados caso você esteja desenvolvendo isso para uso na prática:
Use um formulário separado para lidar com a autenticação depois que o usuário já tiver se cadastrado.
Capture um nome de exibição e uma foto de perfil no seu Typeform.
Use uma string opaca revogável em vez de um JWT dentro de um link mágico.
Permita que os usuários atualizem seus dados após a autenticação.
Mostrar todos os usuários que estão online no momento no menu lateral.
Permitir que os usuários saiam da conta.
Permita que os usuários excluam mensagens.
Permitir que os usuários compartilhem arquivos de mídia.
Expandir URLs compartilhadas como visualizações.
Se você quiser habilitar o áudio em um aplicativo de bate-papo já existente como este, pode conferir meu guia sobre Adicionar funcionalidade de Voice a um aplicativo de bate-papo já existente.
Obrigado pela leitura e me diga o que você achou no Slack da Comunidade ou na seção de comentários abaixo 👇
Compartilhar:
Educador de tecnologia com um jeito amigável, pai de família, defensor da diversidade, provavelmente discuto um pouco demais. Ex-engenheiro de backend. Fale comigo sobre JavaScript (front-end ou back-end), o incrível Vue.js, DevOps, DevSecOps e tudo relacionado ao JamStack. Escritor no DEV.to