Learn how to use RingCentral SMS API to implement Two-Factor Authentication for enhancing security. Click the "Start Tutorial" button below to start.
Start TutorialThis tutorial shows how to use RingCentral SMS API to send a verification code for 2-factor authentication to help prevent brute force attacks and protect user's accounts.
Use case 1: Protect a user account from brute force attacks.
After 3 unsuccessful attempts were made to log into a user's account, the account will be temporarily locked. The service will generate a 6-digit verification code and send the code to the user's mobile phone number.
The user has to use the verification code to unlock his/her account. The verification code can be used only one time and it also expires after 1 hour.
If the verification code is incorrect, or if the verification code expired, the user has to request for a new verification code.
Use case 2: Prevent taking over login control.
When a user wants to reset the login password. The service will generate a 6-digit verification code and send the code to the user's mobile phone number.
The user has to use the verification code to set a new password for his/her account. The verification code can be used only one time and it also expires after 1 hour.
If the verification code is incorrect, or if the verification code expired, the user has to request for a new verification code.
You must have a RingCentral developer account. If you don't have one, click here to create a free developer account.
You also have to create a RingCentral app with the "SMS" permissions. And retrieve the AppKey and AppSecret for using with this tutorial.
Clone the project from GitHub and install RingCentral SDK and dependencies.
$ cp dotenv .env
Add your app client id and client secret as well as account login credentials to the .env file.
$ node index.js
Open your Web browser and enter localhost:5000
To see how it works, signup to create a new user account. Then login with a wrong password for 3 times. Or click the Reset password link.
If you login successfully, it will just launch the about page.
Let's start to setup our Web server.
We import the express framework and create the app object from the express package.
We set the views folder, where we place all the web pages .ejs files.
Finally, we create an HTTP server and listen to the specified port for incoming HTTP requests.
We capture incoming HTTP GET and POST requests from the client side then forward the request to an appropriated function in the engine module to process the request.
We will discuss about these functions later when we make a call from the client side.
The Web page files are located under the views folder.
In this tutorial, we'll implement 3 pages:
signup.ejs: this page contains a form for users to sign up.
index.ejs: this page contains forms for users to login and reset password.
about.ejs: this page contains information about this tutorial.
Client-side JavaScript files are located under the public/js folder.
We'll implement all functions in the main.js file.
We need a signup page for users to create a user account.
In this page, we implement a form where a user provides email address, password, mobile phone number, first name and last name to create a new user account.
When the Submit button in the form is clicked, we will use JQuery post function to send a request to the server instead of using the default submit function.
We also need a login page for users to login.
We create 3 UI blocks to show the login form, reset password form and code verification form in the same page. Then we will use JQuery code to display the forms based on the stage of the login progress.
When a button in a form is clicked, we will use JQuery post function to send a request to the server instead of using the default submit function.
Now, let's create the main.js file and save it to the public/js/ folder.
In this file, we will implement all client-side functions to send HTTP requests to our server.
We implement the signup() function which will be called when a user clicks the Submit button on the signup page.
Inside the client-side signup function, we will check the mandatory user's inputs and encrypt the password before posting the signup request to the our server.
If the server response with no error, we then launch the main page, which will show the login form. If it is an error, we will pop up an alert dialog with the error message.
In the engine.js file, we implement the signup function which will be called when a signup request is made from the client-side.
Inside the server-side signup function, we will create a new user and insert the user information to the users.db database.
For each user account, we create an entry in the database with the following user data:
First, we implement the getSeed function which will be called when a user clicks the Submit button on the login form.
Inside the client-side getSeed function, we will send a getseed request to our server to get a seed value, which will be used to encrypt the user's password.
Then we implement the login function which will be called programmatically when we get the seed from our server.
Inside the client-side login function, we will post a login request with the user's email address, the encrypted password and the id of the seed to the server.
In the engine.js file, we implement the getSeed function which will be called when a getseed request is made from the client-side.
Inside the server-side getSeed function, we will generate a seed value and add it to the seeds table in our users database. We then send a response back to the client with the seed id and seed value.
Then we implement the server-side login function which will be called when a login request is made from the client-side.
Inside the server-side login function, we will read the seed value from the seeds table identified by the seed id we just got back from the client.
Then we read the user's data (phoneno, pwd, failure, locked, code) from the users database identified by the user's email address.
If the user's data is read successfully, we will check if the account is locked or not.
If the account is locked, we will generate a verification code and send it to the user's mobile phone number. We will look into the sendSMSMessage function later. To response to the user's request in this case, we create a message to notify that the account is temporarily locked and a verification code is sent to the user's mobile phone number. However, we should reveal just the last 4-digit phone number e.g. X-XXX-XXX-1234
If the account is not locked, we will encrypt the account password with the seed value then compare it with the user's login password taken from the post request inPassword.
Now, if the passwords match, we will proceed the login with an "ok" response.
If the passwords do not match, we will increase the failure count and check if the failure count reaches the MAX_FAILURE (in this demo, this max is set to 3 times) before updating the user database with the new values. Then we send the failure response to the client.
When we call the sendSMSMessage function, we pass the following parameters to the function:
First, we create the SDK instance rcsdk
and initialize it with the Client id and secret.
Then we call the rcsdk.platform()
function to get the instance of the platform component. The platform
instance will be used later to login and call RingCentral APIs.
To login RingCentral account, we call the platform.login()
function with the account's username and password predefined in the .env file.
After we logged in RingCentral account successfully, we use the randomatic library to generate a verification code.
function generateRandomCode(digits) {
const randomize = require('randomatic');
var code = randomize('0', digits);
return code`
}
We use the platform.post()
function to call the SMS API with the specified parameters:
The from
number is our RingCentral phone number specified in the environment file.
The to
number is the user's mobile phone number we read from the user's database.
The text
contains the verification code we generated above.
After we sent the verification code successfully, we generate a timestamp and save the verification code and the timestamp to the user database.
Finally, we send a response back to the client to notify the user that the user account is locked and a verification code was sent to the user's mobile phone number.
When a user's account is locked, we ask a user to provide a verification code to unlock the account before the user can retry to login.
We implement the verifyPasscode function which will be called when a user clicks the Submit button from the verification form.
Inside the client-side verifyPasscode function, we will post a verifypasscode request with the user's email address and the verification code to the server.
If we receive an "OK" response from the server, which means the verification code matched with the code we saved in the user database, then we will allow the user to try login again.
In the engine.js file, we implement the verifyPasscode function which will be called when a verifypasscode request is posted from the client side.
Inside the server-side verifyPasscode function, we will read the user's data (locked, code, codeexpiry) identified by the user's email address.
If the user's data is read successfully, we will check if the account is locked or not.
If the account is not locked, we just send a response asking the user to login normally.
If the account is locked, we will generate a new timestamp and compare it with the codeexpiry to check if the verification is still valid.
If the verification code is still valid, we validate the user's provided code and compare it with the one we read from the database.
If the codes match, we unlock the user's account by updating the user's data (failure=0, locked=0, code=0) and send a response to allow the user to retry login.
If the codes do not match, we reset the verification code (code=0, codeexpiry=0) and send a response to the user indicating the verification code is invalid. In this demo, the user has to send a new request for a new verification code manually.
Requiring a verification code when a user attempts to change the login password would prevent the account from being taken over by hackers if the account login credentials were compromised.
We implement the resetPwd function which will be called when a user clicks the Submit button from the Reset password form.
Inside the client resetPwd function, we will post a resetpwd request with the user's email address to the server.
We provide two steps to reset a password. The first step is to ask for the user's email address and send it to the server to get a verification code. The second step is when we receive the "OK" response from the server, we will enable the entire form with the new password field and the verification code field.
In the engine.js file, we implement the resetPwd function which will be called when a resetpwd request is posted from the client side.
Inside the server resetPwd function, we will read the user's data (phoneno, code, codeexpiry) identified by the user's email address.
If the user's data is read successfully, we will check if the request is to get a verification code by checking if the code and pwd exist in the request.
If the code and pwd do not exist, it is a request for a verification code. Then we call the SendSMSMessage function to generate a verification code, send it to the user's mobile phone number and send a response to the client.
If the code and the pwd exist, it is a request to set a new password. Then we we will generate a new timestamp and compare it with the codeexpiry to check if the verification is still valid.
If the verification code is still valid, we validate the user's provided code and compare it with the one we read from the database.
If the codes match, we update the user's data with the new password, reset the code and the expiry time. And send a response to allow the user to login with a new password.
If the codes do not match, we reset the verification code (code=0, codeexpiry=0) and send a response to the user to indicate that the verification code is invalid. In this demo, the user has to send a new request for a new verification code manually.
Every time a user requests for a verification code, we read the user's mobile phone number from the user database then call the sendSMSMessage function to generate a new verification code, send it to the user and update the user database with the code and code expiration time.