Firebaseの電話番号認証をNext.jsで実装する方法

2024/01/29に公開1

はじめに

センキャクでは、SMSによる電話番号認証を使って、ユーザー認証を行なっています。認証基盤としてFirebaseを採用しています。今回は、電話番号認証を実装した際にどのように処理を行なったか、まとめてみました。

目次

  • Firebaseの設定
  • reCAPTCHA
  • 電話番号認証を行う
  • 確認コードの入力
  • 再送処理

Firebaseの設定

Firebase Authentication

電話番号認証を追加するには、Firebase AuthenticationのSign-in methodに電話番号を追加します。この際、電話番号認証を行う場合に、毎回実際の電話番号を使用すると、認証の制限に引っかかり、テストできなくなります。あらかじめ、テスト用の番号を設定しておくと便利です。また、運用する際には、サイトのドメインを承認済みドメインに追加するのを忘れないようにしてください。

Firebaseの設定の環境変数に追加する

Webで利用するためのFirebaseの設定を、envに設定するようにします。センキャクでは、Next.jsを採用していて、next.config.jsに環境ごとのFirebaseの設定を読み込めるようにしています。
ここの処理は、こちらの記事を参考にして、環境ごとに切り替えられるようにしています。

環境変数の定義

Firebaseのプロジェクトの設定よりWebアプリの設定をコピーして、環境変数に定義します。env配下に保存すれば、Next.jsの起動時に読み込まれて利用できるようになります。

module.exports = {
  FIREBASE_API_KEY: "APY_KEY",
  FIREBASE_AUTH_DOMAIN: "senkyaku.firebaseapp.com",
  FIREBASE_PROJECT_ID: "senkyaku",
  FIREBASE_STORAGE_BUCKET: "senkyaku.appspot.com",
  FIREBASE_MESSAGING_SENDER_ID: "SENDER_ID",
  FIREBASE_APP_ID: "APP_ID",
  FIREBASE_MEASUREMENT_ID: "G-MEASUREMENT_ID",
};

Firebaseの初期化

Firebaseの設定を呼び出して定義する部分は、ライブラリとして定義し、Firebaseの処理が必要な部分で参照できるようにします。環境変数は、NEXT_PUBLIC_*とつくようになっているので、定義した変数のまま使用しないように注意してください。

import { FirebaseOptions, initializeApp } from "firebase/app";

const firebaseConfig: FirebaseOptions = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  authDomain: process.env.FNEXT_PUBLIC_IREBASE_AUTH_DOMAIN,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
};

export const firebaseApp = initializeApp(firebaseConfig);

利用方法

先ほど定義したライブラリを読み込んで、getAuthでAuthオブジェクトを取得して利用して、認証処理を実装します。

import { getAuth } from "firebase/auth";
import { firebaseApp } from "../lib/firebaseApp";

const firebaseAuth = getAuth(firebaseApp);

reCAPTCHA

電話番号認証を行うには、reCAPTCHAの認証が必要になります。reCAPTCHAの呼び出し方法は、表示型・非表示型の2種類がありますが、今回は、非表示で埋め込んで利用します。

引数に指定しているrecaptcha-containerは表示するページ内に空のdiv要素を定義して、そのidを指定します。認証時には、reCAPTCHAのアイコンが要素内に表示されます。

const applicationVerifier = new RecaptchaVerifier("recaptcha-container", { size: "invisible" }, firebaseAuth);

電話番号認証を行う

定義したRecaptchaVerifierを使って、電話番号認証を行います。Firebaseでは、新規・既存の扱いはなく、同じSignInメソッドを利用します。Firebase側にユーザーがいなければ自動生成され、いればログイン扱いになります。センキャクでは、認証した後に、センキャクサーバ側に実際にユーザーがいるのか確認して、新規か既存の判断を行なっています。

電話番号の正規化

Firebaseで電話番号を行う場合に、090XXXXXXXXでは認証できません。国際表記に合わせて書き換える必要になります。google-libphonenumberのライブラリが公開されており、これを使うと簡単に電話番号を変換できます。

const phoneNumber = "090XXXXXXXX";

import { PhoneNumberFormat, PhoneNumberUtil } from "google-libphonenumber";
const phoneUtil = PhoneNumberUtil.getInstance();
const number = phoneUtil.parse(phoneNumber, "JP");

const verifiedNumber = phoneUtil.format(number, format).split(/[-\s]/g).join("");

変換された電話番号を使って、signInWithPhoneNumberを呼び出します。setPersistenceで囲っていますが、これは認証状態を永続化するために追加しています。

const result = await setPersistence(firebaseAuth, browserLocalPersistence).then(() => {
  return signInWithPhoneNumber(firebaseAuth, verifiedNumber, applicationVerifier);
});

確認コードの入力

signInWithPhoneNumberを呼び出すと、指定した電話番号にSMSが送られます。メソッドの戻り値で確認コードを入力するためのConfirmationResultが返されるので、確認コードを受け取るように、画面を電話番号の入力状態から、コードの入力画面に切り替えます。

受け取った認証コードが正しければ、ユーザーの認証がおこわれて、認証情報が返されます。間違っていた場合には、例外が発生するので、catchで受け取って、適切なメッセージを表示してください。

const verificationCode = "フォームから入力されたコード";

const credential = await confirmationResult.confirm(verificationCode).catch((error) => {
  // 認証エラー
  return undefined;
});
if (!credential) return;

再送処理

電話番号を間違えた場合、SMSが送れなかった場合など、もう一度SMSを送信したい場合があります。この場合は、reCAPTCHAをDiv要素をリセットした上で、同じ処理時実行すると再送できます。

const recaptchaRef = useRef<HTMLDivElement>(null);

const resetRecaptca = useCallback(() => {
  if (recaptchaRef.current) {
    recaptchaRef.current.innerHTML = '<div id="recaptcha-container" />';
  }
}, []);

return <div ref={recaptchaRef}>
  <div id="recaptcha-container" />
</div>;

おわりに

  • 今回はNext.jsとFirebaseを用いた電話番号認証についてまとめてみました。みなさんもぜひ試してみてください。
  • センキャクでは一緒にプロダクト開発をしてくれる仲間を絶賛募集しています。少しでもご興味ある方はこちらから。
センキャク Tech Blog

Discussion

yuiyui

Next.js App Router でFirebaseAuthを使って電話番号認証をした際に、idTokenの検証などはどのようなアプローチでされているのかもし分かれば教えていただきたいです🙇‍♀️