We have already covered how NodeJs Sms verification system works in detail. But in this article, we will implement how Nodejs phone call verification system works. For example number of social media sites including Facebook, linkedIn, and different banks also use this method to verify user’s Phone number by making the call on that number.
In this article, We will implement a Nodejs application which will verify user’s mobile number using OTP by making a call on that number. Here, We will use Express for Nodejs,MongoDB to store user’s detail and at Client Side, we will use AngularJs.
1. How It Works
To understand how Nodejs phone call verification system works, take a look at below diagram.
Nodejs phone call verification
- We will Store user’s information along with user’s mobile number.
- Whenever a user tries to verify her/his mobile number we will initiate a request to third party tool to make a call with randomly generated OTP.
- The third party tool will make a call with OTP to user’s mobile number.
- Than OTP will be entered by the user for verification and after verification, we will activate user’s mobile number.
Above line describes how we will create our Nodejs phone call verification application.
2. Signing up with Voice Gateway
To create Nodejs phone call verification we have to buy one Voice Gateway (VoIP). A VoIP gateway is used to transmit and receive voice communications (VoIP) over the internet by using Internet Protocols. I will not go much deep into VoIP as it is out of the scope of this article hence we will stick to the topic.
When I was implementing this application, I was searching the Voice Gateway(VoIP) and I found one called Twilio. Twilio is not free to use in production, you have to pay some amount in order to use in production. However, you can test your application by using their magic numbers.
2.1 Obtaining Twilio credentials:
I personally think that Twilio is a good choice to make an application like Nodejs phone call verification or something like that. To get the Twilio credentials you have to create an account by visiting this link and you will be taken to the Twilio console. To get the API credential go to account settings and you should see the below image.
In the below image you will find Live Credentials and Test Credentials. You can use Test Credentials in the of testing your application and Live Credentials should be used in Production.
2.2 Buying Phone Number from Twilio:
Now you have API Credential, but to make a call to another phone you will need a Phone number. You can buy phone numbers from Twilio by Visiting this link , now select the country and click the search button as shown in below image. After clicking search button you will list of numbers you can select whatever number you like.
Buying Phone Number from Twilio
2.3 Adding Verified Caller IDs:
Twilio can make calls on only those numbers which are added to verified caller ID list. So first you need to add numbers before making a call on those phone numbers.
To add numbers to your verified caller ID list click this link you will see below image. In the below image I have only one number, You can use this link to add a number or you can use REST API provided by Twilio.
2.4 Making call Request:
Okay, Till now we have all the data required to make a call. You can Test your Phone numbers by visiting this link and you should see the below screen. Now fill the required field and click on the Make Request button.
Making Call Request
Note: This request costs money.
After clicking Make Request button, If you receive a phone call from Twilio Number (Number that you just brought from Twilio).
Then you can go ahead and create your Project with Twilio Till they (Twilio) upgrade your account. Yes, you will have to upgrade your account to use Twilio’s services.
3. Creating Nodejs phone call verification application:
3.1 Getting started with mongoDB.
We can use any Database of your choice but for now, I am using MongoDB and I assume the reader is pro-efficient in mongoDB.
I have created a collection named as callVerifywhich stores user’s information. We will fetch user’s mobile from the stored details and call them accordingly.
Below is the example of JSON we will store in our collection.
{ "_id" : ObjectId("574789f681d3144c15c57629"), "username" : "Shashank", "email" : "shashak@codershood.info", "password" : "rock", "mobile" : "**********", "isVerified" : false }
3.2 Creating New Nodejs Project
1. In this article, we will create a basic application where we will authenticate the user by making phone call user’s mobile number by using Twilio’s rest API. The working of this will be almost same as ourNodeJs Sms verification. Below is folder structure of this application.
+--- utils | +-- config.js | +-- db.js | +-- helper.js | +-- TWILIO.js | +---routes.js | +--- node_modules | \ body-parser | \ ejs | \ express | \ express-session | \ twilio | +--- views |\ css || |+-- bootstrap.min.css |+-- style.css |\ js || |+-- angular.min.js |+-- angular-route.js |+-- script.js (Script for Login & Registration Page) |\ pages || |+-- login.html |+-- home.html | |+-- index.html | +---server.js +---package.json
2. Now let’s start by running npm init
command in CMD to generate a package.json file. Below is our package.json file.
package.json:
{ "name": "nodeJs-phone-call-verification-system", "version": "0.0.1", "description": " NodeJs phone call Verification system - Codershood", "author": "Shashank Tiwari", "license": "MIT", "dependencies": { "body-parser": "^1.15.0", "ejs": "^2.4.1", "express": "^4.13.4", "express-session": "^1.13.0", "mongodb": "^2.1.19", "twilio": "2.9.1", "socket.io": "^1.4.5" } }
3.2 Creating New Nodejs Files
1.Create a folder named as utils
inside root directory this folder contains files which take care our routes, configuration, and Database connection.
2.Create a file named as config.js
inside utils
folder. This file will take of express related configuration.
config.js:
var express = require("express"); var path= require('path'); var method=config.prototype; function config(app){ // Setting .html as the default template extension app.set('view engine', 'html'); // Initializing the ejs template engine app.engine('html', require('ejs').renderFile); // Telling express where it can find the templates app.set('views', (__dirname + '/../views')); //Files app.use(express.static(path.join('public_data'))); } method.get_config=function(){ return this; } module.exports = config;
3.Create a file db.js
inside utils
folder. This file takes care of database connection.
db.js:
"use strict"; /*requiring mongodb node modules */const mongodb=require('mongodb'); const MongoClient = mongodb.MongoClient; const ObjectID = mongodb.ObjectID; const assert = require('assert'); const MongoUrl='mongodb://localhost:27017/nodePhoneCall'; module.exports.onConnect = function(callback){ MongoClient.connect(MongoUrl, function(err, db) { assert.equal(null, err); callback(db,ObjectID); }); }
4. Now create a file called server.js.
server.js:
'use strict'; /*requiring node modules starts */const app = require("express")(); const http = require('http').Server(app); /*requiring node modules ends */ /* requiring config file starts*/const config =require('./utils/config.js')(app); /* requiring config file ends*/ /* requiring config db.js file starts*/const db = require("./utils/db.js"); require('./utils/routes.js')(app); http.listen(81,function(){ console.log("Listening on http://127.0.0.1:81"); });
3.3 Writing code Logic for Nodejs phone call verification
1.Now create a file named as helper.js
inside utils
folder. This file contains Core Logic and all the major functions to perform the read, write operations onto the database.
helper.js:
'use strict'; const Mongodb = require("./db"); const TWILIO = require("./TWILIO"); const self={ createUser:function(data,callback){ var response={}; const userInfo={ "email" : data.email }; Mongodb.onConnect(function(db,ObjectID){ db.collection('smsVerify').findOne(data,function(err, result){ if(err){ response.process = false; response.isUserExists = false; response.message = "Something went Wrong,try after sometime."; }else{ if(result != null ){ response.process = true; response.isUserExists = true; response.message = "User already exists."; callback(response); }else{ db.collection('smsVerify').insertOne(data,function(err, result) { if(err){ response.process = false; response.isUserExists = false; response.isUserAdded = false; response.message = "Something went Wrong,try after sometime."; }else{ response.process = true; response.isUserExists = false; response.isUserAdded = true; response.id=result.ops[0]._id; response.message = "User added."; } callback(response); }); } } }); }); }, isUserExists:function(data,callback){ var response={}; Mongodb.onConnect(function(db,ObjectID){ db.collection('smsVerify').findOne(data,function(err, result){ if(result != null ){ response.process = "success"; response.isUserExists = true; response.id = result._id; }else{ response.process = "failed"; response.isUserExists = false; } callback(response); }); }); }, getUserInfo:function(data,callback){ var response={}; Mongodb.onConnect(function(db,ObjectID){ data._id = new ObjectID(data._id); db.collection('smsVerify').findOne(data,function(err, result){ if(result != null ){ response.process = "success"; result.mobile = (result.mobile).substr((result.mobile).length-4); response.data = { username : result.username, isVerified : result.isVerified, mobile : result.mobile, email : result.email }; }else{ response.process = "failed"; response.isUserExists = false; } callback(response); }); }); }, sendOtp:function(data,callback){ var OTP = self.generateOtp(); var response={}; Mongodb.onConnect(function(db,ObjectID){ data._id = new ObjectID(data._id); db.collection('smsVerify').findOne(data,function(err, result){ console.log(result.mobile); if(result != null ){ if(result.mobile == "" || result.mobile == null){ response.process = false; response.message = "Invalid Number"; callback(response); }else{ TWILIO.callUser(OTP,result.mobile,function(result){ result.otp=OTP; console.log(result); callback(result); }); } }else{ response.process = false; response.message = "Invalid Number"; callback(response); } }); }); }, verifyOtp:function(data,otpData,callback){ const sessionOtp = parseInt(otpData.otp); const sessionUserID = otpData.id; const UserOtp = parseInt(data.otp); const userID = data.id; console.log(UserOtp); console.log(sessionOtp); var response={}; if(UserOtp == "" || typeof UserOtp == "" || UserOtp == null || UserOtp == "NaN"){ response.isVerified=false; response.message="OTP is destroyed,Please resend OTP."; callback(response); }else{ if(UserOtp === sessionOtp){ self.activateVerification(userID,function(result){ if(result.isError){ response.isVerified=true; response.message="Something went Wrong at our end."; }else{ response.isVerified=true; response.message="Your Number is Verified"; } callback(response); }); }else{ response.isVerified=false; response.message="OTP does not match."; callback(response); } } }, activateVerification:function(userID,callback){ var response={}; Mongodb.onConnect(function(db,ObjectID){ db.collection('smsVerify').updateOne( { _id: new ObjectID(userID) }, { $set: {isVerified: true} },function(err, results) { if(err){ console.log(err); response.isError=true; response.isVerified=false; callback(response); }else{ console.log(results.result.nModified); if(results.result.nModified > 0){ response.isError=false; response.isVerified=true; }else{ response.isError=true; response.isVerified=false; } callback(response); } } ); }); }, generateOtp:function(){ return Math.floor(Math.random() * 10000) + 9999; } } module.exports = self;
2. Create one more file named as TWILIO.js which contains a function to make calls on different numbers.
TWILIO.js:
const ACCOUNT_SID = "ACCOUNT_SID";; const AUTH_TOKEN = "AUTH_TOKEN"; const ERROR_CODES = [21217,21214,21215,21216,21210,21212]; const FROM_NUMBER = "From_Number"; var client = require('twilio')(ACCOUNT_SID,AUTH_TOKEN); const self={ callUser : function(OTP,mobileNumber,callback){ var response = {}; var URL = "http://twimlets.com/echo?Twiml=%3CResponse%3E%3CSay%3EYour+OTP+is+"+OTP+".%3C%2FSay%3E%3C%2FResponse%3E"; client.calls.create({ url: URL, to: mobileNumber, from: FROM_NUMBER }, function(err, responseData) { if(err){ if(ERROR_CODES.indexOf(err.code) > -1){ response.message = (err.message).replace(/\\/g, ''); }else{ response.message = "Try after some time."; } response.process = false; }else{ response.message = "The call is being forwarded"; response.process = true; } callback(response); }); } } module.exports = self;
7.Till now we have our core logic ready. Now we have to create front end files and we have to manage our routes. Now let’s create a routes.js file inside the utils folder.
List of routes:
1. /login
: As the name justifies itself.
2. /register
: We use this routes for user Registration.
3. /getUserInfo
: To fetch the information based on mongoDB document ID.
4. /sendOtp
: Here we will initialize TWILIO to make a call on mobile number.
5. /verifyOtp
: Here we will verify the OTP by comparing user’s OTP with OTP stored in the session.
6. /logout
: this method will logs out users from our application.
routes.js:
const bodyParser = require('body-parser'); var Session = require('express-session'); var Session= Session({ secret:'secrettokenhere', saveUninitialized: true, resave: true }); // requiring Helper file to run helper functions const helper = require('./helper'); var method=routes.prototype; function routes(app,io){ app.use(bodyParser.json()); app.use(Session); var sessionInfo; app.get('/', function(req, res){ res.render('index'); }); app.post('/login', function(req, res){ sessionInfo = req.session; const data={ "email" : req.body.email, "password" : req.body.password } helper.isUserExists(data,function(result){ if(result.isUserExists === true){ sessionInfo.sessionData = { userID:result.id }; } res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify(result)); }); }); app.post('/register', function(req, res){ sessionInfo = req.session; const data={ username : req.body.name, email : req.body.email, password : req.body.password, mobile : req.body.mobile, isVerified :false } helper.createUser(data,function(result){ if(typeof result.isUserAdded != "undefined" && result.isUserAdded == true){ sessionInfo.sessionData = { userID:result.id }; } res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify(result)); }); }); app.post('/getUserInfo', function(req, res){ sessionInfo = req.session; if(typeof sessionInfo.sessionData == "undefined" || sessionInfo.sessionData == null){ res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify({process:"failed"})); }else{ if(sessionInfo.sessionData.userID == "" || sessionInfo.sessionData.userID != req.body.id){ res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify({process:"failed"})); }else{ const data={ _id : req.body.id }; helper.getUserInfo(data,function(result){ res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify(result)); }); } } }); app.post('/sendOtp', function(req, res){ sessionInfo = req.session; const userID=req.body.id; const data={ _id : userID } helper.sendOtp(data,function(result){ var response={}; if(result.process){ sessionInfo.otpData={ id : userID, otp : result.otp }; response.otpCreated = true; }else{ response.otpCreated = false; } response.message = result.message; res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify(response)); }); }); app.post('/verifyOtp', function(req, res){ sessionInfo = req.session; const data={ otp : req.body.otp, id : req.body.id } const otpData = sessionInfo.otpData; helper.verifyOtp(data,otpData,function(result){ res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(JSON.stringify(result)); }); }); app.post('/logout', function(req, res){ sessionInfo = req.session; sessionInfo.sessionData = null; res.end(); }); } method.getroutes=function(){ return this; } module.exports = routes;
What is happening in above code:
1.When the user enters OTP and clicks the verify button the OTP will be received in /verifyOtp
as aPOST param.
2.Then user’s OTP will be compared against OTP stored in the session.
3.If OTP matches we will update the status of the isVerified field by updating the MongoDB Document.
3.4 Creating Front End
Now our NodeJs server is ready, Now we will create Front End Files and Here we will use angular.js.
Our application will be single page application so for that, I have created a new folder named aspages inside views folder. We have two fileslogin.html and home.html inside pages folder.
But before that create an index.html outside of pages folder i.e. views folder.
index.html:
<html ng-app="home"> <head> <title>Nodejs phone call verification : Codershood</title> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/font-awesome.min.css"> <link rel="stylesheet" href="css/style.css"> </head> <body ng-controller="home"> <div class="container"> <div ng-view></div> </div> <script type="text/javascript" src = "js/angular.min.js"></script> <script type="text/javascript" src = "js/angular-route.js"></script> <script type="text/javascript" src ="js/script.js"></script> </body> </html>
Now to perform Login and Registration we have created login.html. Below is code for login.html.
login.html:
<div class="row"> <div class="col-md-4 col-md-offset-1"> <h2>Login</h2> <div class="loginBox"> <label>Email : </label> <input type="text" class="form-control email-login" id="emailLogin" placeholder="Email" ng-model="emailLogin"> <br/> <label>Password : </label> <input type="text" class="form-control password-login" id="passwordLogin" placeholder="Password" ng-model="passwordLogin"> <br/> <button class="btn btn-primary" id="login" ng-click="login()">Login</button> </div> </div> <div class="col-md-5"> <h2>Register</h2> <div class="registerBox"> <label>Name : </label> <input type="text" class="form-control name-register" id="nameRegister" placeholder="Email" ng-model="nameRegister"> <br/> <label>Email : </label> <input type="text" class="form-control email-register" id="emailRegister" placeholder="Email" ng-model="emailRegister"> <br/> <label>Moble number : </label> <input type="text" class="form-control mobile-register" id="mobileRegister" placeholder="Mobile" ng-model="mobileRegister"> <br/> <label>Password : </label> <input type="text" class="form-control password-register" id="passwordRegister" placeholder="Password" ng-model="passwordRegister"> <br/> <button class="btn btn-primary" id="register" ng-click="register()">Register</button> </div> </div> </div>
Code for home.html where the user can perform the verification of the mobile number.
home.html:
<div class="container"> <header class=""> <h2>Hello {{ username }}</h2> <a ng-click="logout()">logout</a> </header> <div class="information"> <h3>Your mobile number ******{{ mobile }} is {{ isVerified === true ? "Verified" : "not Verified"}}</h3> </div> <div id="otpSection" class="otpSection"> <div id="sendOtpSection" class="sendOtpSection"> <button class="btn btn-primary sendOtp" id="sendOtp" ng-click="sendOTP()">Send OTP</button> </div> <div id="verifyOtpSection" class="verifyOtpSection"> <input type="number" class="form-control" id="otp" placeholder="Enter OTP" ng-model="enteredOtp"> <br/> <button class="btn btn-primary verifyOtp" id="verifyOtp" ng-click="verifyOTP()">Verify OTP</button> </div> </div> </div> <div id="overlayMessage" class="overlayMessage" ng-show="overlay"> Loading </div>
As of now, we only have HTML file we have to write some AngularJs to make it useful. In below script, we have two controllers one is for login page and other is for the home page. Below is the code for script.js and style.css.
script.js:
"use strict"; var app = angular.module('home',['ngRoute']); app.config(function($routeProvider) { $routeProvider // route for the home page .when('/', { templateUrl : 'pages/login.html', controller : 'index' }) // route for the about page .when('/home', { templateUrl : 'pages/home.html', controller : 'home' }) }); /*--------------------------------------------------------------------------------- Making service to run ajax ---------------------------------------------------------------------------------*/app.service('runajax', ['$http', function ($http) { this.runajax_function = function(request,callback){ var url=request.url; var data=request.data; $http.post(url,data).success(function(data, status, headers, config) { callback(data); }) .error(function(err){ callback(err); }); } }]); app.controller('index', function ($scope,runajax,$window) { $scope.players=[]; /*--------------------------------------------------------------------------------- Call to add Player ---------------------------------------------------------------------------------*/ $scope.login = function() { if (typeof $scope.emailLogin == "undefined" || $scope.emailLogin == "" ) { alert(`Enter Login Email`); }else if(typeof $scope.passwordLogin == "undefined" || $scope.passwordLogin == "" ){ alert(`Enter Login Password`); }else{ var urlData={ url:'/login', data:{ email:$scope.emailLogin, password:$scope.passwordLogin } } runajax.runajax_function(urlData,function(userData){ if(userData.isUserExists){ $window.location.href ="/#/home?id="+userData.id; }else{ alert(`Not Done Login Failed`); } }); } }; /*--------------------------------------------------------------------------------- Call to Register ---------------------------------------------------------------------------------*/ $scope.register = function() { if (typeof $scope.nameRegister == "undefined" || $scope.nameRegister == "" ) { alert(`Enter Register Name`); }else if(typeof $scope.emailRegister == "undefined" || $scope.emailRegister == "" ){ alert(`Enter Register Email`); }else if(typeof $scope.passwordRegister == "undefined" || $scope.passwordRegister == "" ){ alert(`Enter Register Password`); }else if(typeof $scope.mobileRegister == "undefined" || $scope.mobileRegister == "" ){ alert(`Enter Register Mobile `); }else{ var urlData={ url:'/register', data:{ name:$scope.nameRegister, email:$scope.emailRegister, password:$scope.passwordRegister, mobile:$scope.mobileRegister } } runajax.runajax_function(urlData,function(userData){ console.log(userData); if(userData.process){ if(userData.isUserExists){ alert(userData.message); }else{ $window.location.href ="/#/home?id="+userData.id; } }else{ alert(userData.message); } }); } }; }); app.controller('home', function ($scope,$window,runajax,$routeParams) { $scope.username=""; $scope.mobile = ""; $scope.isVerified = ""; $scope.overlay = false; const userId= $routeParams.id; if(typeof $routeParams.id != "undefined"){ var urlData={ url:'/getUserInfo', data:{ id : userId } }; runajax.runajax_function(urlData,function(userData){ if(userData.process == "failed"){ $window.location.href ="/#/"; }else{ userData=userData.data; $scope.username = userData.username; $scope.mobile = userData.mobile; $scope.isVerified = userData.isVerified; if( userData.isVerified == false){ document.querySelector( '#sendOtpSection' ).style.display="block"; } } }); } $scope.sendOTP = function() { $scope.overlay = true; var urlData = { url:'/sendOtp', data:{ id : userId } }; runajax.runajax_function(urlData,function(result){ if(result.otpCreated === true){ document.querySelector( '#sendOtpSection' ).style.display="none"; document.querySelector( '#verifyOtpSection' ).style.display="block"; } console.log(result); $scope.overlay = false; alert(result.message); }); }; $scope.verifyOTP = function() { $scope.overlay = true; if(typeof $scope.enteredOtp == "undefined" || $scope.enteredOtp == ""){ alert(`Enter OTP`); }else{ var urlData = { url:'/verifyOtp', data:{ otp : $scope.enteredOtp, id : userId } }; runajax.runajax_function(urlData,function(result){ if(result.isVerified === true){ $scope.isVerified = true; document.querySelector( '#sendOtpSection' ).style.display="none"; document.querySelector( '#verifyOtpSection' ).style.display="none"; }else{ document.querySelector( '#verifyOtpSection' ).style.display="none"; document.querySelector( '#sendOtpSection' ).style.display="block"; } $scope.overlay = false; alert(result.message); }); } }; $scope.logout = function(){ $scope.overlay = true; var urlData = { url:'/logout', data:{ id : userId } }; runajax.runajax_function(urlData,function(result){ $scope.overlay = false; $window.location.href ="/#/"; }); } });
Well, this brings us to end of this Nodejs phone call verification article, apart from Twilio there can be other Voice gateways which can help us to built such kind of applications.
Let me know your thoughts by commenting in below comment box, I would love to hear you. And if you like this article share with other people out there.