Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rate limit of twilio support #593

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ DATABASE_NAME=staging
DATABASE_URL=mysql://username:password@hostname:port/database
TWILIO_ACCOUNT_SID=ACCOUNT_ID
TWILIO_AUTH_TOKEN=ACCOUNT_TOKEN
TWILIO_SERVICE_SID=XXX
TWILIO_SERVICE_SID=VAXXX
TWILIO_RATE_LIMIT_SID=RKxxx
TWILIO_RATE_LIMIT_BUCKET_SID=BLxxx
TWILIO_RATE_LIMIT_BUCKET_MAX=4
TWILIO_RATE_LIMIT_BUCKET_INTERVAL=60
RECAPTCHA_SITE_KEY=RECAPTCHA_KEY
RECAPTCHA_SECRET=RECAPTCHA_SECRET
CONVEYOR_POSTING_WIF=CONVEYOR_KEY
Expand Down
34 changes: 31 additions & 3 deletions helpers/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ async function sendSMS(to, body) {
/**
* Send a SMS Code.
* @param to Message recipient, e.g. +1234567890.
* @param client_ip the client ip, e.g. 1.1.1.1
*/
async function sendSMSCode(to) {
async function sendSMSCode(to, client_ip) {
if (DEBUG_MODE) {
logger.warn('Send SMS to %s', to);
logger.warn('Send SMS to %s, client_ip: %s', to, client_ip);
} else {
return twilio.sendAuthCode(to);
return twilio.sendAuthCode(to, client_ip);
}
}

Expand Down Expand Up @@ -571,6 +572,30 @@ async function getPendingClaimedAccountsAsync() {
});
}

async function createTwilioRateLimit() {
if (DEBUG_MODE) {
twilio = require('./twilio');
return twilio.createTwilioRateLimit();
}
return {};
}

async function createTwilioRateLimitBucket() {
if (DEBUG_MODE) {
twilio = require('./twilio');
return twilio.createTwilioRateLimitBucket();
}
return {};
}

async function updateTwilioRateLimitBucket() {
if (DEBUG_MODE) {
twilio = require('./twilio');
return twilio.updateTwilioRateLimitBucket();
}
return {};
}

module.exports = {
checkUsername,
condenserTransfer,
Expand All @@ -596,4 +621,7 @@ module.exports = {
recordSmsTracker,
recordSource,
getPendingClaimedAccountsAsync,
createTwilioRateLimit,
createTwilioRateLimitBucket,
updateTwilioRateLimitBucket,
};
52 changes: 46 additions & 6 deletions helpers/twilio.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const fromNumber = process.env.TWILIO_PHONE_NUMBER;
const serviceSid = process.env.TWILIO_SERVICE_SID;
const rateLimitSid = process.env.TWILIO_RATE_LIMIT_SID;
const rateLimitBucketSid = process.env.TWILIO_RATE_LIMIT_BUCKET_SID;
const rateLimitBucketMax = process.env.TWILIO_RATE_LIMIT_BUCKET_MAX;
const rateLimitBucketInterval = process.env.TWILIO_RATE_LIMIT_BUCKET_INTERVAL;
const authCodeServiceSid = process.env.TWILIO_AUTH_CODE_SERVICE_SID;

if (!accountSid || !authToken) {
Expand All @@ -16,6 +20,10 @@ if (!fromNumber && !serviceSid && !authCodeServiceSid) {
);
}

if (!rateLimitSid) {
throw new Error('Misconfiguration: Missing twilio rate limit config');
}

const client = new Twilio(accountSid, authToken);

async function sendMessage(to, body) {
Expand All @@ -28,25 +36,57 @@ async function sendMessage(to, body) {
return client.messages.create(payload);
}

async function sendAuthCode(to) {
return client.verify
.services(authCodeServiceSid)
.verifications.create({ to, channel: 'sms' });
async function sendAuthCode(to, client_ip) {
return client.verify.v2.services(authCodeServiceSid).verifications.create({
to,
channel: 'sms',
rateLimits: {
end_user_ip_address: client_ip,
},
});
}

async function checkAuthCode(to, code) {
return client.verify
return client.verify.v2
.services(authCodeServiceSid)
.verificationChecks.create({ to, code });
}

async function isValidNumber(numberE164) {
return client.lookups.v1.phoneNumbers(numberE164).fetch();
return client.lookups.v2.phoneNumbers(numberE164).fetch();
}

async function createTwilioRateLimit() {
return client.verify.v2.services(authCodeServiceSid).rateLimits.create({
description: 'Limit on end user IP Address',
uniqueName: 'end_user_ip_address',
});
}

async function createTwilioRateLimitBucket() {
return client.verify.v2
.services(authCodeServiceSid)
.rateLimits(rateLimitSid)
.buckets.create({
max: rateLimitBucketMax,
interval: rateLimitBucketInterval,
});
}

async function updateTwilioRateLimitBucket() {
return client.verify.v2
.services(authCodeServiceSid)
.rateLimits(rateLimitSid)
.buckets(rateLimitBucketSid)
.update({ max: rateLimitBucketMax, interval: rateLimitBucketInterval });
}

module.exports = {
sendMessage,
sendAuthCode,
checkAuthCode,
isValidNumber,
createTwilioRateLimit,
createTwilioRateLimitBucket,
updateTwilioRateLimitBucket,
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"sequelize": "5.2.4",
"serve-favicon": "2.4.5",
"tronweb": "^3.0.0",
"twilio": "^3.64.0",
"twilio": "^5.2.0",
"validator": "9.2.0"
},
"devDependencies": {
Expand Down
15 changes: 15 additions & 0 deletions routes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,19 @@ router.get(
apiMiddleware(apiHandlers.handleCreateTronAddr)
);

router.get(
'/create_twilio_rate_limit',
apiMiddleware(apiHandlers.handleCreateTwilioRateLimit)
);

router.get(
'/create_twilio_rate_limit_bucket',
apiMiddleware(apiHandlers.handleCreateTwilioRateLimitBucket)
);

router.get(
'/update_twilio_rate_limit_bucket',
apiMiddleware(apiHandlers.handleUpdateTwilioRateLimitBucket)
);

module.exports = router;
31 changes: 28 additions & 3 deletions routes/apiHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1396,7 +1396,7 @@ async function handleRequestSmsNew(req) {
});
req.log.info(
{ response, ip: req.ip, req: req.body },
'sms_response_info_in_country_code_list'
'sms_message_send_in_country_code_list'
);
if (response && response.status !== 'pending') {
throw new ApiError({
Expand All @@ -1406,15 +1406,15 @@ async function handleRequestSmsNew(req) {
});
}
} else {
const response = await services.sendSMSCode(phoneNumber);
const response = await services.sendSMSCode(phoneNumber, req.ip);
services.recordSmsTracker({
sendType: 'after_send_sms_2',
countryCode: req.body.prefix,
phoneNumber: req.body.phoneNumber,
});
req.log.info(
{ response, ip: req.ip, req: req.body },
'sms_response_info'
'sms_verify_sent'
);
if (response && response.status !== 'pending') {
throw new ApiError({
Expand Down Expand Up @@ -1600,6 +1600,10 @@ async function handleConfirmSmsNew(req) {
req.body.phoneNumber,
req.body.code
);
req.log.info(
{ response, ip: req.ip, req: req.body },
'sms_verify_apply'
);
if (response === true) {
record.phone_code = req.body.code;
record.save();
Expand Down Expand Up @@ -2099,6 +2103,24 @@ async function handleCreateTronAddr() {
return tronUser;
}

async function handleCreateTwilioRateLimit() {
if (process.env.DEBUG_MODE) {
return services.createTwilioRateLimit();
}
}

async function handleCreateTwilioRateLimitBucket() {
if (process.env.DEBUG_MODE) {
return services.createTwilioRateLimitBucket();
}
}

async function handleUpdateTwilioRateLimitBucket() {
if (process.env.DEBUG_MODE) {
return services.updateTwilioRateLimitBucket();
}
}

module.exports = {
handleRequestEmail,
handleRequestSms,
Expand All @@ -2116,4 +2138,7 @@ module.exports = {
handleConfirmEmailCode,
handleCreateAccountNew,
handleCreateTronAddr,
handleCreateTwilioRateLimit,
handleCreateTwilioRateLimitBucket,
handleUpdateTwilioRateLimitBucket,
};
Loading
Loading