概要
認証にFirebase authを使っているWebサービスで、アカウントに対して電話番号認証をさせ認証情報をリンクさせる仕組みを導入します。
動機
Webサービスにおいて一人にたくさんのアカウントを作られることを防ぎたいというのは割とよくあるケースだと思います。
このときに使える方法のひとつが電話番号(SMS)認証です。SMSで4桁とか6桁の数字を送ってユーザーに入力させるアレです。
メールアドレスとちがい電話番号は無料でいくらでも作るということはできないので、アカウントの開通に電話番号の確認を要求するようにすることは有効な手段です。
ちなみにFirebase authでは電話番号をログイン情報に使うこともできますが、今回はログインはあくまでGoogleやGithubなどOAuthを使い、電話番号認証は既存のアカウントに紐付ける方法についてです。
リポジトリ
今回作成したデモアプリはこのリポジトリにあります。
さくっと動かすためにNuxt.jsを使っています。
流れ
Firebase authで既存アカウントに対して電話番号認証を行う際の流れは次のようになります。
- ユーザーに電話番号を紐付ける対象のアカウントでログインしてもらう
- ユーザーに電話番号を入力させる
- 入力された電話番号に確認コードを送信する
- 2の時点で払い出されるトークンと、SMSを受け取ったユーザーが入力する確認コードを使って認証を行う
- 得られた認証情報をアカウントとリンクさせる
実装
1については詳細を省略します。
2. ユーザーに電話番号を入力させる & 3. 入力された電話番号に確認コードを送信する
これは意外に思ったのですが、電話番号認証はポップアップなりリダイレクトなりでGoogleの用意したUIを使うのではありませんでした。電話番号や確認コードの入力フォームは自分で用意する必要があります。
ユーザーに確認コードを送信すると、その確認コード送信に対応するトークンが払い出されます。
確認コードを入力してもらったときに、このトークンもあわせて必要になりますので、どこかに保存しておく必要があります。
サンプルコードでは、Vue.jsコンポーネントのdataに格納しています。
(confirmationResultに入っている)
async sendSmsVerification() {
try {
const confirmationResult = await firebase.auth().signInWithPhoneNumber(this.phoneNumber, this.recaptchaVerifier)
this.confirmationResult = confirmationResult
this.waitingVerify = true
} catch(error) {
console.error(error)
}
},
4. 2の時点で払い出されるトークンと、SMSを受け取ったユーザーが入力する確認コードを使って認証を行う & 5. 得られた認証情報をアカウントとリンクさせる
さきほどの電話番号のとき同様にユーザーに確認コードを入力してもらうフォームも自分で作成する必要があります。
そのフォームに確認コードを入れてもらったら、さきほど保存したトークンと一緒にFirebaseに送信すると、電話番号の認証情報が得られます。
この認証情報をFirebaseのユーザーオブジェクトに生えているlinkAndRetrieveDataWithCredential
メソッドに渡すと、既存のアカウント情報に電話番号認証情報がリンクされます。
async confirmVerification() {
const credential = firebase.auth.PhoneAuthProvider.credential(this.confirmationResult.verificationId, this.verificationCode)
const userCred = await this.user.linkAndRetrieveDataWithCredential(credential)
this.auth.phoneNumber = userCred.user.phoneNumber
}
電話番号認証が終わると、Firebase authのコンソールが次のような表示になっているはずです。
Googleアカウントと電話番号が紐付けられました。