这份文档还在翻译中,预期年底前完成。欢迎您提供宝贵的意见及建议。
Private SMS communication
Sometimes you want two parties to exchange SMS without revealing their actual phone numbers.
For example, if you are operating a taxi booking service, then you want your customers and drivers to be able to communicate to coordinate pick-up times, locations, etc. But you don't want your driver to know your customer's phone number, so that you can protect their privacy. And, conversely, you don't want your customer to know the driver's number and be able to book taxi services directly, bypassing your application.
In this tutorial
This tutorial is based on the private SMS use case. It teaches you how to build an SMS proxy system using Node.js and the Node Server SDK, using a virtual phone number to mask the real numbers of the participants.
To build the application, you perform the following steps:
- Create the basic web application - build the basic application framework
- Configure the application - to use your API key and secret and the virtual number you have provisioned
- Create a chat - create a mapping between your users' real numbers and the virtual one
- Receive inbound SMS - capture incoming SMS on your virtual number and forward them to the target user's real number
Prerequisites
To complete this tutorial, you need:
- A Vonage account - for your API key and secret and to rent virtual numbers.
- A Vonage number - to hide each user's real number. You can rent a number in the developer dashboard.
- The source code on GitHub - installation instructions are in the README.
- Node.js installed and configured.
- ngrok - (optional) to make your development web server accessible to Vonage's servers over the Internet.
Create the basic web application
This application uses the Express framework for routing and the Node Server SDK for sending and receiving SMS. We use dotenv
so that we can configure the application in a .env
text file.
In server.js
we initialize the application's dependencies and start the web server. We provide a route handler for the application's home page (/
) so that you can test that the server is running by visiting http://localhost:3000
:
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const SmsProxy = require('./SmsProxy');
const app = express();
app.set('port', (process.env.PORT || 3000));
app.use(bodyParser.urlencoded({ extended: false }));
app.listen(app.get('port'), function () {
console.log('SMS Proxy App listening on port', app.get('port'));
});
const smsProxy = new SmsProxy();
app.get('/', (req, res) => {
res.send('Hello world');
})
Note that we are instantiating an object of the SmsProxy
class to handle the routing of messages sent to your virtual number to the intended recipient's real number. We cover the actual proxying process in proxy the SMS, but for now be aware that this class initializes nexmo
using the API key and secret that you will configure in the next step. This enables your application to send and receive SMS:
const Nexmo = require('nexmo');
class SmsProxy {
constructor() {
this.nexmo = new Nexmo({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET
}, {
debug: true
});
}
...
Configure the application
Copy the example.env
file provided to .env
and modify it to include your Vonage API key and secret and your Vonage number. You can find this information in the developer dashboard:
VONAGE_API_KEY=YOUR_VONAGE_API_KEY
VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET
VONAGE_NUMBER=YOUR_VONAGE_NUMBER
Create a chat
To use the application, you make a POST
request to the /chat
route, passing in the real phone numbers of two users. (You can see a sample request in start the chat).
The route handler for /chat
is shown below:
app.post('/chat', (req, res) => {
const userANumber = req.body.userANumber;
const userBNumber = req.body.userBNumber;
smsProxy.createChat(userANumber, userBNumber, (err, result) => {
if (err) {
res.status(500).json(err);
}
else {
res.json(result);
}
});
res.send('OK');
});
The chat object is created in the createChat()
method of the smsProxy
class. It stores each user's real number:
createChat(userANumber, userBNumber) {
this.chat = {
userA: userANumber,
userB: userBNumber
};
this.sendSMS();
}
Now that we have created a chat, we need to let each user know how they can contact the other.
Introduce the users
Note: In this tutorial each user receives the virtual number via an SMS. In production systems this could be supplied using email, in-app notifications or as a predefined number.
In the sendSMS()
method of the smsProxy
class we use the sendSms()
method to send two messages to the virtual number from each user's real number.
sendSMS() {
/*
Send a message from userA to the virtual number
*/
this.nexmo.message.sendSms(this.chat.userA,
process.env.VIRTUAL_NUMBER,
'Reply to this SMS to talk to UserA');
/*
Send a message from userB to the virtual number
*/
this.nexmo.message.sendSms(this.chat.userB,
process.env.VIRTUAL_NUMBER,
'Reply to this SMS to talk to UserB');
}
We now need to intercept these incoming messages on the virtual number and proxy them to the intended recipient's real number.
Receive inbound SMS
When one user sends a message to the other, they are sending it to the application's virtual number instead of the target user's real number. When Vonage receives an inbound SMS on the virtual number it makes an HTTP request to the webhook endpoint associated with that number:
In server.js
, we provide a route handler for the /webhooks/inbound-sms
request that Vonage's servers make to your application when your virtual number receives an SMS. We're using a POST
request here, but you could also use GET
or POST-JSON
. This is configurable in the dashboard, as described in expose your application to the internet.
We retrieve the from
and text
parameters from the inbound request, and pass them to the SmsProxy
class to determine which real number to send it to:
app.get('/webhooks/inbound-sms', (req, res) => {
const from = req.query.msisdn;
const to = req.query.to;
const text = req.query.text;
// Route virtual number to real number
smsProxy.proxySms(from, text);
res.sendStatus(204);
});
We return a 204
status (No content
) to represent successful receipt of the message. This is an important step, because if we don't acknowledge receipt Vonage's servers will make repeated attempts to deliver it.
Determine how to route the SMS
Now that you know the real number of the user sending the SMS, you can forward the message to the real number of the other user. This logic is implemented in the getDestinationRealNumber()
method of the SmsProxy
class:
getDestinationRealNumber(from) {
let destinationRealNumber = null;
// Use `from` numbers to work out who is sending to whom
const fromUserA = (from === this.chat.userA);
const fromUserB = (from === this.chat.userB);
if (fromUserA || fromUserB) {
destinationRealNumber = fromUserA ? this.chat.userB : this.chat.userA;
}
return destinationRealNumber;
}
Now that you can determine which user to send the message to, all you have to do is send it!
Proxy the SMS
Proxy the SMS to the real phone number of the intended recipient. The from
number is always the virtual number (to preserve the user's anonymity), the to
is the user's real phone number.
proxySms(from, text) {
// Determine which real number to send the SMS to
const destinationRealNumber = this.getDestinationRealNumber(from);
if (destinationRealNumber === null) {
console.log(`No chat found for this number);
return;
}
// Send the SMS from the virtual number to the real number
this.nexmo.message.sendSms(process.env.VIRTUAL_NUMBER,
destinationRealNumber,
text);
}
Try it out
Expose your application to the Internet
When the SMS API receives an SMS destined for your virtual number, it alerts your application via a webhook. The webhook provides a mechanism for Vonage's servers to communicate with yours.
For your application to be accessible to Vonage's servers, it must be publicly available on the Internet. One way to achieve this during development and testing is to use ngrok, a service that exposes local servers to the public Internet over secure tunnels. See this blog post for more details.
Download and install ngrok, then start it with the following command:
ngrok http 3000
This creates public URLs (HTTP and HTTPS) for any web site that is running on port 3000 on your local machine.
Use the ngrok
web interface at http://localhost:4040 and make a note of the URLs that ngrok
provides.
Go to your account settings page and enter the full URL to your webhook endpoint in the "Inbound Messages" text box. For example, if you are using ngrok
then your URL might resemble the following:
https://33ab96a2.ngrok.io/webhooks/inbound-sms
Ensure that you select POST
from the "HTTP Method" drop-down list so that Vonage knows that your application is expecting the message details to be delivered via a POST
request.
Start the chat
Make a POST
request to your application's /chat
endpoint, passing in your users' real numbers as request parameters.
You could use Postman for this, or a curl
command similar to the following, replacing USERA_REAL_NUMBER
and USERB_REAL_NUMBER
with your users' actual numbers:
curl -X POST \
'http://localhost:3000/chat?userANumber=USERA_REAL_NUMBER&userBNumber=USERB_REAL_NUMBER'
Continue the chat
Each user should receive a text from the application's virtual number. When a user replies to that number it is delivered to the other user's real number, but appears to come from the virtual one.
Conclusion
In this tutorial, you learned how to build an SMS proxy to enable two users to exchange SMS without either seeing the other's real number.
Where Next?
You could extend this sample application to use the same virtual number to host multiple chats by using SmsProxy.createChat()
to instantiate and then persist a separate chat
object for different pairs of users. So, for example, you could have one chat
object for userA
and userB
to converse and another for userC
and userD
.
You could create routes that enable you to see all current chats and also terminate a chat when it's over.
The following resources will help you find out more about what you learned in this tutorial: