Two-factor authentication (2FA) is a security process in which a user provides two different authentication factors to verify their identity. In this section, we’ll go over how to implement 2FA in a JavaScript/Node.js application using the speakeasy
library.
First, let’s start by installing speakeasy
and any other dependencies you might need. You can do this by running the following command:
npm install speakeasy
Next, let’s import the speakeasy
library and generate a secret key for 2FA. The secret key is a unique string that is shared between the server and the client, and is used to generate and verify 2FA codes.
const speakeasy = require('speakeasy');
// Generate a secret key for 2FA
const secret = speakeasy.generateSecret({ length: 20 });
console.log(secret.base32); // Outputs the secret key in base32 format
Now, let’s create a function to generate a 2FA code based on the secret key. We can use the totp
method from speakeasy
to do this:
function generateCode() {
// Generate a 2FA code based on the secret key
const code = speakeasy.totp({
secret: secret.base32,
encoding: 'base32'
});
console.log(code); // Outputs the 2FA code
}
On the client side, we’ll need to display the secret key to the user and provide a way for them to enter the 2FA code. We can use a QR code to make it easier for the user to enter the secret key into their 2FA app, and a form to allow them to enter the 2FA code when prompted.
To display the QR code on the client side, we can use the qrcode
library to generate a QR code image from the secret key. Here’s an example of how you might do this:
import QRCode from 'qrcode';
// Generate a QR code image from the secret key
QRCode.toDataURL(secret.otpauth_url, (err, imageUrl) => {
// Render the QR code image in an img element
const qrCodeImg = document.getElementById('qr-code');
qrCodeImg.src = imageUrl;
});
To allow the user to enter the 2FA code, we can create a form with an input field and a submit button. When the user submits the form, we’ll send the 2FA code to the server to be verified:
<form id="2fa-form">
<label for="code">Enter 2FA code:</label>
<input type="text" id="code" name="code">
<button type="submit">Verify</button>
</form>
For the client side to send the 2FA code to the server, we’ll need to implement the /verify-code
endpoint on the server side to receive and verify the code.
Here’s an example of what the JavaScript code on the client side might look like to display a QR code and allow the user to enter the 2FA code:
import QRCode from 'qrcode';
// Generate a QR code image from the secret key
QRCode.toDataURL(secret.otpauth_url, (err, imageUrl) => {
// Render the QR code image in an img element
const qrCodeImg = document.getElementById('qr-code');
qrCodeImg.src = imageUrl;
});
const form = document.getElementById('2fa-form');
form.addEventListener('submit', event => {
event.preventDefault();
const code = document.getElementById('code').value;
// Send the 2FA code to the server to be verified
verifyCode(code);
});
async function verifyCode(code) {
const response = await fetch('/verify-code', {
method: 'POST',
body: JSON.stringify({ code: code }),
headers: { 'Content-Type': 'application/json' }
});
if (response.ok) {
console.log('2FA code verified');
// You might want to redirect the user to a different page here, or show a success message
} else {
console.error('2FA code verification failed');
}
}
This code imports the qrcode
library to generate a QR code image from the secret key, and creates a form to allow the user to enter the 2FA code. When the form is submitted, it sends the 2FA code to the server to be verified using the fetch
API.
On the server side here’s how you would verify the client’s request using the speakeasy
library in a Node.js/Express application:
const express = require('express');
const speakeasy = require('speakeasy');
const app = express();
app.use(express.json()); // Parse JSON request body
app.post('/verify-code', (req, res) => {
const code = req.body.code;
// Verify the 2FA code
const verified = speakeasy.totp.verify({
secret: secret.base32, // The secret key we generated earlier
encoding: 'base32',
token: code
});
if (verified) {
// Save the verified 2FA code in the database or perform any other necessary actions
saveCode(code);
res.sendStatus(200);
} else {
res.sendStatus(400);
}
});
function saveCode(code) {
// Save the verified 2FA code in the database or perform any other necessary actions
}
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
This should provide a basic implementation of 2FA in a JavaScript/Node.js application using the speakeasy
library.