Most technology applications we use today integrate two-step authentication to improve security and prevent unauthorized access to private data. In this article, I want to show you how to implement two-step authentication in your web applications using your regular authentication method and OTPs (one-time passwords).
I will be using NodeJs and Express in the small application we will build but I will not be using a database; also, I will not be building a frontend for this, we will test everything from Postman (or you can use any api test client of your choice), these are to enable us to go straight to the point and reduce redundancy.
For the OTP, we will be integrating with Termii and the Termii Token API to be specific. This is because we would obviously need a messaging solution to handle the sending and delivery of SMS and Termii is just perfect for its simplicity.
Prerequisites
In order to successfully complete this guide, you need:
- basic understanding of NodeJs and Javascript (ES6).
- an existing Node.js project. You can generate a REST skeleton using Express Generator.
- Node.js installed on your machine
- Postman client installed on your machine.
Setting Up — Project
use express-generator to generate a skeleton app :
npx express-generator --no-view --git otp-app
$ cd otp-app
$ npm install
- Create a Services folder and a Controller folder in your project folder, this is a pattern I like to work with personally, you can use any other if you feel like it.
- create a .env file in your project directory and run the following:
npm i dotenv
also, open up app.js and add this in as early as possible:
require('dotenv').config();
We will install a NodeJS SDK for the Termii APIs, this will allow us to reduce 10 lines of code to maybe just 1 while integrating. Run the following in your terminal.
npm i termii-nodejs
With these, we’re good to go, let’s move on to setting up and getting an API key from Termii.
Setting Up — Termii
- Visit termii.com and sign up. Once that is done, you can find your API key on your dashboard. Copy and transfer it to your env file. Your env should look like this:
SENDER_ID=your_sender_id_here
API_KEY=your_API_key_here
- Let’s sort out the Sender ID bit now. A Sender ID is a name or number that identifies the sender of an SMS message. You cannot send SMS without registering one, the good news is, you can register one on your dashboard and have it approved within 24 hours. I have registered one already named “Octopii”. If you, however, need to test before your sender ID is approved then use the Sandbox feature accessible from your dashboard.
- Once we’ve done all these, we’re ready to implement two-step authentication anywhere in our application.
Sending OTPs
Let’s start by configuring the routes. I will only be using the routes/index.js
file. modify this file to look like this:
var express = require('express');
var router = express.Router();
const otp = require('../controllers/otpController');
router.post('/send-otp', otp.sendOtp);
router.post('/verify-otp', otp.verifyOtp);
router.get('/balance', otp.checkBalance);
module.exports = router;
Here I defined 3 routes, actually, we only need 2 (the signup endpoint that sends the OTP and the verification endpoint), the balance endpoint is just nice to have, yunno, so we can always check how much credit we have left in our wallet.
As you must have guessed, now that we imported /controllers/otpController.js
, we do need to create it inside the controllers folder.
const otpService = require('../services/otpService');
const sendOtp = async(req, res) => {
const name = req.body.name
const phoneNumber = req.body.phone
const response = await otpService.sendOtp(name, phoneNumber);
res.status(200).json(response);
}
const verifyOtp = async(req, res) => {
}
const checkBalance = async(req, res) => {
}
module.exports = {
sendOtp,
verifyOtp,
checkBalance
}
Here, we only want to process the name and phone number of the new user, in a real application, you’d probably have many more fields but this is concise because these are the only fields we need for what we’re trying to achieve. We then call the sendOtp
method in otpService
, we’ll be creating that right now.
Create a file titled otpService.js
in the services folder and put this in:
const Termii = require("termii-nodejs").Termii;
const sender_id = process.env.SENDER_ID;
const api_key = process.env.API_KEY;
const termii = new Termii({
api_key: api_key,
sender_id: sender_id,
pin_time: 5,
});
const sendOtp = (name, phoneNumber) => {
const pinPlaceholder = '< 1234 >';
const message = `Hello ${name}, your OTP is ${pinPlaceholder}. This pin will expire in 1 minute.`;
try{
const response = termii.sendToken(phoneNumber, pinPlaceholder, message);
return response;
}
catch(err){
throw err;
}
}
A number of things are happening here, first, we call and initialize the termii-nodejs SDK
using our sender_id and api_key in our env file. make sure to provide correct details or your requests will return “unauthorized”.
The Termii Token API has a number of required parameters to send an OTP like the type of pin which can be numeric or alphanumeric, length of the pin, pin time (I set it for 5 minutes in my example), etc; you can see a full list of them here. The SDK however can set the defaults for us and we only need to set the pin placeholder and the actual message, then make a call to the SDK sendToken
method.
If we run npm start and make a call to localhost:3000/send-otp
with the appropriate payload we should get something similar to this:
That’s a 200 OK HTTP status which means our OTP has just been sent! make sure to note the pinId
in the response, you need it to verify this OTP, ideally, you should persist this in a database and retrieve it when you need to but I will be passing it in the request I make to the verify endpoint.
You should receive the SMS on your phone now. If you don’t, there’s a chance that DND (Do-Not-Disturb) is activated on your phone number and this sender ID delivers only on Non-DND numbers. OTPs generally should only be sent on DND-enabled sender IDs since they’re so important however DND routes are a little more expensive. To get this activated on your sender ID, reach out to the customer success team at Termii, they’re very responsive.
Verifying OTPs
Everything being equal we’re almost there. Let’s add the following to the otpController.js
file:
const verifyOtp = async(req, res) => {
const pin = req.body.pin;
const pinId = req.body.pinId;
const response = await otpService.verifyOtp(pinId, pin);
res.status(200).json(response);
}
Let’s also modify the otpService.js
file again and add:
const verifyOtp = (pindId, pin) => {
try{
const response = termii.verifyToken(pindId, pin);
return response;
}
catch(err){
throw err;
}
}
If we call http://localhost:3000/verify-otp
then we should have a working 2-step authentication fully implemented now.
There you have it, we have successfully implemented two-step authentication into our web application.
To see the full working code, you can check out the project repo on Github.
Where to go from here
For starters, now that you’ve verified this user, you can then go on to save the user or carry out whatever else you need to do.
You can also delve into the Termii official documentation to see other solutions like voice tokens and how you can integrate them with your application.
Top comments (0)