Tutorials>How to implement Two-Factor Authentication

Learn how to use RingCentral SMS API to implement Two-Factor Authentication for enhancing security. Click the "Start Tutorial" button below to start.

Start Tutorial

Two-Factor Authentication

This 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.

Requirements

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

Clone the project from GitHub and install RingCentral SDK and dependencies.

Set environment parameters and run the demo

$ cp dotenv .env

Add your app client id and client secret as well as account login credentials to the .env file.

  • Note: The username is the account number and it is also the phone number we will use to send SMS from. If you want to use another number under this account, you can create a new variable and specify the phone number and use that number for sending SMS instead.
$ php -S localhost:5000

Open your Web browser and enter localhost:5000

Web pages

In this tutorial, we'll implement 3 pages:

signup.html: this page contains a form for users to sign up.

index.html: this page contains forms for users to login and reset password.

about.html: this page contains information about this tutorial.

Client-side JavaScript files

Client-side JavaScript files are located under the public/js folder.

We'll implement all functions in the main.js file.

Add a signup page

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.

Add a login page

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.

Implement JavaScript client-side

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.

Implement signup client-side

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.

Implement signup server-side

In the engine.php 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:

  • id: a unique user ID
  • phoneno: user's mobile phone number
  • email: user's email address
  • pwd: encrypted password
  • fname: user's first name
  • lname: user's last name
  • failure: number of failed login attempts
  • locked: account locked/unlocked status
  • code: latest sent verification code
  • codeexpiry: verification code expiry time

Implement login client-side

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.

Implement login server-side - step 1

In the engine.php 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.

Implement login server-side - step 2

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.

Implement login server-side - step 3

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.

Implement sendSMSMessage function

When we call the sendSMSMessage function, we pass the following parameters to the function:

    • $db: the SQLite database handle,
    • $phoneno: the user's mobile phone number, to which we can send a text message,
    • $email: the user's email address which we can use to update the user's data in the database,
    • $message: the message we want to respond to the user who tries to login or to reset password.

Instantiate the RingCentral PHP SDK

First, we create the SDK instance $rcsdk and initialize it with the AppKey and AppSecret.

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.

Login to RingCentral account

To login RingCentral account, we call the $platform->login() function with the account's username and password predefined in the __credentials.php_ file.

Generate a verification code

After we logged in RingCentral account successfully, we generate a random 6-digit verification code.

function generateRandomCode($length) {
  $min = 10;
  for ($i=1; $i<$length-1; $i++) {
      $min *= 10;
  }
  $max = ($min * 10) - 1;
  return mt_rand($min, $max);
}

Send a SMS message

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.

Save the verification code

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.

Unlock a user account - client-side

When a user's account is locked, we ask the 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.

Unlock a user account - server-side

In the engine.php 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.

Validate the verification code

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.

Reset password - client-side

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.

Reset password - server-side

In the engine.php 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.

Send a verification code

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.

Implement reset password

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.

Resend a verification code

Every time a user requests for a new 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.