Verify API

Tutorial: Two Factor Authentication with Node

Looking to enable two factor authentication for your application? We've got you covered with this quick, 20 minute Node tutorial.

By Risa Takenaka
Two_Factor_Authentication_Tutorial copy.png

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 with 2FA at Telnyx

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:

{
    "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.

{
    "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:

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.

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.

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.

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.

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:

  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.

  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.

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.

  else {
    res.render('verify.html')
  }
})

Last Steps

At the end of the file, run the server:

const port = 3000
app.listen(port, () => console.log(`App listening on ${port}!`))

To start the application, run

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.

FAQ

What is 2FA in Node.js and how does TOTP work? Two-factor authentication adds a second check beyond the password to confirm a user’s identity. Time-based One-Time Passwords use a shared secret and the current time to generate a six-digit code that typically refreshes every 30 seconds.

How do I implement 2FA in Node.js? Generate a per-user secret with a library like otplib or speakeasy, enroll via QR code, encrypt the secret at rest, and verify a TOTP during sign-in. If you deliver OTPs over messaging, follow the provider’s developer documentation to handle retries, delivery status webhooks, and regional rules.

Why do some say 2FA is no longer safe? Weak 2FA flows can be phished, SIM-swapped, or abused through prompt fatigue if protections like rate limits and origin binding are missing. Use authenticator apps or hardware keys for sensitive actions, and add anti-phishing checks such as origin-bound prompts or verified domains.

Should I use SMS or an authenticator app for 2FA? SMS is convenient and reaches nearly everyone, but it is more vulnerable than app-based TOTP or WebAuthn hardware keys. If you pick SMS delivery, understand the differences between SMS and MMS so you design for text-only constraints and carrier policies.

How do I send one-time passcodes via SMS from Node.js? Generate a short numeric code, set a brief expiration, and send it through a messaging API with throttling and replay protections. Choose the correct messaging types, avoid links when possible, and never log the full OTP.

How do I get my 2FA authenticator code? Open your enrolled authenticator app and use the current six-digit code shown for the account. If the code fails, resync your device’s time and confirm you are selecting the correct account entry.

How do I add 2FA to a JWT or session-based auth flow in Node.js? After verifying username and password, mark the session as 2FA pending and block access until the user passes the second factor, then issue the final JWT or elevate the session. Use step-up prompts for high-risk actions and add lockouts, IP checks, and rate limits to curb abuse.

Share on Social

Sign up for emails of our latest articles and news

Related articles

Sign up and start building.