Securely Send Mass SMS in Node.js

Posted on March 22, 2017

The Flowroute messaging API enables developers to programmatically send and receive SMS text messages. Many scenarios require the ability to provide notifications by sending mass messages to a list of opt-in recipients. The cloud provides the services and components necessary to securely authenticate a user with administrative privileges, persist the recipient numbers, and send the SMS texts. It also enables a secure store for secrets such as database connection strings and API keys. For smaller projects, these resources are open source and free to use.

To get started, ensure you have met the following prerequisites:

– Installed the latest stable version of Node.js: https://nodejs.org/

– Have access to a functional Docker engine: https://www.docker.com/

– Activated your Flowroute account

Database in the Cloud

A simple option for hosting MongoDB in the cloud is a service called mLab (http://www.mlab.com/). Sign up for an account and opt for the free 0.5 GB database. Figure 1 shows the button to tap on the web-based UI.

Create new MongoDB

Figure 1: Create new Mongo DB Deployment

After you choose to create the deployment, you are given the option to choose your cloud provider. Pick whichever one you are comfortable working with (this does not require that you have an existing account with the cloud provider and simply gives you the option to choose where mLab will host your database). Use the single-node plan for the free sandbox, and give it the name telephony as shown in Figure 2. Tap the button in the lower right to create your database.

Deployment Options

Figure 2: Deployment options

After the database is created, navigate to the “Users” tab and tap the “add database user” button.  This example used the name “telephony-user.” Choose a password for the user. At the top of the UI is a connection string as shown in Figure 3. Write this down (the connection string you’ll use is everything after the two slashes) with the appropriate username and password, i.e.:

telephony-user:mysecretpwd@ds012345.mlab.com:58369/telephony

Connection String
Figure 3: Connection String

The database will contain two collections, one for a list of authorized users and another with a list of approved telephone numbers. This will be populated once you have a user-id for authentication.

Authentication as a Service

Authentication is required to identify users who are authorized to send the SMS texts. Without this crucial step, anyone could potentially blast the service with spam text messages. A convenient and free way to handle authentication is provided by Auth0 at http://auth0.com. After you sign up, you are given the option to use your GitHub, Google, LinkedIn, or Microsoft Account credentials. There is also an option for a work email and password if you don’t want to use a third-party provider. The provider you choose to login with is important because it must match what you will set as an “approved user” to send SMS emails.

After logging in, Auth0 will create a default “client” app so that you can build authentication into your own software. For now, choose the “Clients” option, tap “Default App” and note:

– Your domain (for example, user.auth0.com)

– Your client id

– Your client secret (you can click the checkbox to reveal the secret or use the copy icon to copy it to the clipboard)

There is one more important piece of information. In the navigation bar, tap on “Users” and you will see your own account listed. Click on the link with your name to grab the user details, then tap the “Raw JSON” tab. This will list various properties in JSON format that look like this:

Bulk text code

The credentials may be different depending on what provider you used, but all should give you a value for “user id” with the provider. Note this value down. In the example, this value is:

google-oauth2|199999---------------

You will use this in a minute.

Database Client

A simple way to manage your database is to use the Mongoclient management tool: https://hub.docker.com/r/mongoclient/mongoclient/. Follow the Docker instructions to pull the container down and run it, then browse to http://localhost:3000. Tap “Connect” in the upper right to open the connection dialog, then “Create New.” Give it the name “mLab” and paste the connection URL you saved earlier for your MongoDB (for this dialog, you will need to prefix it with mongodb://). After you tab out of the field, it will auto-populate the rest of the form as shown in Figure 4.

Connection Client

Figure 4: The Connection Client

Save your changes and tap “Connect Now.” If all goes well, you should be taken to the main screen.

Add the Authorized User

The next step is to add the authorized user that you created with Auth0. Tap “Tools” then “Shell” in the navigation. In the input box, type:

db.authorized.insertOne({"user": "google-oauth2|199999---------------", "source": "19995551212"});

Replace the property for “user” with the one you copied earlier from the raw JSON of your Auth0 account. For “source” use a valid phone number registered with your Flowroute account. This will be the number used to send out your mass SMS texts. You should see an acknowledgment. The command created the collection called “authorized” and inserted the first record that is your user identity.

Next, you can insert some phone numbers to target:

db.users.insertOne({ "phone" : "19995551212" , "verified" : true });

Repeat the command as many times as needed. For your production system, you will likely use a similar method to the one described in “Fast and Easy Two-Factor Authentication in Node.js” (https://blog.flowroute.com/2016/11/10/easy-two-factor-authentication-in-node-js/) for users to opt-in to receive SMS messages. When they confirm, the entry is flagged as “verified” and able to receive texts. Now that your authentication and database are set up you can build a simple UI to send the text messages.

The Angular App

Install the Angular Command Line Interface (CLI) with the command:

npm i -g @angular/cli

Once installed, create a new project:

ng new sms-mailer

Navigate to the project and install the helper libraries for authentication:

npm install --save auth0-lock angular2-jwt

Also, save the type definitions to provide IntelliSense:

npm install –save-dev @types/auth0-lock

Finally, create the shell for the authentication service:

ng g service auth

Now the UI is ready for code. The service (auth.service.ts) uses the existing libraries to manage authentication. The login experience is completely controlled by the third-party library. The service simply indicates how to store the token (in local storage) and makes a second call to the email to greet the user. Here is the code for the service:

The next step is to configure the authentication provider and provision the service. This is done in the app.module.ts file. A factory is used to provide the authentication library and is configured in the providers for the module along with the service:

Now you can update the application component for the UI. In this example, the UI is very basic. Normally you would create a custom component and reference it from the app component, but to keep the example simple you can update the HTML directly in app.component.html:

For the component code in app.component.ts, start with some basic properties and stub out the methods for the service calls:

At this stage, you should be able to run the app and authenticate. Create a folder under src called webtasks. The Flowroute messaging SDK for Node.js (https://github.com/flowroute/flowroute-messaging-nodejs) creates a directory named lib under flowroutemessaginglib. Copy this under the webtasks directory and rename it to flowroutelib so your final directory structure looks like Figure 5.

directory for sms

Figure 5: Folder Structure

Create a file in the root of the webtasks directory named index.js. This will be the serverless task that is used to both confirm the identity of the user and connect to the database to retrieve phone numbers. The basic shell references dependent libraries and uses express to set up handlers for secured HTTP operations on the API endpoint:

The getToken method is reused to parse the user’s claim for identity. If the token is valid based on the secret, the “sub” claim which represents the user is used to query the database. If a record is found, the phone number is passed back as the “source” for the user. Otherwise, the user is considered unauthorized and unable to use the API for sending SMS. One important aspect of this approach is that the secrets for the authentication and database connection are all stored in the API host and not as part of the source code. This means you will configure the secrets when you publish the API. This allows you to manage secrets in a secure way without having to put them inside your code base (the only exception is the client id stored as part of the Angular app).

When the API is called with the get verb, and the user is authorized, it will return the list of verified phone numbers:

When it is called with a post verb, it parses the body of the post for a “to” phone and message, uses the source phone based on the user’s identity, then calls the Flowroute API to send the SMS.

Now the service is ready to publish. This example uses webtask (https://webtask.io/), a service that works in conjunction with Auth0. First, install the web task command line interface (CLI):

npm i -g wt-cli

Next, initialize with your Auth0 email:

wt init myemail@myemail.com

You will be prompted for a code that is sent to the email address, and then you are activated.

The next step is typically to publish the service using a create command. This will work for most cases, but in this example, the custom Flowroute SDK is included, so the service must be bundled before getting published. Bundle the service by installing the bundler and then typing this from the root of the sms-mailer project:

npm i -g wt-bundle

wt-bundle --output src/webtasks/sms.js src/webtasks/index.js

This will bundle all dependencies into a single file. Now you can publish the service:

wt create src/webtasks/sms.js --meta wt-compiler=webtask-tools/express -s AUTH0_CLIENT_SECRET=<Auth0 client secret> -s MONGO_URL=<Mongo URL without the mongodb:// portion> -s FLOWROUTE_ACCESS_KEY=<flowroute access key> -s FLOWROUTE_SECRET_KEY=<flowroute secret key>

The command takes the bundled file, notes that it uses express, then passes secrets for authentication, the database, and the Flowroute API. If it is successful, you will receive a success message with the API endpoint. Update this in the “URL” property in your app.component.ts.

With the proper URL in place, it is now possible to implement the method to get the list of phones. Notice it uses AuthHttp instead of Angular’s built-in Http. This will automatically secure the API endpoint call with the identity of the signed-in user using encrypted JSON Web Tokens (JWT).

The post method iterates the phones and calls to send the message. It uses some basic logic to keep track of success and failure. In a production project, you could leverage Reactive Extensions (RxJS) to better manage the stream of asynchronous calls rather than having to keep a count on the component, but the approach in the example is a simple way to demonstrate the API call.

Now everything is in place! You should be able to run the app with:

ng serve

Then navigate to http://localhost:4200 and perform the three steps:

1. Authenticate/log in with Auth0

2. Retrieve the list of phones

3. Send the mass SMS

Figure 6 shows a sample run.

Figure 6: Sample Execution

If the services don’t appear to be working, you can troubleshoot them using a live stream. From a shell prompt, type:

wt logs

This will start the live log display as shown in . From there, you can read the console to troubleshoot your issue.

 Figure 7: Webtask logs

There are a few ways you can approach the architecture for the app. For example, if your system must send out massive numbers of SMS messages, it may not be feasible to wait for the messages to send from the UI. Although one approach would be to create a single service that iterates the phone numbers and sends out the messages (so the numbers are iterated in the service and not from the UI). Another approach is to write to a collection as a “queue” of messages to send, and create a regularly scheduled job that reads from the collection and sends out the messages.

Conclusion

Leveraging free, open party solutions, you can architect a cloud-based application to securely send a message through the Flowroute API. This example leveraged:

– Auth0 – authentication as a service

– WebTask – serverless code as a service and secure secret management

– mLab – database as a service

Most importantly, it leveraged the powerful telephony-as-a-service features of Flowroute to enable sending mass SMS messages in a secure manner to verified, opt-in end users.