意外に簡単!LINEのような電話番号認証の作り方(Twilio-SMS編)

LINEで使われているような電話番号による認証を試しに作ってみたので、ブログにまとめておきます。
電話番号認証とはこんなのです。
f:id:i2key:20130210184107p:plain:w200f:id:i2key:20130210184112p:plain:w200

まず、電話番号を用いた認証ですが、方式が2通りありますので、それらの概要を説明します。

  • SMS利用パターン

電話番号をアプリ側で入力すると、サーバから4桁の暗証番号入りのSMSが届き、その4桁の暗証番号をアプリ側で入力することで電話番号が正しいことを証明するやりかたです。
SMSを送信するには送信元の番号が必要になります。また、例えばアメリカ、カナダのようにショートコード(SMSで利用される事業者コード)を取得する必要のある国もあります。ショートコードは審査が入るため、数ヶ月取得に要することがあります。今回は送信者番号は通常の電話番号にします。

  • IVR利用パターン

IVRとはInteractive Voice Responseの略で、自動音声応答による認証です。
事前にサービスに電話番号を登録しておき、ユーザにサービスの電話番号へ電話をかけさせることで、その番号を認証するInbound方式と、サービス側から登録した電話番号に電話をかけるOutbound方式があります。これはサービスを展開する国の電話料金、国際電話、国内電話の条件にあわせて使い分けることになります。
SMS認証だけでは、SMSが到達しなかったりとエラーをハンドリングした運用が必要になるため、補足的にIVRをリカバリ手段として提供するケースが多いです。

今回はSMS利用パターンで電話番号認証を作ってみました。
まずは、SMSを送信するためのサービスプロバイダーが何社かありますので、そのなかの一つを使ってみます。

SMS/IVR両方可能 無料枠が$20あり、開発用に自分に電話するだけであればある程度無料で利用可。

IVRのみ可能。日本のサービスなのでサポートが安心?

日本企業ですが、B2B型なので、個人サービサーがさくっとはじめるには敷居が高いかも。

ということで、Twilioの無料枠内で実験してみました。
SMS送信元番号で利用する電話番号はTwilio内で取得可能なので、それを使います。
Twilioで日本の番号を取得しようとするとSMSがno availableです。月$5で電話番号が取得できます。
f:id:i2key:20130210200259p:plain:w400
なので、USでてきとーに取得します。USの番号はSMS利用可能です。USは月$1で電話番号が取得できます・・・。すごい。
f:id:i2key:20130210200307p:plain:w400

次にSMS送信方法ですが、以下のAPIで簡単にSMSが送信できます。

curl -X POST 'https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/SMS/Messages.json' \
-d 'From={+81のような国際電話形式の送信元電話番号}' \
-d 'To={+81のような国際電話形式の送信先電話番号}' \
-d 'Body={メッセージ本文}' \
-u {AccountSid}:{AuthToken}

Twilio REST APIのリファレンスはこちら

更にTwilioは各言語のためのSDKを用意してくれているので、それを使えばさくっとAPI利用できます。
Javaでは以下の感じです。

// Install the Java helper library from twilio.com/docs/libraries
import com.twilio.sdk.TwilioRestClient;
import com.twilio.sdk.TwilioRestException;
import com.twilio.sdk.resource.factory.SmsFactory;
import com.twilio.sdk.resource.instance.Sms;
import com.twilio.sdk.resource.list.SmsList;
import java.util.HashMap;
import java.util.Map;
 
public class Example { 
 
  // Find your Account Sid and Token at twilio.com/user/account
  public static final String ACCOUNT_SID = "SIDを設定してね";
  public static final String AUTH_TOKEN = "{{ auth_token }}";
 
  public static void main(String[] args) throws TwilioRestException {
    TwilioRestClient client = new TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN);
 
    // Build a filter for the SmsList
    Map<String, String> params = new HashMap<String, String>();
    params.put("Body", "メッセージ本文");
    params.put("To", "+81901234XXXX");
    params.put("From", "+81909876XXXX");
     
    SmsFactory messageFactory = client.getAccount().getSmsFactory();
    Sms message = messageFactory.create(params);
    System.out.println(message.getSid());
  }
}

これを利用して、AWS環境でSMS認証サーバを構築しました。下図に処理フローをまとめてあります。認証用の一時データ保存領域としてMemcache(ElastiCache)を、QueueとしてSQSを使いました。
f:id:i2key:20130210193754p:plain:w800

基本的には図に書いてある通りです。
補足としては... Twilioはショートコードを利用するとSMSの一括大量送信が可能になるのですが、今回はショートコードを取得していない関係上、Rate Limitで1送信元電話番号あたり1秒に1通しか送信できないという制限にひっかかってしまいます。そこで、どうせ月1ドルなので、スケールさせたい数だけ電話番号を取得して、取得した番号の数だけ、スレッドプールにSMS送信スレッドをプールする方式をとりました。それで購入した電話番号の数だけ並列送信が可能になります。そのためにQueueを利用しています。

  • 実装環境

サーバ : AWS EC2
Memcache : AWS ElastiCache
Queue : AWS SQS
FW : Play2.0.4
言語 : Java