Twilio+Deviseで電話番号(SMS)ユーザログイン機能を実装
電話番号認証、サービスによってはすごい比率が高いらしいので実装したいなと思います。
目次
方針の検討
思いつくいい感じの方法は、3つほど。
- 電話からの音声案内
- 情報をアプリケーション側で開示した後、ユーザがSMSを送信
- アプリケーション側から6桁の番号を送信、ユーザはそれをブラウザから入力
3番が一番よくあるパターンだと思いますが、まずは1と2から検討してきたいと思います。
1. 電話からの音声案内
いい感じとは書いたものの、1番は手間的な意味でかなりハードルが高いので却下。SMSって届かないことがあるとよくきくので(rebuildでDaiseさんが出てるときもそんな話があったような)、大きくなったらリスクヘッジ的な意味で導入できたらいいなって感じですね。
2. 情報をアプリケーション側で開示した後、ユーザがSMSを送信
以下の記事で、受信のみで電話番号認証を行っている方法をみかけました。なるほど...(こういうのを知見というのだろう)
これだと「安い」ってメリットはあると思うのですが、
- コモンなインターフェースでないのでユーザが戸惑うかも
- 「ユーザからSMSを送信してもらう」ことはハードルが少し上がる
というデメリットがあるなと思いました。
3. アプリケーション側から6桁の番号を送信、ユーザはそれをブラウザから入力
ということでお金を払えるなら、3番の方法がやはり一番いいと思いました。
実装手順
1. Twillio準備
Twilioに登録して、USの電話番号でもなんでもいいので、SMSを使える番号を取得します。
USの番号だとSMSを使えるみたいなので、取得。
TwillioのREST APIは以下から参照できます
Twilio Docs: REST API Reference
APIのバージョニング日付でやってるのか。
SMS送信は以下。
Twilio Docs: Send text messages with Twilio's REST API
$ 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}
AccountSid, AuthTokeは以下のURLで確認できます。
https://jp.twilio.com/user/account/messaging/dashboard
Rubyでの使い方、twillio-rubyの使い方は以下を参照。
Twilio を使って Rails から ショートメール(SMS) を送信する - Shred IT!!!!
こんな感じ。
届いた!なんか感動嬉しい。
2. 実装
以下を参考にさせていただきながら。
ログイン機能にdeviseを導入して しまった いるので、若干つらいことになる。自分は以下のような仕様で実装しました。
- 電話番号での登録時にはfakeのemailを登録する
- 登録の流れは以下
- formのデータをPOST, 認証コード入力画面に遷移
- 認証コードを生成・ユーザのデータと一緒に保存
- SMSで認証コードを送信
- 認証コードが入力、POSTされたら正しいことを確認してuserのデータをverifiedにする
- 「電話番号またはemailでログイン」できるようにする
- 認証コードをfillしてPOSTしたら自動でログインする
- UserにSMSログイン用のカラムを追加
sms_confirmed: boolean
... SMSの認証がされたかどうかsms_token: string
... 認証コード自体の保存場所
基本的にUsers::RegistrationsController
とUsers::SessionsController
にアクションを追加して実装した。
3. テスト
各モデルのメソッドは単体テストを書けるとしても、feature specをどうするか悩みました。
ここの記事を参考にして書きました。
日本語訳: SMSインタラクションのテスト | プログラミング | POSTD
具体的には、以下のようなFakeSmsというクラスをつくり、これをTwilioのクラスに定数としてスタビングする。
# 例えば spec/support/fake_sms.rb class FakeSms Message = Struct.new(:from, :to, :body) cattr_accessor :messages self.messages = [] def initialize(_account_sid, _auth_token) end def account self end def messages self end def create(from:, to:, body:) self.class.messages << Message.new(from: from, to: to, body: body) end end
(こうやってハッシュ受け取れるんや)
そしてスタブ。
# spec/spec_helper.rb RSpec.configure. do |config| config.before(:each) do stub_const("Twilio::REST::Client", FakeSms) end end
大したことじゃないですがクラス名FakeSMS
かFakeSms
か悩む。キャメルケースがFakeSMS
ならスネークケースだとfake_s_m_s
じゃんとか思うとつらい。SMSとして1単語とした方がいいなーとか。
おわり
ということで、自前で電話番号ログイン機能を実装してみました。
機能的には複雑ではないですがテストなど若干つまずいたとこもありました。
実際のコードは省略しちゃったので、何かご質問等あれば気軽に @totzyuta にでも投げてやってくださいー