
Compartir:
Hui Jing es defensora de los desarrolladores en Nexmo. Tiene un amor desmesurado por CSS y la tipografía, y en general es una apasionada de todo lo relacionado con la web.
Cómo añadir autenticación de dos factores con Node.js y Koa.js
Tiempo de lectura: 14 minutos
La autenticación de dos factores (2FA) recibe su nombre del hecho de que necesitas dos cosas para verificar tu identidad. Algo que sabes, como una contraseña, y algo que tienes, como el código de verificación de tu dispositivo móvil o token físico.
Agregar 2FA a tu aplicación no tiene que ser una tarea difícil. Este tutorial cubrirá cómo implementar 2FA para tus aplicaciones y servicios web para una capa adicional de seguridad con la ayuda de Vonage Verify API. Construiremos una simple aplicación Koa.js para entender cómo funciona el mecanismo subyacente. Esto hará más fácil ver cómo esto encajará en tus propios proyectos existentes, incluso si no estás usando Koa.js.
Este tutorial cubrirá cómo implementar un sistema de token de verificación con Vonage Verify API y Koa.js. Tenemos un tutorial similar en Node.js usando Express.js - puedes encontrarlo aquí.
Empezarás con una página de inicio de sesión en la que se pedirá al usuario un número de teléfono móvil. Una vez introducido, se le pedirá que introduzca un código de verificación que se enviará a su número de teléfono móvil por SMS. Una vez hecho esto, podrán acceder a la aplicación.
Requisitos previos
Conocimientos básicos de Javascript
Node.js instalado en su máquina
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.
Una vez que tengas una cuenta de API de Vonage, podrás encontrar tu clave y secreto de API en la parte superior del Panel de API de Vonage.
Este tutorial te llevará a través del proceso desde cero. Si quieres ver el código terminado, puedes clonar el repositorio repositorio git de este proyecto. También tenemos una versión Glitch, que tiene un diseño más exagerado, y que puedes remezclar también. Tenga en cuenta que son ligeras diferencias para la implementación Glitch para atender a cómo los proyectos están alojados en la plataforma.
Glitch version of demo
Empezar un proyecto Koa.js desde cero
Crea una carpeta de proyecto en tu máquina local y ejecuta el siguiente comando para configurar un nuevo proyecto Node.js.
Esto activará una serie de preguntas que generarán su archivo package.json archivo. Si lo desea, puede dejar las respuestas en blanco para utilizar los valores por defecto.
Configuring package.json
A continuación, instale Koa.js. Ten en cuenta que Koa requiere node v7.6.0 o superior para ES2015 y soporte de funciones async.
Cree un server.js en la carpeta del proyecto.
Pegue el siguiente código en el archivo recién creado.
const Koa = require('koa')
const port = process.env.PORT || 3000
const app = new Koa()
app.use(async ctx => {
ctx.body = 'Hello Unicorn 🦄'
})
const listener = app.listen(port, function() {
console.log('Your app is listening on port ' + listener.address().port)
})
Ejecute el server.js archivo.
Si navega a http://localhost:3000 desde tu navegador, deberías ver una página vacía con el texto "Hola Unicornio 🦄".
Check that server is running
También debe instalar dotenvque le permite cargar variables de entorno almacenadas en un archivo .env en un archivo process.env.
Y ahora puede crear el archivo .env y debe contener al menos las siguientes variables:
Para acceder a las variables de entorno, tendrás que requerirlo, idealmente en la parte superior de tu archivo server.js archivo.
require('dotenv').config()Si no se ha cuenta en Nexmo ahora es un buen momento para hacerlo. Una vez que haya iniciado sesión en el panel de control, sus credenciales de la API debe ser la primera cosa que usted ve. Asegúrate de escribir la clave y el secreto entre comillas.
Estructura del proyecto
En este momento, su proyecto probablemente sólo tendría un package.json, a server.js y un archivo .env archivo. Vamos a configurar la estructura del proyecto para que puedas tener un frontend básico con el que los usuarios puedan interactuar.
PROJECT_NAME/
|-- public/
| |-- client.js
| `-- style.css
|-- views/
| `-- index.html
|-- .env
|-- package.json
`-- server.jsCon eso, tendrás que hacer algunos ajustes en el archivo server.js para servir el archivo index.html y los activos relacionados, en lugar de simplemente una línea de texto. Koa.js es un framework bastante básico, por lo que cualquier funcionalidad adicional para enrutar o servir activos estáticos necesita ser instalada por separado. Aquí está la lista de módulos adicionales y sus usos:
koa-staticpara servir activos estáticoskoa-bodyparserpara gestionar los datos enviados a través de peticiones POSTkoa-routerpara el enrutamientokoa-viewspara renderizar plantillas
Este ejemplo también utiliza Nunjucks para generar archivos de plantilla. La API Verify de Vonage se utilizará para activar el código de verificación a través de SMS, por lo que también deberás instalar la biblioteca cliente Node.js de Vonage.
Servir activos estáticos y archivos HTML
Para permitir que la aplicación sirva activos estáticos, como hojas de estilo y JavaScript del lado del cliente, fuera del directorio /público puede añadir lo siguiente al archivo server.js archivo:
const serve = require('koa-static')
app.use(serve('./public'))Para servir archivos HTML desde el directorio /vistas puede utilizar koa-viewsque proporciona una función render() función. El motor de plantillas utilizado en este ejemplo es Nunjucks, pero usted es libre de elegir cualquier motor de plantillas que funcione mejor para usted.
const views = require('koa-views')
app.use(views('./views', { map: { html: 'nunjucks' }}))Lo siguiente a configurar serían algunas rutas básicas para servir las páginas de su aplicación.
const Router = require('koa-router')
const router = new Router()
router.get('/', (ctx, next) => {
return ctx.render('./index')
})
app.use(router.routes()).use(router.allowedMethods())
Para este ejemplo, necesitará 3 páginas, la index.html como página de destino principal, verify.html para que los usuarios introduzcan su código de verificación y result.html para mostrar si la verificación se ha realizado correctamente o no.
La estructura del formulario web es bastante sencilla, y usted es libre de adornarlo con CSS como desee.
<form method="post" action="verify">
<input name="phone" type="tel" placeholder="+6588888888">
<button>Get OTP</button>
</form>Este formulario enviará las entradas del usuario a la ruta /verify y puede utilizar el número de teléfono en la entrada para activar la solicitud de código de verificación. Un formulario similar se puede utilizar para las otras 2 rutas para /check y /cancel también.
<form method="post" action="check">
<input name="pin" placeholder="Enter PIN">
<input name="reqId" type="hidden" value="{{ reqId }}">
<button>Verify</button>
</form><form method="post" action="cancel">
<input name="reqId" type="hidden" value="{{ reqId }}">
<button class="inline">Cancel verification</button>
</form> Tratamiento de las entradas de usuario
Luego, para manejar las entradas del usuario a través de formularios web, necesitará algunas rutas para manejar POST también. Asegúrese de declarar bodyparser() antes de cualquiera de las rutas.
const bodyParser = require('koa-bodyparser')
/* This should appear before any routes */
app.use(bodyParser())
router.post('/verify/', async (ctx, next) => {
const payload = await ctx.request.body
/* Function to trigger verification code here */
})
router.post('/check/', async (ctx, next) => {
const payload = await ctx.request.body
/* Function to check verification code here */
})
router.post('/cancel/', async (ctx, next) => {
const payload = await ctx.request.body
/* Function to cancel verification code here */
})
Ahora que puedes recibir el número de teléfono de tu usuario, necesitarás usar Verify API para enviarle un código PIN. Inicializa una nueva instancia de Nexmo con tus credenciales de API de Vonage.
const Nexmo = require('nexmo');
const nexmo = new Nexmo({
apiKey: YOUR_API_KEY,
apiSecret: YOUR_API_SECRET
});Hay 3 funciones de las que debemos ocuparnos. La primera es activar el código de verificación con la función nexmo.verify.request() función. Implica el número de teléfono del usuario, y una cadena para el nombre de la marca que se mostrará al usuario como remitente.
async function verify(number) {
return new Promise(function(resolve, reject) {
nexmo.verify.request({
number: number,
brand: process.env.NEXMO_BRAND_NAME
}, (err, result) => {
if (err) {
console.error(err)
reject(err)
} else {
resolve(result)
}
})
})
}
Una vez que el usuario haya recibido el código PIN por SMS, deberá enviarlo a la función nexmo.verify.check() para que pueda ser verificado. Observará un parámetro request_id parámetro. Este valor se obtiene cuando el código PIN se ha activado correctamente. Hay varias formas de pasar el ID de la solicitud a la función nexmo.verify.check() y este ejemplo utiliza un campo oculto en la función comprobar formulario.
async function check(reqId, code) {
return new Promise(function(resolve, reject) {
nexmo.verify.check({
request_id: reqId,
code: code
}, (err, result) => {
if (err) {
console.error(err)
reject(err)
} else {
resolve(result)
}
})
})
}
La última función ofrece al usuario la opción de cancelar la verificación si ha cambiado de opinión. Utiliza la función nexmo.verify.control() y, de nuevo, requiere el ID de solicitud generado al activar el código PIN y un valor de cadena de cancel.
async function cancel(reqId) {
return new Promise(function(resolve, reject) {
nexmo.verify.control({
request_id: reqId,
cmd: 'cancel'
}, (err, result) => {
if (err) {
console.error(err)
reject(err)
} else {
resolve(result)
}
})
})
}
Landing page for demo
Ahora necesitas hacer uso de estas 3 funciones en las rutas que especificamos anteriormente, comenzando primero con la que activa el código de verificación.
router.post('/verify/', async (ctx, next) => {
const payload = await ctx.request.body
const phone = payload.phone
const result = await verify(phone)
const reqId = result.request_id
ctx.status = 200
return ctx.render('./verify', { reqId: reqId })
})
La página ctx.request.body será algo parecido a esto:
{
"phone": "+40987654321"
}Puedes coger ese número de teléfono y pasarlo a la función verify() función. Siempre que sea un número de teléfono válido, se disparará el código de verificación y recibirás una respuesta que contendrá un icono request_id y status.
{
"request_id": "1bf002ecd1e94d8aa81ba7463b19f583",
"status": "0"
}A partir de ahí, puede enviar el ID de solicitud al frontend para que lo utilice cuando el usuario introduzca el código de verificación.
The request_id is passed to the frontend
Cuando el usuario introduzca el PIN correcto, deberá introducir tanto el PIN como el ID de la solicitud en la función check() función
router.post('/check/', async (ctx, next) => {
const payload = await ctx.request.body
const code = payload.pin
const reqId = payload.reqId
const result = await check(reqId, code)
const status = result.status
ctx.status = 200
return ctx.render('./result', { status: status })
})
De nuevo, ambos valores se pueden obtener del ctx.request.body y si se valida que el PIN es correcto, recibirá una respuesta parecida a ésta:
{
"request_id": "1bf002ecd1e94d8aa81ba7463b19f583",
"status": "0",
"event_id": "150000001AC57AB2",
"price": "0.10000000",
"currency": "EUR"
}A continuación, puede utilizar el código de estado para determinar el mensaje que desea mostrar al usuario. Este ejemplo utiliza Nunjucks, por lo que el marcado en la página de resultados podría ser algo como esto:
{% if status == 0 %}
<p>Code verified successfully. ¯\_(ツ)_/¯</p>
{% else %}
<p>Something went wrong… ಠ_ಠ</p>
<p>Please contact the administrator for more information.</p>
{% endif %}
Verification messages
Este ha sido un desglose exhaustivo de cada parte del código, pero para ver el aspecto de la aplicación en su totalidad, echa un vistazo al código fuente en GitHub.
Otras cosas que hay que tener en cuenta
Este tutorial es una versión simplificada, destacando sólo los bits necesarios para implementar la autenticación de dos factores. Pero hay numerosas cosas de las que hay que ocuparse en una aplicación real. Una de las más importantes es la gestión de errores. La Verify API devuelve un valor de estado de 0 para las consultas correctas, pero cualquier otro valor indica un error.
Estos errores deben ser manejados y la interfaz de usuario en el frontend debe reflejar cualquier error potencial que impida una verificación exitosa. También podría ser una buena idea implementar algún tipo de validación de frontend, o incluso utilizar la Number Insight API de Vonage de Vonage para garantizar que sólo se pasen números de teléfono válidos a Verify API.
¿Y ahora qué?
Si quieres saber más sobre estas API, aquí tienes algunos enlaces que pueden resultarte útiles:
Documentación de Verify API en el portal para desarrolladores
Serie de tutoriales para varias API de Vonage
Si nos necesitas, prueba el canal Slack de la comunidad de desarrolladores de Vonage
Háganos saber lo que piensa tuiteando en @VonageDev
