
Partager:
Hui Jing est développeuse chez Nexmo. Elle a un amour immodéré pour le CSS et la typographie, et est généralement passionnée par tout ce qui touche au web.
Comment ajouter l'authentification à deux facteurs avec Node.js et Koa.js
Temps de lecture : 14 minutes
L'authentification à deux facteurs (2FA) tire son nom du fait que vous avez besoin de deux choses pour vérifier votre identité. Quelque chose que vous connaissez, comme un mot de passe, et quelque chose que vous avez, comme le code de vérification de votre appareil mobile ou un jeton physique.
Ajouter 2FA à votre application n'a pas à être une corvée difficile. Ce tutoriel abordera la manière de mettre en œuvre 2FA pour vos applications et services web pour une couche de sécurité supplémentaire avec l'aide de l'API Verify de Vonage. Nous allons construire une simple application Koa.js pour comprendre comment fonctionne le mécanisme sous-jacent. Il sera ainsi plus facile de voir comment cela s'intègre dans vos propres projets existants, même si vous n'utilisez pas Koa.js.
Ce tutoriel explique comment mettre en œuvre un système de jetons de vérification avec l'API Verify de Vonage et Koa.js. Nous avons un tutoriel Node.js similaire utilisant Express.js. vous pouvez le trouver ici.
Vous commencez par une page de connexion qui demande à l'utilisateur son numéro de téléphone portable. Une fois ce numéro communiqué, l'utilisateur est invité à saisir un code de vérification qui lui est envoyé par SMS sur son téléphone portable. Une fois cette étape franchie, l'utilisateur peut accéder à l'application.
Conditions préalables
Compréhension de base de Javascript
Node.js installé sur votre machine
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.
Une fois que vous avez un compte API Vonage, vous pouvez trouver votre clé API et votre secret API en haut du tableau de bord de l'API de Vonage.
Ce tutoriel vous guidera à travers le processus en partant de zéro. Si vous souhaitez voir le code fini, vous pouvez cloner le dépôt dépôt git pour ce projet. Nous avons également une version Glitch, qui a un design plus exagéré, et vous pouvez remixer remixer. Notez qu'il y a de légères différences dans l'implémentation de Glitch pour tenir compte de la façon dont les projets sont hébergés sur la plateforme.
Glitch version of demo
Démarrer un projet Koa.js à partir de zéro
Créez un dossier de projet sur votre machine locale, puis exécutez la commande suivante pour créer un nouveau projet Node.js.
Cela déclenchera une série d'invites qui généreront votre fichier package.json fichier. Vous pouvez choisir de laisser les réponses en blanc pour utiliser les valeurs par défaut si vous le souhaitez.
Configuring package.json
Ensuite, installez Koa.js. Notez que Koa nécessite node v7.6.0 ou plus pour le support de ES2015 et des fonctions asynchrones.
Créez un fichier server.js dans le dossier de votre projet.
Collez le code suivant dans votre fichier nouvellement créé.
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)
})
Exécuter le server.js fichier.
Si vous naviguez vers http://localhost:3000 dans votre navigateur, vous devriez voir une page vide avec le texte "Hello Unicorn 🦄".
Check that server is running
Vous devez également installer dotenvqui vous permet de charger les variables d'environnement stockées dans un fichier .env dans un fichier process.env.
Vous pouvez maintenant créer le fichier .env qui doit contenir au moins les variables suivantes :
Pour accéder aux variables d'environnement, vous devez les exiger, idéalement au début de votre fichier server.js fichier.
require('dotenv').config()Si vous n'avez pas ouvert un compte Nexmo c'est le bon moment pour le faire. Une fois que vous vous êtes connecté au tableau de bord, vos identifiants API devraient être la première chose que vous voyez. Veillez à mettre votre clé et votre secret entre guillemets.
Structure du projet
À l'heure actuelle, votre projet ne comporte probablement que les éléments suivants : a package.json, a server.js et un fichier .env et un fichier . Mettons en place la structure du projet pour que vous puissiez avoir une interface de base avec laquelle les utilisateurs pourront interagir.
PROJECT_NAME/
|-- public/
| |-- client.js
| `-- style.css
|-- views/
| `-- index.html
|-- .env
|-- package.json
`-- server.jsDans ce cas, vous devrez apporter quelques modifications au fichier server.js pour servir le fichier index.html et les ressources associées, au lieu d'une simple ligne de texte. Koa.js est un framework assez dépouillé, donc toutes les fonctionnalités supplémentaires pour le routage ou le service de ressources statiques doivent être installées séparément. Voici la liste des modules additionnels et leur utilisation :
koa-staticpour servir des fichiers statiqueskoa-bodyparserpour traiter les données envoyées via des requêtes POSTkoa-routerpour le routagekoa-viewspour rendre les modèles
Cet exemple utilise également Nunjucks pour rendre les fichiers modèles. L'API Verify de Vonage sera utilisée pour déclencher le code de vérification par SMS, vous devrez donc également installer la bibliothèque client Node.js de Vonage.
Servir les actifs statiques et les fichiers HTML
Pour permettre à l'application de servir des ressources statiques, telles que des feuilles de style et du JavaScript côté client, hors du répertoire /public vous pouvez ajouter ce qui suit au fichier server.js ce qui suit :
const serve = require('koa-static')
app.use(serve('./public'))Pour servir des fichiers HTML à partir du répertoire /views vous pouvez utiliser koa-viewsqui fournit une fonction render() qui fournit une fonction Le moteur de templating utilisé dans cet exemple est Nunjucks, mais vous êtes libre de choisir le moteur de templating qui vous convient le mieux.
const views = require('koa-views')
app.use(views('./views', { map: { html: 'nunjucks' }}))La prochaine chose à mettre en place serait quelques routes de base pour servir les pages de votre application.
const Router = require('koa-router')
const router = new Router()
router.get('/', (ctx, next) => {
return ctx.render('./index')
})
app.use(router.routes()).use(router.allowedMethods())
Pour cet exemple, vous aurez besoin de 3 pages, la index.html comme page d'atterrissage principale, verify.html pour que les utilisateurs puissent saisir leur code de vérification et result.html pour indiquer si la vérification a réussi ou non.
La structure du formulaire web est relativement simple, et vous êtes libre de l'agrémenter de CSS comme vous le souhaitez.
<form method="post" action="verify">
<input name="phone" type="tel" placeholder="+6588888888">
<button>Get OTP</button>
</form>Ce formulaire affichera les entrées de l'utilisateur dans la route /verify et vous pouvez utiliser le numéro de téléphone dans l'entrée pour déclencher la demande de code de vérification. Un formulaire similaire peut être utilisé pour les deux autres itinéraires de /check et /cancel également.
<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> Traitement des données de l'utilisateur
Ensuite, pour gérer les entrées des utilisateurs via les formulaires web, vous aurez besoin de quelques routes pour gérer les demandes de POST pour traiter les requêtes. Veillez à déclarer bodyparser() avant chaque route.
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 */
})
Maintenant que vous êtes en mesure de recevoir le numéro de téléphone de votre utilisateur, vous devez utiliser l'API Verify pour lui envoyer un code PIN. Initialisez une nouvelle instance Nexmo avec vos identifiants API Vonage.
const Nexmo = require('nexmo');
const nexmo = new Nexmo({
apiKey: YOUR_API_KEY,
apiSecret: YOUR_API_SECRET
});Il y a trois fonctions dont nous devons nous occuper. La première consiste à déclencher le code de vérification avec la fonction nexmo.verify.request() . Elle implique le numéro de téléphone de l'utilisateur. Elle implique le numéro de téléphone de l'utilisateur, et une chaîne pour le nom de la marque qui sera affichée à l'utilisateur en tant qu'expéditeur.
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)
}
})
})
}
Une fois que votre utilisateur a reçu le code PIN par SMS, il doit le soumettre à la fonction nexmo.verify.check() afin qu'il soit vérifié. Vous remarquerez un request_id paramètre. Cette valeur est obtenue lorsque le code PIN a été déclenché avec succès. Il existe plusieurs façons de transmettre l'identifiant de la demande à la fonction nexmo.verify.check() et cet exemple utilise un champ caché dans la fonction vérification du formulaire de contrôle.
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 dernière fonction permet à l'utilisateur d'annuler la vérification s'il a changé d'avis. Elle utilise la fonction nexmo.verify.control() et nécessite à nouveau l'ID de la demande généré par le déclenchement du code PIN et une chaîne de caractères de la valeur 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
Vous devez maintenant utiliser ces trois fonctions dans les itinéraires que nous avons spécifiés précédemment, en commençant par celui qui permet de déclencher le code de vérification.
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 })
})
Les ctx.request.body ressemblera à ceci :
{
"phone": "+40987654321"
}Vous pouvez récupérer ce numéro de téléphone et le transmettre à la fonction verify() à la fonction S'il s'agit d'un numéro de téléphone valide, le code de vérification sera déclenché et vous recevrez une réponse contenant un request_id et status.
{
"request_id": "1bf002ecd1e94d8aa81ba7463b19f583",
"status": "0"
}À partir de là, vous pouvez envoyer l'identifiant de la demande au frontend, qui l'utilisera lorsque l'utilisateur saisira le code de vérification.
The request_id is passed to the frontend
Lorsque l'utilisateur soumet le bon code PIN, vous devez insérer le code PIN et l'identifiant de la demande dans la fonction check() dans la fonction
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 })
})
Là encore, ces deux valeurs peuvent être obtenues à partir de la page d'accueil. ctx.request.body et si le code PIN est validé comme étant correct, vous recevrez une réponse qui ressemblera à celle-ci :
{
"request_id": "1bf002ecd1e94d8aa81ba7463b19f583",
"status": "0",
"event_id": "150000001AC57AB2",
"price": "0.10000000",
"currency": "EUR"
}Vous pouvez ensuite utiliser le code d'état pour déterminer le message que vous souhaitez afficher à l'utilisateur. Cet exemple utilise Nunjucks, de sorte que le balisage de la page de résultats pourrait ressembler à ceci :
{% 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
Il s'agit d'une description détaillée de chaque partie du code, mais pour voir à quoi ressemble l'application dans son intégralité, consultez le code source sur GitHub.
Autres éléments à prendre en compte
Ce tutoriel est une version simplifiée, qui ne met en évidence que les éléments nécessaires à la mise en œuvre de l'authentification à deux facteurs. Mais il y a de nombreuses choses dont il faut s'occuper dans une application réelle. L'une des plus importantes est la gestion des erreurs. L'API Verify renvoie une valeur d'état de 0 pour les requêtes réussies, mais toute autre valeur indique une erreur.
Ces erreurs doivent être traitées et l'interface utilisateur sur le frontend doit refléter toute erreur potentielle empêchant une vérification réussie. Il peut également être judicieux de mettre en œuvre une sorte de validation frontale, ou même d'utiliser l'API Number Insight API de Vonage Number Insight de Vonage pour s'assurer que seuls des numéros de téléphone valides sont transmis à l'API Verify.
Quelle est la prochaine étape ?
Si vous souhaitez en savoir plus sur ces API, voici quelques liens qui pourraient vous être utiles :
Documentation pour l'API Verify sur le portail des développeurs
Série de tutoriels pour diverses API de Vonage
Si vous avez besoin de nous, essayez le canal Slack de la canal Slack de la communauté des développeurs de Vonage
Dites-nous ce que vous en pensez en tweetant à @VonageDev
