Skip to content

Commit

Permalink
add rate limit of twilio support
Browse files Browse the repository at this point in the history
  • Loading branch information
ety001 committed Jun 21, 2024
1 parent ceb1503 commit f54e0b8
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 78 deletions.
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

0 comments on commit f54e0b8

Please sign in to comment.