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?
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
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)
npm install
Step 3 - Configure your CipherTrust credentials in the .env file
cp .env.example .env
Now update the .env
file with your CipherTrust credentials.
Step 3 - Run dev server
Spin up the demo
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.
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;
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.
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.