Implementing SMS two-factor authentication (2FA) with MessageBird

⏱ 15 min build time || Download the Code

Why build two-factor authentication?

In this MessageBird Developer Tutorial you’ll learn how to improve your security building an SMS-based two-factor authentication solution with the MessageBird Verify API. The runnable application we’ll build is a prototype in Node.js for our fictitious online banking application, BirdBank.

Enterprises are increasingly challenged to keep sensitive information from falling into the wrong hands. This means that we can no longer trust old online authentication systems that rely solely on usernames and passwords, especially as security breaches grow in frequency, severity and sophistication.

With the MessageBird Verify API, you can implement two-factor authentication (2FA) solutions to provide an additional layer of account security by verifying the user's password with a second authentication token and in turn, secure customer data, block fraudulent accounts and safeguard key transactions in a matter of minutes. The most common use case involves the application of one-time passwords (OTP) generated by hardware tokens, authenticator apps or directly sent to the user's mobile phone via SMS messaging.

We'll walk you through the following steps:

  • Asking for the phone number
  • Sending a verification code
  • Verifying the code

Pro-tip: Follow this tutorial to build the whole application from scratch or, if you want to see it in action right away, you can download, clone or fork the sample application from the MessageBird Developer Tutorials GitHub repository.

Getting started

To build our sample application we'll use Node.js, the Express framework, the Handlebars templating engine as well as the MessageBird SDK.

Before we get started, make sure Node's package manager (npm) is installed. If not, you can easily download it for free.

Project setup

Dependencies

First, let's create a new directory to store the sample application. Within this new directory we'll create a file called package.json with the following content:

{
"name": "node-messagebird-verify-example",
"main": "index.js",
"dependencies": {
"body-parser": "^1.18.3",
"dotenv": "^5.0.1",
"express": "^4.16.3",
"express-handlebars": "^3.0.0",
"messagebird": "^2.1.4"
}
}

This file provides a name for your sample application, declares the main (which we'll create next) and lists the dependencies you'll need along with their versions. Apart from Express, Handlebars and the MessageBird SDK, we're adding a small helper library called body-parser to help simplify your code.

Next, we need to instruct npm to install all the required modules in your project. Let's open a console pointed to the directory that contains the file you just created and type the following command:

npm install

Create your API Key 🔑

To enable the MessageBird SDK, we need to provide an access key for the API. MessageBird provides keys in live and test modes. To get this application running, we’ll need to create and use a live API access key. You can read more about the difference between test and live API keys in our Help Center.

Let's create your live API access key. First, go to the MessageBird Dashboard; if you have already created an API key it will be shown right there. If you don’t see any key on the Dashboard or if you're unsure whether this key is in live mode, go to the Developers section in the MessageBird Dashboard and open the API access (REST) tab. There you can create new API keys and manage your existing ones.

If you’re having any issues creating your API key, please reach out to support@messagebird.com; we’ll make sure to help you out.

Pro-tip: Hardcoding your credentials is a risky practice that should never be used in production applications. A better method, also recommended by the Twelve-Factor App Definition, is to use environment variables.

We've added dotenv to the sample application so that you can supply your API key in a file named .env. You can copy the provided file env.example to .env and add your API key like this:

MESSAGEBIRD_API_KEY=YOUR-API-KEY

Main file

You now have your API key, so let's get started with the main file. First, create an index.js file in the same directory as your package.json. This file starts by including the dependencies:

var express = require('express');
var exphbs = require('express-handlebars');
var bodyParser = require('body-parser');

Then, we initialize dotenv to load the API key from the .env file:

require('dotenv').config();

Next, append the following line to your index.js to include and set up the SDK:

var messagebird = require('messagebird')(process.env.MESSAGEBIRD_API_KEY);

Now, let's initialize and configure the framework and enable Handlebars and the body parser:

var app = express();
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');
app.use(bodyParser.urlencoded({ extended : true }));

Views

We use Handlebars to separate the logic of our code from the HTML pages. To do this, we create a directory named views; inside views, also we create another directory named layouts, and a file called main.handlebars inside with the following content:

<!DOCTYPE html>
<html>
<head>
<title>MessageBird Verify Example</title>
</head>
<body>
<h1>MessageBird Verify Example</h1>
{{{body}}}
</body>
</html>

This is the main layout which acts as a container for all pages of our application. We'll create the views for each page next.

Asking for the phone number

The first step in verifying a user's phone number is asking them to provide their phone number. Let's do exactly this by creating an HTML form and storing it as step1.handlebars inside the views directory:

{{#if error}}
<p>{{error}}</p>
{{/if}}
<p>Please enter your phone number (in international format, starting with +) to receive a verification code:</p>
<form method="post" action="/step2">
<input type="tel" name="number" />
<input type="submit" value="Send code" />
</form>

The form is simple, having just one input field and one submit button. Providing tel as the type attribute of our input field allows some browsers, especially on mobile devices, to optimize for telephone number input, for example, by displaying a number pad-style keyboard. The section starting with {{#if error} is needed to display errors. We'll come back to this in a minute.

Now, it's time to add a route to your index.js to display the page:

app.get('/', function(req, res) {
res.render('step1');
});

Sending a verification code

Once we've collected the number, we can send a verification message to a user's mobile device. The MessageBird Verify API takes care of generating a random token, so you don't have to do this yourself. Codes are numeric and six digits by default. If you want to customize the length of the code or configure other options, you can check out our Verify API documentation.

The form we created in the last step submits the phone number via HTTP POST to /step2, so let's define this route in our index.js:

app.post('/step2', function(req, res) {
var number = req.body.number;
messagebird.verify.create(number, {
originator : 'Code',
template : 'Your verification code is %token.'
}, function (err, response) {
if (err) {
console.log(err);
res.render('step1', {
error : err.errors[0].description
});
} else {
console.log(response);
res.render('step2', {
id : response.id
});
}
})
});

Before we move on, let's quickly dive into what happens here: 🤔

First, we're creating a MessageBird\Objects\Verify object to encapsulate the parameters for our API request. We set the number from the form as the recipient attribute and specify a template for the message. The value for the template attribute contains the placeholder %token, which is replaced with the generated token on MessageBird's end. If we omitted this, the message would contain the token and nothing else.

Also, using originator we specify the sender ID of the text message with the code. The value should either be a valid telephone number international format with country code , or an alphanumeric string with at most 11 characters. If we omitted this, it would default to the string Code. Keep in mind that alphanumeric senders are not supported in every country including the United States, so it’s important to check the country restrictions.

Like most JavaScript functions, the MessageBird API call is asynchronous and transfers control to a callback function once finished. In the SDK's case, this callback function takes two parameters, err and response.

If err is set, it means that an error has occurred. A typical error could be that the user has entered an invalid phone number. For our application, we simply re-render the page from our first step and pass the description of the error into the template. Remember the {{#if error} section from the first step? In production applications, you'd most likely not expose the raw API error; instead, you could consider different possible problems and return an appropriate message in your own words. You might also want to prevent some errors from occurring by doing some input validation on the phone number yourself. We've also added a console.log(err) statement so you can explore the complete error object in your console and learn more about how it works.

In case the request was successful, we'll render a new page. Our API response contains an ID—which we'll need for the next step—so we'll just add it to the form. Since the ID is meaningless without your API access key, there are no security implications of doing so; however, in practice, you'd be more likely to store this ID in a session object on the server. Just as before, we're logging the whole response to the console for debugging purposes. We still need to build the new page, so create a file called step2.handlebars in your views directory:

{{#if error}}
<p>{{error}}</p>
{{/if}}
<p>We have sent you a verification code!</p>
<p>Please enter the code here:</p>
<form method="post" action="/step3">
<input type="hidden" name="id" value="{{id}}" />
<input type="text" name="token" />
<input type="submit" value="Check code" />
</form>

The form is very similar to the first step. Keep in mind that we include a hidden field with our verification ID and, once again, have a conditional error section.

Verifying the code

Once the code is delivered, our user will check their phone, enter their verification code and submit the form. We now need to send the user's input along with the ID of the verification request to the MessageBird API and see whether the verification was successful or not. Let's declare this third step as a new route in our index.js:

app.post('/step3', function(req, res) {
var id = req.body.id;
var token = req.body.token;
messagebird.verify.verify(id, token, function(err, response) {
if (err) {
console.log(err);
res.render('step2', {
error: err.errors[0].description,
id: id,
});
} else {
console.log(response);
res.render('step3');
}
});
});

This code looks very similar to the one in the second step. First, we're reading the input and then make a call to the MessageBird API. This time, it's the messagebird.verify.verify() method, which accepts id and token as its parameters. Inside the callback, error and success cases are handled.

In case of an error, such as an invalid or expired token, we're showing that error on our page from the second step.

In the success case, we simply show a new page. Create this page in your views directory and call it step3.handlebars:

<p>You have successfully verified your phone number.</p>

Testing

Let's write one more line of code in your index.js to run the Express application:

app.listen(8080);

Take a quick look at the directory structure you created. It should look something like this:

node_modules
views
- layouts
- - main.handlebars
- step1.handlebars
- step2.handlebars
- step3.handlebars
index.js
package-lock.json
Package.json

You’re done! Save your index.js and run the application from the command line to test it:

node index.js

Then, point your browser to http://localhost:8080/ and try to verify your own phone number.

Awesome! You can now leverage the flow, code snippets, and UI examples from this tutorial to build your own two factor authentication system. Don't forget to download the code from the MessageBird Developer Tutorials GitHub repository.

Nice work! 🎉

You now have a running integration of MessageBird's Verify API using Node.js!

Start building!

Want to build something similar but not quite sure how to get started? Please feel free to let us know at support@messagebird.com; we'd love to help!

Questions?

We’re always happy to help with code or other doubts you might have! Check out our Quickstarts, API Reference, Tutorials, SDKs, or contact our Support team.

Cookie Settings