toc

Tutorial

Send an SMS to an encrypted phone number

Evervault is encryption infrastructure for developers. We build simple tools so that your apps only handle and process data that is encrypted at the field-level at all times.

In this tutorial, we’re going to use Evervault to encrypt phone numbers, verify that they’re valid, and to send an SMS with Twilio. This tutorial is designed for beginners, and aims to demonstrate how to effectively integrate Evervault.

We’ll be using Express.js, a library for easily building APIs in Node.js, and the tools we’ll use include:

  • Replit, a collaborative browser-based IDE for writing code and building apps in your browser
  • Evervault Relay, a simple way to automatically encrypt data at the field-level before it touches your app, and decrypt it as it leaves
  • Evervault Cages, isolated serverless functions hosted on Evervault for processing the data encrypted with Relay
  • Twilio SMS API, an API for sending SMS messages

First, we’ll set up our Express API in Replit. This API will include creating a HTML form for collecting a name and a phone number from a user. Next, we’ll create a Relay to automatically encrypt phone numbers before they touch our API. We’ll then create and deploy a Cage to verify that a phone number is valid. Finally, we’ll take the name and encrypted phone number and send the user an SMS using Twilio.

A live version of the application is hosted on https://twilio.evervault.dev

All the code for this application can be found on the Evervault Replit. If you want to follow along from scratch, continue reading.

Setting up our Express API

First, we’re going to set up an Express API in Replit. If you don’t have one already, the first thing you’ll need to do is create a Replit account. Next, you’ll need to create a new Repl.

Creating a Replit

For this tutorial, you’ll need to select Node.js as the language, and can name the Repl anything you want. We chose to name our Repl evervault-twilio.

Create a Replit Account

Creating an Express.js server

Now you’ve created the Repl, you’ll need to import Express, create an application, and start it as a server. You can do this by copying the code below into the index.js file in the Repl:

javascript
// Include Express.js, a library for easily building APIs in Node.js
const express = require('express');
// Create an Express.js application
const app = express();
// Serve static files from the public folder (that we will create later)
app.use(express.static('public'));
// Return middleware that only parses POST JSON requests and only looks at requests where the Content-Type header matches the type option
app.use(express.json());
// Start the Express.js server on port 3030
app.listen(3030, () => {
console.log('Server listening on port 3030');
});

If you click the Run button in Replit, the Console will log the message:

Console
Server listening on port 3030.

Creating a form to collect a name and a phone number

It’s now time to create a public folder and an index.html file within it. In the index.html file, we’re going to create a simple form for collecting a name and a phone number from a user. Here is the HTML code:

html
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' type='text/css' href='https://assets.evervault.com/css/name-phone-form.css'>
</head>
<body>
<h2>Send an SMS</h2>
<!-- This is the form with two fields (name and phone) and a button for submitting the form. -->
<form id="form">
<p><input id="name" type="text" placeholder="Name" /></p>
<p><input id="phone" type="text" placeholder="Phone number" /></p>
<p><button class="submit">Send SMS</button></p>
</form>
<!-- These are the messages which are shown in the UI depending on whether the form was successfully or unsuccessfully sent -->
<div id="pill-container">
<span id="success" class="hidden">SMS successfully sent!</span>
<span id="failure" class="hidden">SMS failed to send.</span>
</div>
</body>
<script>
// When the form is submitted, run this code
// to handle sending the data as JSON to the API
document
.getElementById('form')
.addEventListener('submit', async (event) => {
// Override the default browser settings for handling forms
event.preventDefault();
// Send a POST request to https://{this URL}/submit with the name and phone number encoded in JSON
const res = await fetch('/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: document.getElementById('name').value,
phone: document.getElementById('phone').value,
}),
});
// Take the response from the API and parse it as JSON
const data = await res.json();
// Show feedback message for both successful and failure requests
if (data.success === true) {
document.getElementById('success').classList.remove('hidden');
document.getElementById('failure').classList.add('hidden');
} else {
document.getElementById('success').classList.add('hidden');
document.getElementById('failure').classList.remove('hidden');
}
return false;
});
</script>
</html>

Now if you run the application you should see a simple form inside the preview window.

Phone Form

Processing form requests

The form will not work yet because we still need to give our server the ability to process requests arriving on the /submit route. The code you'll need to add:

javascript
// Define a route and a handler function for that route
app.post('/submit', async (req, res) => {
try {
// Extract name and phone from the JSON the user sends from the form
const { name, phone } = req.body;
console.log('Sending SMS for', { name, phone });
return res.json({ success: true });
} catch (err) {
return res.json({ success: false });
}
});
// Start this app as an Express.js server on port 3030
app.listen(3030, () => {
console.log('Server listening on port 3030');
});

Your index.js file should now look like this:

javascript
// Include Express.js, a library for easily building APIs in Node.js
const express = require('express');
// Create an Express.js application
const app = express();
// Serve static files from the public folder (that we will create later)
app.use(express.static('public'));
// Return middleware that only parses POST JSON requests and only looks at requests where the Content-Type header matches the type option
app.use(express.json());
// Define a route and a handler function for that route
app.post('/submit', async (req, res) => {
try {
// Extract name and phone from the JSON the user sends from the form
const { name, phone } = req.body;
console.log('Sending SMS for', { name, phone });
return res.json({ success: true });
} catch (err) {
return res.json({ success: false });
}
});
// Start this app as an Express.js server on port 3030
app.listen(3030, () => {
console.log('Server listening on port 3030');
});

Now if you run the application and submit data to the form (i.e. submit your name and phone number), you should see the following output in the console:

Console
Sending SMS for { name: 'Claude Shannon', phone: '+1 (415) 091-1945' }

You’ll notice that this log is showing your phone number in plaintext. This means that if your server is compromised, the data breached would be unencrypted and readable data. To avoid this, you can encrypt data before it touches your server — which is where Evervault Relay comes in. Before we integrate Relay, let’s summarize where we are. We have:

  1. Created an Express.js server (index.js),
  2. Created a HTML form for collecting a name and a phone number from a user (index.html), and
  3. Connected your form to your server.

This will be a useful reference for understanding how the architecture of a standard, plaintext app differs from the architecture of an encrypted app built with Evervault.

Your first Relay

It’s now time to integrate Relay into your app so that your server does not handle phone numbers in plaintext — but in a way that you can still use the phone numbers.

Creating a Relay

The first step to integrating Relay is creating an Evervault account and team at app.evervault.com.

Once your team is provisioned, you will be able to create a Relay. The target for your Relay should be the link of your Repl, as shown in the video below.

Create a Relay

Once you've created your new Relay, you will be given its domain. If you copy this domain into your browser, you will see your form there.

Now it’s time to select which fields of your form will be encrypted by Relay — and when those fields will be encrypted. There are two options. First, encrypting a field regardless of the route it is sent to (globally). Second, encrypting a field on a specific route.

In this tutorial, we are going to use route-specific encryption to encrypt the phone field. The method used is POST. The route is /submit.

Specifying fields to encrypt

Specify fields to encrypt

If you submit the form hosted at your Relay domain and check your Replit console, you should see a log that looks similar to:

Console
Sending SMS for { name: 'Claude Shannon', phone: 'ev:AqQ=:Q2xhdS2luZw=:$' }

Great! Now you’re using Relay to encrypt the submitted phone number. This means that your server is no longer handling plaintext, readable data. If your server is compromised, the data breached will be encrypted and unreadable.

Initializing the Evervault SDK

The final step related to Relay is to include and initialize the Evervault SDK so that any outbound requests from your server are decrypted as they pass out of your app. To do this, paste the code below at the top of your index.js file:

javascript
// Including the Evervault SDK
const Evervault = require('@evervault/sdk');
// Initializing the Evervault SDK
const evervault = new Evervault(process.env.EVERVAULT_API_KEY);

You will need to create and save a secret in Replit called EVERVAULT_API_KEY. This will be your Evervault Team’s API key which is available in Settings in the Evervault Dashboard.

Fetch your API key

Let’s summarize where you are. You’ve:

  1. Created an Express.js server (index.js),
  2. Created a HTML form for collecting a name and a phone number from a user (index.html),
  3. Connected your form to your server,
  4. Integrated Evervault Relay so that phone numbers submitted to your form are encrypted before they enter your server, and
  5. Included the Evervault SDK so that outbound requests from your server are automatically decrypted.

Next, you’ll deploy some Node.js code as a Cage to verify that the phone number a user submits is valid.

Your first Cage

The magic of Evervault is that although you never handle sensitive data in plaintext, you can still use and process it — without the risk of it being breached. This is where Evervault Cages come in. Cages are isolated serverless functions hosted on Evervault for processing the data you encrypt with Relay.

Now we’re going to create and deploy a Cage to verify that the phone numbers submitted to your form are valid.

Creating a Cage

First, in the Evervault Dashboard, create a Cage using our GitHub integration. Select Choose template and, then, authenticate with GitHub. For this, you’ll need a GitHub account — so, create a GitHub account if you haven't got one already. Once you’ve authenticated with GitHub, you can select the ‘Verify Phone’ Cage template. This will automatically deploy a Cage on Evervault and create a repository in your GitHub. We recommend naming the repo 'verify-phone-number'.

Create a Cage

Navigate to your new repo in GitHub (the URL is available on your Cage's page in the Evervault Dashboard). You’ll notice that the index.js file contains the following code:

javascript
const { phone: validatePhone } = require('phone');
exports.handler = async ({ phone }) => {
const { isValid, countryIso2: country } = validatePhone(phone);
return {
isValid,
country,
};
};

Verifying phone numbers

This Cage uses the phone library to validate the phone number and returns the result.

Now that you know what the Cage does, it’s time to run it. Update the try block of your POST /submit handler to contain the following:

javascript
// Extract name and phone from the JSON the user sends from the form
const { name, phone } = req.body;
console.log('Sending SMS for', { name, phone });
// Verify phone number using a Cage
// Will return a result including valid true:false,
// as well as country code information
const { result } = await evervault.run('verify-phone-number', { phone });
// If the phone number is valid, send the SMS through Twilio
if (result.isValid) {
console.log('Verified')
} else {
// If the phone number is not valid, return an error to the user
console.log('Phone number not valid')
return res.json({ success: false });
}
return res.json({ success: true });

Note: If you did not name your GitHub repo 'verify-phone-number', make sure you reference the correct repo in this snippet of code.

Complete the form hosted on your Relay domain again. In the Replit console, you should see Verified or an error message returned. Notice that the phone number remains encrypted in the Console log.

Let’s summarize where you are. You’ve:

  1. Created an Express.js server (index.js),
  2. Created a HTML form for collecting a name and a phone number from a user (index.html),
  3. Connected your form to your server,
  4. Integrated Evervault Relay so that phone numbers submitted to your form are encrypted before they enter your server,
  5. Included the Evervault SDK so that outbound requests from your server are automatically decrypted, and
  6. Deployed a Cage to verify that phone numbers are valid.

Great! Now that you've used a Cage to verify the user's phone number, it’s time to use Twilio to send them an SMS.

Sending an SMS with Twilio

Let’s now send an SMS to your user with the Twilio SMS API. If you haven't got a Twilio account, the first thing you’ll need to do is create one.

Let’s create three Twilio environment variables inside Replit.

Add Twilio environment variables

Paste this code into your index.js file to initialize the Twilio SDK:

javascript
// Initialize Twilio
const client = require('twilio')(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);

Next, update the if block of your POST /submit handler to include the following:

javascript
// If the phone number is valid, send the SMS through Twilio
if (result.isValid) {
const sms = await client.messages.create({
body: `Hello, ${name}!`,
from: process.env.TWILIO_NUMBER,
to: phone,
});
}

This is what your completed index.js file should look like:

javascript
// Include Express.js, a library for easily building APIs in Node.js
const express = require('express');
const app = express();
// Including the Evervault SDK
const Evervault = require('@evervault/sdk');
// Initializing the Evervault SDK
const evervault = new Evervault(process.env.EVERVAULT_API_KEY);
// Initialize Twilio
const client = require('twilio')(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
// Parse JSON bodies
app.use(express.json());
// Serve files from public folder
app.use(express.static('public'));
app.post('/submit', async (req, res) => {
try {
// Extract name and phone from the JSON the user sends from the form
const { name, phone } = req.body;
console.log('Sending SMS for', { name, phone });
// Verify phone number using a Cage
// Will return a result including isValid (bool),
// as well as country code information
const { result } = await evervault.run('verify-phone-number', { phone });
// If the phone number is valid, send the SMS through Twilio
if (result.isValid) {
const sms = await client.messages.create({
body: `Hello, ${name}!`,
from: process.env.TWILIO_NUMBER,
to: phone,
});
} else {
// If the phone number is not valid, return an error to the user
return res.json({ success: false });
}
return res.json({ success: true });
} catch (err) {
return res.json({ success: false });
}
});
// Start the server and listen on port 3030
app.listen(3030, () => {
console.log('App listening on port 3030');
});

If you submit the form with your real phone number, you should receive an SMS: Hello, Claude Shannon!.

That’s it! You’ve built a fully-functioning encrypted app with Evervault.

Summary

Let’s summarize what you’ve built. You’ve:

  1. Created an Express.js server (index.js),
  2. Created a HTML form for collecting a name and a phone number from a user (index.html),
  3. Connected your HTML form to your Express.js server,
  4. Integrated Evervault Relay so that phone numbers submitted to your form are encrypted before they enter your server,
  5. Included the Evervault SDK so that outbound requests from your server are automatically decrypted,
  6. Deployed a Cage to verify that phone numbers are valid, and
  7. Integrated the Twilio SMS API to send a user a text — without ever handling their phone number in plaintext.

Was this page useful?