Simple minds think alike

より多くの可能性を

【Golang】Twilio VerifyでSMS送信、検証する方法

直近仕事でTwilio Verifyに関して調査した時に、SMS送信、パスコード検証のサンプルアプリを作ってみたので手順を記事にまとめてみます。

サンプルアプリはGo言語のプロジェクトで作っていて、ライブラリはtwilio-goを使いました。

以下の公式記事を参考にしました。

www.twilio.com

前提

  • Twilioアカウント作成済み
  • direnvインストール済み

Twilio Verifyとは

Twilio Verify は SMS、電話、プッシュ通知、TOTP を利用して二要素認証を簡単に実現する仕組みです。

本記事では、SMSでワンタイムパスコードを送信し、二段階認証を実現します。Programmable Messaging (SMS)を使用する場合は、テキストを特定の電話番号SMS送信するだけですが、Verifyを使うと認証コードの発行や発行したコードの検証まで行ってくれます。

Programmable Messaging (SMS)との違い

まず特徴的な違いとしては以下の通りです。(2022年8月時点)

  • 違いがないところでは、国際網・国内網のどちらでも送信可能
  • SMSを送信する電話番号として共用番号のみ選択可能
  • 国内網で送信する場合 Twilio Verifyの方が1通あたり$0.01 高い
    • Twilio Verifyの場合、1通送信$0.8+認証成功$0.5=$0.13
    • Programmable Messagingの場合、$0.12
  • 認証コードのカスタマイズ性が低い
  • Twilio Verifyの場合は、メッセージテンプレートを使うことになるので送信メッセージの自由度が低い(参考)
    • Publicテンプレート: 全ユーザー共通のテンプレート
    • Privateテンプレート: 顧客毎に設定するテンプレート。どこまで自由に設定できるのかは不明。

Verify Serviceの作成

Twilioのアカウントを作成後、Verify Serviceを作成します。

Twilioのダッシュボード画面でVerify Serviceを作成

表示されるダイアログ上で、SMSだけを選択し、Createを押します。

TwilioダッシュボードのVerify Service新規作成画面

Verify Serviceを作成するとService SIDが発行されるためそれをコピーして控えておきます。 TwilioダッシュボードのServicesからService SIDを確認

Account SIDとAuth token

また、Verify APIを使うにはAccount SIDとAuth tokenも必要になるため、console上からこれらもコピーして控えておきます。 TwilioのダッシュボードからAccount SIDとAuth tokenをコピー

サンプルプロジェクト作成

$ mkdir verify-go
$ cd verify-go

.envrcを作成し、先ほど控えておいて環境変数を設定します。

$ direnv edit .
export TWILIO_ACCOUNT_SID=XXXXXXXXXXXXXXXXXXXXXXXXX
export TWILIO_AUTH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXX
export VERIFY_SERVICE_SID=XXXXXXXXXXXXXXXXXXXXXXXXX

go.mod を作っておきます。

$ go mod init verify

twilio-goをインストール

$ go get github.com/twilio/twilio-go

ここからはGoのコードを書いていきます。

まずは環境変数を読み込み、Twilioのクライアントを生成します。

package main

import (
   "fmt"
   "os"

   "github.com/twilio/twilio-go"
   openapi "github.com/twilio/twilio-go/rest/verify/v2"
)

var TWILIO_ACCOUNT_SID string = os.Getenv("TWILIO_ACCOUNT_SID")
var TWILIO_AUTH_TOKEN string = os.Getenv("TWILIO_AUTH_TOKEN")
var VERIFY_SERVICE_SID string = os.Getenv("VERIFY_SERVICE_SID")
var client *twilio.RestClient = twilio.NewRestClientWithParams(twilio.ClientParams{
   Username: TWILIO_ACCOUNT_SID,
   Password: TWILIO_AUTH_TOKEN,
})

次にワンタイムパスコード(OTP)を送る機能と送ったOTPを検証する機能を追加していきます。

以下のコードではVerificationのエンドポイントを呼び出してOTPを送信します。

func sendOtp(to string) {
   params := &openapi.CreateVerificationParams{}
   params.SetTo(to)
   params.SetChannel("sms")

   resp, err := client.VerifyV2.CreateVerification(VERIFY_SERVICE_SID, params)

   if err != nil {
       fmt.Println(err.Error())
   } else {
       fmt.Printf("Sent verification '%s'\n", *resp.Sid)
   }
}

以下のコードではユーザー入力受け付け、受け取った文字列パスコードとしてVerification Checkのエンドポイントに送信し、OTPを検証します。

func checkOtp(to string) {
   var code string
   fmt.Println("Please check your phone and enter the code:")
   fmt.Scanln(&code)

   params := &openapi.CreateVerificationCheckParams{}
   params.SetTo(to)
   params.SetCode(code)

   resp, err := client.VerifyV2.CreateVerificationCheck(VERIFY_SERVICE_SID, params)

   if err != nil {
       fmt.Println(err.Error())
   } else if *resp.Status == "approved" {
       fmt.Println("Correct!")
   } else {
       fmt.Println("Incorrect!")
   }
}

最後にmain関数を実装します。

func main() {
   to := "【SMSを送信する電話番号 (例: +818011112222)】"

   sendOtp(to)
   checkOtp(to)
}

実行すると、main関数に書いた電話番号にSMSが送信され、認証コードが届きます。受信した認証コードを入力し、検証結果が表示されます。

$ go run .
Sent verification 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
Please check your phone and enter the code:
XXXXXX
Correct!

"スマートフォンで送られてきたSMSの認証コードを確認"

curl実行する

参考までにcurlで実行するコマンドも書いておきます。もしよろしければ動かない時のトラブルシューティングとしてお使いください!

$ curl -X POST https://verify.twilio.com/v2/Services/{Service SID}/Verifications \
--data-urlencode "To=+81xxxxxxxxx" \
--data-urlencode "Channel=sms" \
-u your_account_sid:your_auth_token
$ curl -X POST https://verify.twilio.com/v2/Services/{Service SID}/VerificationCheck \
--data-urlencode "To=+81xxxxxxxxx" \
--data-urlencode "Code=123456" \
-u your_account_sid:your_auth_token

感想

費用としてはProgrammable Messaging (SMS)よりもちょっと高いくらいでSMSを経由した2段階認証機能が簡単に作れるので、あまり工数に余裕がない状況で予算は十分にある、という場合には採用検討の余地がありそうと思いました。

参考記事