電話番号と C++ を使用して Firebase 認証を行う

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

このドキュメントでは、Firebase SDK を使用して電話番号ログインのフローを実装する方法について説明します。

始める前に

  1. Firebase を C++ プロジェクトに追加します
  2. アプリを Firebase プロジェクトに接続していない場合は、Firebase コンソールで接続します。
  3. 次に示す、電話番号ログインのプラットフォーム要件を把握します。
    • 電話番号ログインはモバイル プラットフォームでのみ使用できる。
    • iOS で電話番号ログインを行うには実機が必要で、シミュレータでは機能しない。

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

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

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

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

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

  1. Firebase コンソールで [Authentication] セクションを開きます。
  2. [Sign-in Method] ページで、[電話番号] のログイン方法を有効にします。

APNs 通知の受信を開始する(Apple プラットフォーム)

Apple プラットフォームで電話認証を使用するには、アプリが Firebase から APNs 通知を受信できる必要があります。ユーザーがデバイスで初めて電話番号を使用してログインすると、Firebase Authentication はデバイスにサイレント プッシュ通知を送信し、電話番号のログイン リクエストがアプリから来ていることを確認します(このため、電話番号のログインはシミュレータでは使用できません)。

Firebase Authentication で APN 通知を有効にするには:

  1. Xcode で、プロジェクトのプッシュ通知を有効にします
  2. APNs 証明書を Firebase にアップロードします。まだ APNs 証明書を用意していない場合は、Apple Developer Member Center で作成してください。

    1. Firebase コンソールのプロジェクト内で歯車アイコンを選択し、[プロジェクトの設定]、[Cloud Messaging] タブの順に選択します。

    2. 開発用証明書、本番用証明書、またはその両方の [証明書をアップロード] ボタンを選択します。少なくとも 1 つ選択する必要があります。

    3. 証明書ごとに .p12 ファイルを選択し、必要に応じてパスワードを入力します。この証明書のバンドル ID はアプリのバンドル ID と一致させてください。[保存] を選択します。

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

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

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

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

  2. PhoneAuthProvider::VerifyPhoneNumber を呼び出し、ユーザーの電話番号を渡します。
    class PhoneListener : public PhoneAuthProvider::Listener {
     public:
      ~PhoneListener() override {}
    
      void OnVerificationCompleted(PhoneAuthCredential credential) override {
        // Auto-sms-retrieval or instant validation has succeeded (Android only).
        // No need for the user to input the verification code manually.
        // `credential` can be used instead of calling GetCredential().
      }
    
      void OnVerificationFailed(const std::string& error) override {
        // Verification code not sent.
      }
    
      void OnCodeSent(const std::string& verification_id,
                      const PhoneAuthProvider::ForceResendingToken&
                          force_resending_token) override {
        // Verification code successfully sent via SMS.
        // Show the Screen to enter the Code.
        // Developer may want to save that verification_id along with other app states in case
        // the app is terminated before the user gets the SMS verification code.
      }
    };
    
    PhoneListener phone_listener;
    PhoneAuhtOptions options;
    options.timeout_milliseconds = kAutoVerifyTimeOut;
    options.phone_number = phone_number;
    PhoneAuthProvider& phone_provider = PhoneAuthProvider::GetInstance(auth);
    phone_provider->VerifyPhoneNumber(options, &phone_listener);
    PhoneAuthProvider::VerifyPhoneNumber を呼び出すと、Firebase によって以下が行われます。
    • (iOS の場合)アプリにサイレント プッシュ通知を送信します。
    • 指定された電話番号に認証コードを含む SMS メッセージを送信し、確認 ID を補完関数に渡します。ユーザーをログインさせるには、確認コードと確認 ID の両方が必要です。
  3. 確認 ID を保存し、アプリの読み込み時に復元します。これにより、ユーザーがログインフローを完了する前にアプリが終了した場合でも(たとえば SMS アプリへの切り替え時など)、有効な確認 ID を残すことができます。

    確認 ID は任意の方法で保持できます。クロスプラットフォーム C++ フレームワークを使用している場合は、アプリの終了と復元の通知を提供する必要があります。これらのイベントで、確認 ID の保存と復元をそれぞれ行うことができます。

VerifyPhoneNumber を呼び出したことによってリスナーで OnCodeSent が呼び出された場合、ユーザーが SMS メッセージを受信したら確認コードを入力するように要求できます。

また、VerifyPhoneNumber を呼び出したことによって OnVerificationCompleted が返された場合は自動検証が成功しており、PhoneAuthCredential を取得します(使用方法は後で説明します)。

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

ユーザーが SMS メッセージで受信した確認コードをアプリに入力したら、確認コードと確認 ID から PhoneAuthCredential オブジェクトを作成し、そのオブジェクトを Auth::SignInWithCredential に渡して、ユーザーをログインさせます。

  1. ユーザーから確認コードを取得します。
  2. 確認コードと確認 ID から Credential オブジェクトを作成します。
    PhoneAuthCredential credential = phone_auth_provider->GetCredential(
        verification_id_.c_str(), verification_code.c_str());
        
  3. Credential オブジェクトを使用して、ユーザーをログインさせます。
    Future<User> future = auth_->SignInWithCredential(credential);
    future.OnCompletion(
        [](const Future<User*>& result, void*) {
          if (result.error() == kAuthErrorNone) {
            // Successful.
            // User is signed in.
            User user = *result.result();
    
            // This should display the phone number.
            printf("Phone number: %s", user.phone_number().c_str());
    
            // The phone number provider UID is the phone number itself.
            printf("Phone provider uid: %s", user.uid().c_str());
    
            // The phone number providerID is 'phone'
            printf("Phone provider ID: %s", user.provider_id().c_str());
          } else {
            // Error.
            printf("Sign in error: %s", result.error_message().c_str());
          }
        },
        nullptr);

次のステップ

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

  • アプリでは、firebase::auth::User オブジェクトからユーザーの基本的なプロフィール情報を取得できます。

    firebase::auth::User user = auth->current_user();
    if (user.is_valid()) {
      std::string name = user.display_name();
      std::string email = user.email();
      std::string photo_url = user.photo_url();
      // The user's ID, unique to the Firebase project.
      // Do NOT use this value to authenticate with your backend server,
      // if you have one. Use firebase::auth::User::Token() instead.
      std::string uid = user.uid();
    }
  • Firebase Realtime DatabaseCloud Storageセキュリティ ルールでは、ログイン済みユーザーの一意のユーザー ID を auth 変数から取得し、それを使用して、ユーザーがアクセスできるデータを制御できます。

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

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

auth->SignOut();