Skip to main content

Build a TOTP service

Use the top-notch security that CipherTrust Manager provides to build a time-based OTP service for your users' multi-factor authentication.

Time-based OTPs are a popular version of multi-factor authentication (MFA) used in many services today. The concept of generating them is simple, yet a single blunder could risk all your user's accounts.

How do Time-based OTPs work?

Source Twilio.com Image Source: twilio.com

Time-based OTP's work on a simple concept. When a users sets it up, the device (such as Google Authenticator, Authy, etc) and the server share a secret key. The key is then hashed with the time and the same code can be generated on the server and the user's client device.

Single Point of Failiure

If a bad actor gains access to the servers or databases where you store all the secret keys used to generate the OTPs, now all your users accounts could get compromised!

How do we fix this 👉 Use a KEY MANAGER

Key Managers like the CipherTrust Platform are designed to store cryptographic keys very securely. This demo is an example where I use the CipherTrust platform to store keys and generate time based OTP codes.

Quick Start

This tutorial assumes that you've already setup the CipherTrust Platform. If not, check out our other step-by-step tutorials that show you how to deploy CipherTrust.

Step 1 - Clone the demo

Clone the demo project from the learn-ciphertrust repository

Terminal
git clone https://github.com/ThalesGroup/learn-ciphertrust.git && cd learn-ciphertrust/learn/key-management/totp-demo/

Step 2 - Install the required packages

The important packages that are necessary for this demo:

  • jsonwebtoken - helps authenticate with the CipherTrust Platform's APIs.
  • otplib - JavaScript implementation of the cryptographic timebased-otp standard (AKA - the library that calculates the one time code for the next 30 secs)
Terminal
npm install

Step 3 - Configure your CipherTrust credentials in the .env file

Terminal
cp .env.example .env

Now update the .env file with your CipherTrust credentials.

Step 3 - Run dev server

Spin up the demo

Terminal
npm run dev

Breaking down the demo

Now that you've got the demo setup and played around with it, let's understand how it works. The demo application involves 3 important code-blocks.

Autheticating with CipherTrust Platform's APIs

Create a JWT (JSON Web Token) to be able to authenticate and communicate with the CipherTrust Platform's APIs.

./src/utils/create-jwt.js
import axios from "axios";

// `createJWT` is a helper function that creates a JWT.
// This function must only called on the server.
// If you call it on the client-side you will expose your username and password in every request. DO NOT DO THIS. Just use it in a server-side API call.
async function createJWT() {
const response = await axios.post(
`${process.env.CTM_URL}/api/v1/auth/tokens`,
{
username: process.env.CTM_USERNAME,
password: process.env.CTM_PASSWORD,
}
).catch(err => {
console.error(err);
res.status(502).send(err.toString());
});

const token = response.data.jwt;

return token
}

export default createJWT;
info

Remember to replace process.env.CTM_USERNAME and process.env.CTM_PASSWORD with your CipherTrust Manager username and password or update it in your the .env file

Creating and Exporting Cryptographic keys

To initiate the time-based OTP setup, a symmetric key (such as AES256) would be needed, so over here, we create and export a new key on the CipherTrust Platform using the API.

./src/utils/create-jwt.js
import axios from 'axios';

async function createKey(keyname, jwt) {
// /api/create-key-proxy rewrites to https://<your_ciphertrust_url>/api/v1/vault/keys2
// This is done to avoid CORS errors thrown by the browser in the client side.
const response = await axios.post(
`/api/create-key-proxy`,
{
"name": keyname,
"algorithm": "aes",
},
{
headers: {
"Authorization": `Bearer ${jwt}`
}
}
).catch(err => {
console.error("Error creating key: ", err);
return false
})

// Export key from CipherTrust Manager
// /api/export-key-proxy/<keyId> rewrites to https://<your_ciphertrust_url>/api/v1/vault/keys2/<keyId>/export
// This is done to avoid CORS errors thrown by the browser in the client side.
const exportResponse = await axios.post(
`/api/export-key-proxy/${keyname}`,
{},
{
headers: {
"Authorization": `Bearer ${jwt}`
}
}
).catch(err => {
console.error("Error exporting key: ", err);
return false;
})

return exportResponse.data.material;


}

export { createKey };

Then we load that key into a QR code using the otplib library. Now we have finished setting up a TOTP service using the secure CipherTrust platform as our key manager.