Open13

twilio

nicopinnicopin
nicopinnicopin

Verification logic

import { HttpException, Injectable, NestMiddleware } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NextFunction, Request, Response } from 'express';
import { validateRequest } from 'twilio/lib/webhooks/webhooks';

@Injectable()
export class TwilioSignatureMiddleware implements NestMiddleware {
	constructor(private readonly configService: ConfigService) {}

	use(req: Request, res: Response, next: NextFunction) {
		const signature = req.headers['x-twilio-signature'] as string;
		if (!signature) {
			throw new HttpException('No signature provided', 403);
		}
		// somehow req.protocol return http(it may be because of alb->ecs connection is http)
		const url = 'https://' + req.get('host') + req.originalUrl;
		const sortedBody = Object.keys(req.body)
			.sort()
			.reduce((acc, key) => {
				acc[key] = req.body[key];
				return acc;
			}, {});
		const authToken = this.configService.get('TWILIO_ACCOUNT_AUTH_TOKEN');
		const isValid = validateRequest(authToken, signature, url, sortedBody);
		if (!isValid) {
			throw new HttpException('Invalid signature', 403);
		}
		next();
	}
}

nicopinnicopin

Initialize Client

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Twilio } from 'twilio';
import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message';

@Injectable()
export class TwilioSMSService {
	private readonly twilioClient: Twilio;
	private readonly twilioPhoneNumber: string;

	constructor(private readonly configService: ConfigService) {
		const apiKeySid = this.configService.get('TWILIO_API_KEY_SID');
		const apiKeySecret = this.configService.get('TWILIO_API_KEY_SECRET');
		const accountSid = this.configService.get('TWILIO_ACCOUNT_SID');
		this.twilioClient = new Twilio(apiKeySid, apiKeySecret, {
			accountSid: accountSid,
			autoRetry: true, //defaultではリトライなしなので必要なら明示
			maxRetries: 3,
		});
		this.twilioPhoneNumber = this.configService.get('TWILIO_PHONE_NUMBER');
	}

	send(to: string, body: string): Promise<MessageInstance> {
		return this.twilioClient.messages.create({
			body,
			from: this.twilioPhoneNumber,
			to,
		});
	}
}

Error: accountSid must start with AC. The given SID indicates an API Key which requires the accountSid to be passed as an additional option

アカウントのTokenではなくAPI keyを利用する場合にはオプションでアカウントのSIDを渡さないといけない