JavaScript を使用して電話番号で Firebase 認証を行う

Firebase Authentication を使用してユーザーのスマートフォンに SMS メッセージを送信することで、ユーザーはログインすることができます。ユーザーは SMS メッセージに記載されたワンタイム コードを使用してログインします。

電話番号ログインをアプリに追加する最も簡単な方法は、FirebaseUI を使用することです。このライブラリには、電話番号ログインのほか、パスワードに基づくログインやフェデレーション ログインのログインフローを実装するドロップイン式のログイン ウィジェットが含まれています。このドキュメントでは、Firebase SDK を使用して電話番号ログインフローを実装する方法について説明します。

始める前に

初期化スニペットを Firebase コンソールからプロジェクトにコピーしていない場合は、Firebase を JavaScript プロジェクトに追加するの説明に沿って初期化スニペットをコピーします。

セキュリティに関する懸念

電話番号の所有権はユーザー間で簡単に移転できるため、電話番号のみを使用する認証は便利である反面、セキュリティ面では他の認証方法より劣ります。また、複数のユーザー プロファイルを持つデバイスでは、SMS メッセージを受信できるすべてのユーザーが、デバイスの電話番号を使用してアカウントにログインできます。

アプリで電話番号ベースのログインを使用する場合は、よりセキュリティの高いログイン方法も同時に提供し、電話番号ログインを使用した場合のセキュリティ面での懸念をユーザーに通知する必要があります。

Firebase プロジェクトで電話番号ログインを有効にする

ユーザーが SMS を介してログインできるようにするには、まず Firebase プロジェクトで電話番号ログイン方法を有効にする必要があります。

  1. Firebase コンソールで [Authentication] セクションを開きます。
  2. [Sign-in method] ページで、[電話番号] のログイン方法を有効にします。
  3. 同じページで、アプリをホストするドメインが [OAuth リダイレクト ドメイン] セクションのリストにない場合は、ドメインを追加します。 電話認証を目的としたホストドメインとして localhost は使用できません。

reCAPTCHA ベリファイアを設定する

電話番号を使ってユーザーをログインさせる前に、Firebase の reCAPTCHA ベリファイアを設定する必要があります。Firebase は不正行為を防ぐ手段として reCAPTCHA を使用します。これにより、たとえば電話番号の確認リクエストがアプリで許可されたドメインから発信されたものかどうかを確認できます。

reCAPTCHA クライアントを手動で設定する必要はありません。Firebase SDK の RecaptchaVerifier オブジェクトを使用すると、Firebase によって必要なクライアント鍵とシークレットが自動的に作成され、処理されます。

RecaptchaVerifier オブジェクトは invisible reCAPTCHA をサポートしています。多くの場合、これを使用すると、ユーザーに操作を要求せずにユーザーを確認できます。また、reCAPTCHA ウィジェットもサポートされており、この場合、ログインを完了するために常にユーザーによる操作が必要になります。

reCAPTCHA をレンダリングする前に Auth インスタンスの言語コードを更新することによって、元のテキスト画像の reCAPTCHA をユーザーの設定に合わせてローカライズできます。前述のローカリゼーションは、ユーザーに送信される SMS メッセージ(確認コードを含む)にも適用されます。

Web

import { getAuth } from "firebase/auth";

const auth = getAuth();
auth.languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// auth.useDeviceLanguage();

Web

firebase.auth().languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// firebase.auth().useDeviceLanguage();

invisible reCAPTCHA を使用する

invisible reCAPTCHA を使用するには、size パラメータを invisible に設定し、ログイン フォームを送信するボタンの ID を指定して RecaptchaVerifier オブジェクトを作成します。次に例を示します。

Web

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

reCAPTCHA ウィジェットを使用する

可視の reCAPTCHA ウィジェットを使用するには、ウィジェットを含む要素をページに作成してから、そのコンテナの ID を指定して RecaptchaVerifier オブジェクトを作成します。

Web

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');

省略可: reCAPTCHA パラメータを指定する

必要に応じて、ユーザーが reCAPTCHA を解決したとき、またはユーザーがフォームを送信する前に reCAPTCHA が期限切れになったときに呼び出されるコールバック関数を RecaptchaVerifier オブジェクトに設定できます。

Web

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

省略可: reCAPTCHA を事前レンダリングする

ログイン リクエストを送信する前に reCAPTCHA を事前レンダリングする場合、render を呼び出します。

Web

recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

Web

recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

render が解決された後、reCAPTCHA のウィジェット ID を取得します。これを使用して reCAPTCHA API を呼び出すことができます。

Web

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

Web

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

ユーザーの電話に確認コードを送信する

電話番号ログインを開始するには、ユーザーに電話番号の入力を求めるインターフェースを表示した後、signInWithPhoneNumber を呼び出して、ユーザーのスマートフォンに認証コードを SMS で送信するよう Firebase にリクエストします。

  1. ユーザーの電話番号を取得します。

    法的要件はさまざまに異なりますが、電話番号ログインを使用する場合は確認用の SMS メッセージが送られる旨、それには標準料金がかかる旨をユーザーにあらかじめ知らせることをおすすめします。

  2. signInWithPhoneNumber を呼び出し、ユーザーの電話番号と、前の手順で作成した RecaptchaVerifier を渡します。

    Web

    import { getAuth, signInWithPhoneNumber } from "firebase/auth";
    
    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    
    const auth = getAuth();
    signInWithPhoneNumber(auth, phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });

    Web

    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });
    signInWithPhoneNumber の結果がエラーであった場合は、ユーザーが再試行できるように reCAPTCHA をリセットします。
    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    window.recaptchaVerifier.render().then(function(widgetId) {
      grecaptcha.reset(widgetId);
    });

signInWithPhoneNumber メソッドは reCAPTCHA 画像をユーザーに発行します。ユーザーがその画像認証に正しく回答すると、Firebase Authentication によって確認コードを含む SMS メッセージがユーザーのスマートフォンに送信されます。

確認コードを使ってユーザーをログインさせる

signInWithPhoneNumber への呼び出しが成功したら、SMS メッセージで受け取った確認コードを入力するようユーザーに要求します。次に、signInWithPhoneNumber のフルフィルメント ハンドラ(つまり、その then ブロック)に渡された ConfirmationResult オブジェクトの confirm メソッドに確認コードを渡してユーザーをログインさせます。次に例を示します。

Web

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

Web

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

confirm の呼び出しが成功すると、ユーザーは正常にログインされます。

中間の AuthCredential オブジェクトを取得する

ユーザーのアカウントの AuthCredential オブジェクトを取得する必要がある場合は、confirm を呼び出す代わりに、確認コードと、確認の結果から得た確認コードを PhoneAuthProvider.credential に渡します。

var credential = firebase.auth.PhoneAuthProvider.credential(confirmationResult.verificationId, code);

この認証情報を使ってユーザーをログインさせることができます。

firebase.auth().signInWithCredential(credential);

架空の電話番号でテストする

Firebase コンソールを使用して、開発用の架空の電話番号を設定できます。架空の電話番号でテストすると、次のメリットがあります。

  • 使用量の割り当てを消費することなく電話番号認証をテストできます。
  • 実際の SMS メッセージを送信することなく電話番号認証をテストできます。
  • 同じ電話番号で連続してテストを実施してもスロットルが発生しません。このため、レビュー担当者が偶発的にテストに同じ電話番号を使用しても、アプリストアのレビュー プロセス中に拒否されるリスクが最小限に抑えられます。
  • 追加の作業を必要とせずに開発環境で簡単にテストできます。たとえば、Google Play 開発者サービスを使用せずに iOS シミュレータや Android Emulator で開発できます。
  • セキュリティ チェックによるブロックが生じない統合テストを作成できます。通常、本番環境の実際の電話番号にはセキュリティ チェックが適用されます。

架空の電話番号は、次の要件を満たしている必要があります。

  1. 間違いなく架空で、存在していない電話番号を使用します。Firebase Authentication では、実際のユーザーが使用している既存の電話番号をテスト番号として設定することはできません。1 つの選択肢としては、+1 650-555-3434 のように、555 プレフィックス付きの番号を米国のテスト電話番号として使用します。
  2. 長さなどの制約に関して、正しい形式の電話番号を使用する必要があります。番号には、実際のユーザーの電話番号と同じ検証が行われます。
  3. 開発用に最大 10 個の電話番号を追加できます。
  4. 推測が困難なテスト用の電話番号やコードを使用し、頻繁に変更してください。

架空の電話番号と確認コードを作成する

  1. Firebase コンソールで、[Authentication] セクションを開きます。
  2. [Sign-in method] タブで、[電話番号] をまだ有効にしていない場合は有効にします。
  3. [テスト用の電話番号] アコーディオン メニューを開きます。
  4. テストする電話番号を入力します(例: +1 650-555-3434)。
  5. 特定の番号用の 6 桁の確認コードを入力します(例: 654321)。
  6. 番号を追加します。必要に応じて、電話番号の対応する行にカーソルを合わせてゴミ箱アイコンをクリックすると、電話番号とそのコードを削除できます。

手動テスト

架空の電話番号をアプリケーションで直接使用できます。これにより、割り当ての問題やスロットルを発生させることなく、開発段階で手動テストを実施できます。また、Google Play 開発者サービスをインストールすることなく、iOS シミュレータや Android Emulator から直接テストすることもできます。

架空の電話番号を指定して確認コードを送信しても、実際の SMS は送信されません。代わりに、以前に構成した確認コードを入力してログインを完了する必要があります。

ログインが完了すると、その電話番号を使用して Firebase ユーザーが作成されます。そのユーザーの動作やプロパティは実際の電話番号のユーザーと同じであり、また同じ方法で Realtime DatabaseCloud Firestore などのサービスにアクセスできます。このプロセス中に作成された ID トークンには、実際の電話番号のユーザーと同じ署名が含まれます。

別の選択肢としては、アクセス制限をさらに厳格にする場合、これらのユーザーにカスタム クレームを介してテスト役割を設定して、架空のユーザーとして区別します。

統合テスト

Firebase Authentication には、手動テスト以外にも、スマートフォン認証テスト用の統合テストの作成に役立つ API が用意されています。これらの API は、ウェブの reCAPTCHA の要件や iOS のサイレント プッシュ通知を無効にすることで、アプリの確認を無効にします。これにより、これらのフローで自動テストを実施でき、実装が容易になります。また、Android で即時確認のフローをテストするための機能も備わっています。

ウェブでは、firebase.auth.RecaptchaVerifier をレンダリングする前に appVerificationDisabledForTestingtrue に設定します。これにより reCAPTCHA が自動的に解決されるため、手動で解決せずに電話番号を渡すことができます。reCAPTCHA が無効になっていても、架空ではない電話番号を使用するとログインが完了しません。この API では、架空の電話番号のみ使用できます。

// Turn off phone auth app verification.
firebase.auth().settings.appVerificationDisabledForTesting = true;

var phoneNumber = "+16505554567";
var testVerificationCode = "123456";

// This will render a fake reCAPTCHA as appVerificationDisabledForTesting is true.
// This will resolve after rendering without app verification.
var appVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
// signInWithPhoneNumber will call appVerifier.verify() which will resolve with a fake
// reCAPTCHA response.
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
    .then(function (confirmationResult) {
      // confirmationResult can resolve with the fictional testVerificationCode above.
      return confirmationResult.confirm(testVerificationCode)
    }).catch(function (error) {
      // Error; SMS not sent
      // ...
    });

アプリの確認が無効になっている場合、可視および不可視の模擬的な reCAPTCHA アプリ ベリファイアの動作は異なります。

  • 可視の reCAPTCHA: 可視の reCAPTCHA が appVerifier.render() によってレンダリングされると、一瞬遅れて自動的に解決されます。これは、ユーザーがレンダリング時にすぐに reCAPTCHA をクリックするのと同じです。reCAPTCHA 応答はしばらくしてから有効期限が切れ、再度自動解決されます。
  • invisible reCAPTCHA: invisible reCAPTCHA は、レンダリング時に自動解決されず、appVerifier.verify() が呼び出されたとき、または reCAPTCHA のボタンアンカーがクリックされたときに一瞬遅れて自動解決されます。同様に、応答はしばらくしてから有効期限が切れ、appVerifier.verify() 呼び出しの後、または reCAPTCHA のボタンアンカーを再度クリックしたときにのみ自動解決されます。

模擬的な reCAPTCHA が解決されると、偽の応答で、対応するコールバック関数が期待通りに起動されます。有効期限のコールバックも指定されている場合は、有効期限が切れるとトリガーされます。

次のステップ

ユーザーが初めてログインすると、新しいユーザー アカウントが作成され、ユーザーがログイン時に使用した認証情報(ユーザー名とパスワード、電話番号、または認証プロバイダ情報)にアカウントがリンクされます。この新しいアカウントは Firebase プロジェクトの一部として保存され、ユーザーのログイン方法にかかわらず、プロジェクトのすべてのアプリでユーザーを識別するために使用できます。

  • アプリでユーザーの認証ステータスを把握するには、Auth オブジェクトにオブザーバーを設定することをおすすめします。これによって、ユーザーの基本的なプロフィール情報を User オブジェクトから取得できます。ユーザーを管理するをご覧ください。

  • Firebase Realtime DatabaseCloud Storageセキュリティ ルールでは、ログイン済みユーザーの一意のユーザー ID を auth 変数から取得し、それを使用して、ユーザーがアクセスできるデータを制御できます。

既存のユーザー アカウントに認証プロバイダの認証情報をリンクすることで、ユーザーは複数の認証プロバイダを使用してアプリにログインできるようになります。

ユーザーのログアウトを行うには、signOut を呼び出します。

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});