Tutorial: Two Factor Authentication with Node
Articles - 3 min read

Tutorial: Two Factor Authentication with Node

Two Factor Authentication Tutorial with Node
Data privacy and security are a major concern in every industry today. With the average cost of a data breach reaching 8 million dollars, companies are implementing measures to avoid these instances and protect their consumers. One popular method for increasing security is two factor authentication (2FA), which requires users to verify their identity by providing two things. The first is some information that you know -- usually a username/password combination -- and the other is something you have -- usually a cellular device or some other physical token.
The latter is where SMS becomes relevant; enabling SMS-based 2FA will send a one-time code to the user’s mobile phone number so that they can verify their identity by confirming their device, and securely log into their account.
With Telnyx’s developer docs, you can enable 2FA on your application using Node, in about 20 minutes.

Getting Started

To get started, this is a basic overview of the steps we’ll cover:
  1. Configuration
  2. Token Storage
  3. Initialize Server
  4. Collect User Input
  5. Generate OTP token
  6. Send OTP to Device
  7. Verify Token

Configuration

Before you start building your 2FA service, you’ll need to provision an SMS number and messaging profile on the Mission Control Portal. A guide on how to set up your portal can be found here. Once your messaging profile is complete, you will add see the account’s API key in your portal. Next, you’ll create a config.json file in your project directory, and add the API key to the config file:
1
2
3
4
{
    "API_KEY": "YOUR_API_KEY",
    "FROM_NUMBER": "YOUR_TELNYX_NUMBER"
}
Place Node in debug mode, and specify the number of characters you’d like your OTP token to be. The country code will be +1 if all numbers are in the U.S.
1
2
3
4
5
{
    "NODE_ENV": "development",
    "COUNTRY_CODE": "+1",
    "TOKEN_LENGTH": 4 
}

Token Storage

In a production environment, a traditional database would be appropriate, but for the purpose of this example, we’ll be storing the tokens in a class. This class will store uppercase tokens as keys with details about those tokens as values.
Create a class, name it TokenStorage , and specify three static methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class TokenStorage{

  static add_token(token, phone_number){
    this.tokens[token] = {
      'phone_number': phone_number,
      'last_updated': Date.now(),
      'token': token.toUpperCase()
      }
  }

  static token_is_valid(token){
    return token in TokenStorage.tokens 
  }

  static clear_token(token){
    delete TokenStorage.tokens[token]
  }
}

TokenStorage.tokens = {}

Initialize Server

To initialize the server, we’ll set up a simple Express app that watches the templates directory with Nunjucks , load the config file, and configure the telnyx library.
1
2
3
4
5
6
7
8
9
10
11
const config = require('./config.json';)
const express = require("express")
const app = express()

app.use(express.urlencoded());
app.set('views', `${__dirname}/templates`);

expressNunjucks(app, {
  watch: true,
  noCache: true,
});

Collect User Input

We’ll need to create a simple HTML form, index.html, to collect phone numbers for validation; the full HTML source can be found at our Github repo.
1
2
3
app.get('/', (_req, res) => {
  res.render('index');
});

Generate OTP Token

To generate the OTP tokens, we’ll start with get random token hex, which generates a random string of hex characters.
1
2
3
function get_random_token_hex(num_chars) {
  return crypto.randomBytes(num_chars).toString('hex');
}
The randomBytes method accepts a number of bytes, so we need to divide by two and and round up in order to ensure we get enough characters (two characters per byte),and then trim by the desired length. This allows us to support odd numbered token lengths.
Next, handle the form on the /request route, which will normalize the phone number.
1
2
app.post('/request', (req, res) => {
  let phone_number = req.body.phone.replace('/[\-\.\(\)]/g','')
Then, generate a token and add the token/phone number pair to the data store:
1
2
  let generated_token = get_random_token_h(config.TOKEN_LENGTH)
  TokenStorage.add_token(generated_token, phone_number)

Send Token to Device

Finally, we send an SMS including the token to the user’s device and serve the verification page.
1
2
3
4
5
6
7
8
  telnyx.messages.create({
    "to":   `${phone_number}`,
    "from": `${config.COUNTRY_CODE}${config.FROM_NUMBER}`,
    "text": `Your Token is ${generated_token}`
    })

  res.render('verify.html');
})

Verify Token

The verify.html file includes a form that collects the token and sends it back to the server. If the token is valid, we'll clear it from the datastore and serve the success page.
1
2
3
4
5
6
app.post("/verify", (req, res) => {
  let token = req.body.token
  if (TokenStorage.token_is_valid(token)) {
    TokenStorage.clear_token(token)
    res.render('verify_success.html')
  }
If the token is not valid, an error message will be displayed and the user will be sent back to the verify form.
1
2
3
4
  else {
    res.render('verify.html')
  }
})

Last Steps

At the end of the file, run the server:
1
2
const port = 3000
app.listen(port, () => console.log(`App listening on ${port}!`))
To start the application, run
1
node index.js

Congrats! You’ve just configured 2FA on the Telnyx platform that you can use to scale across your application. Make sure you check out our developer community on Slack to see what others are building with Telnyx.
Share on Social

Worth checking out

By using the site, you agree to our use of cookies. Accept and close Find out more here.