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

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

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

準備

  1. Firebase を Android プロジェクトに追加します
  2. Firebase Authentication の依存関係をアプリレベルの build.gradle ファイルに追加します。
    implementation 'com.google.firebase:firebase-auth:16.0.3'
  3. アプリを Firebase プロジェクトに接続していない場合は、Firebase コンソールで接続します。
  4. アプリの SHA-1 ハッシュが Firebase コンソールで設定されていない場合は、設定します。アプリの SHA-1 ハッシュの検索方法について詳しくは、クライアントの認証をご覧ください。

また、電話番号ログインを行うには物理デバイスが必要です。エミュレータでは機能しません。

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

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

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

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

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

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

Firebase の電話番号ログイン リクエストの割り当ては多めになっており、ほとんどのアプリはこれで十分です。ただし、大量のユーザーを電話認証でログインさせる場合は、料金プランをアップグレードしなければならないことがあります。料金ページをご覧ください。

ユーザーのスマートフォンに確認コードを送信する

電話番号ログインを開始するには、電話番号の入力を求めるインターフェースをユーザーに表示します。法的要件はさまざまに異なりますが、電話番号ログインを使用する場合は確認用の SMS メッセージが送られること、それには標準料金がかかることをユーザーに知らせることをおすすめします。そうすることで、ユーザーもそのように心構えできます。

次に、電話番号を PhoneAuthProvider.verifyPhoneNumber メソッドに渡して、ユーザーの電話番号を確認するよう Firebase にリクエストします。次に例を示します。

PhoneAuthProvider.getInstance().verifyPhoneNumber(
        phoneNumber,        // Phone number to verify
        60,                 // Timeout duration
        TimeUnit.SECONDS,   // Unit of timeout
        this,               // Activity (for callback binding)
        mCallbacks);        // OnVerificationStateChangedCallbacks

verifyPhoneNumber メソッドはリエントラントです。たとえば、アクティビティの onStart メソッドなどで複数回呼び出された場合でも、元のリクエストがタイムアウトしていなければ、verifyPhoneNumber メソッドから 2 番目の SMS は送信されません。

この動作を利用して、ユーザーがログインする前にアプリが終了した場合でも(たとえばユーザーが SMS のアプリを使用している間など)、電話番号のログイン プロセスを再開できます。verifyPhoneNumber を呼び出した後で、確認中であることを示すフラグを設定します。次に、アクティビティの onSaveInstanceState メソッドでフラグを保存したら、フラグを onRestoreInstanceState で復元します。最後に、アクティビティの onStart メソッドで、確認がすでに実行中であるかどうかをチェックし、実行中であれば verifyPhoneNumber を再度呼び出します。確認が完了または失敗した場合は、必ずフラグをクリアしてください(詳しくは確認のコールバックをご覧ください)。

画面の回転など、アクティビティを再起動する操作を簡単に処理できるよう、アクティビティを verifyPhoneNumber メソッドに渡します。アクティビティが停止するとコールバックは自動的に接続解除されるので、UI 遷移コードをコールバック メソッドに自由に書き込むことができます。

Firebase によって送信された SMS メッセージは、Auth インスタンスの setLanguageCode メソッドを使用して認証言語を指定することによってローカライズすることもできます。

auth.setLanguageCode("fr");
// To apply the default app language instead of explicitly setting it.
// auth.useAppLanguage();

PhoneAuthProvider.verifyPhoneNumber を呼び出すときは、リクエストの結果を処理するコールバック関数の実装を含む OnVerificationStateChangedCallbacks のインスタンスも指定する必要があります。以下に例を示します。

mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

    @Override
    public void onVerificationCompleted(PhoneAuthCredential credential) {
        // This callback will be invoked in two situations:
        // 1 - Instant verification. In some cases the phone number can be instantly
        //     verified without needing to send or enter a verification code.
        // 2 - Auto-retrieval. On some devices Google Play services can automatically
        //     detect the incoming verification SMS and perform verification without
        //     user action.
        Log.d(TAG, "onVerificationCompleted:" + credential);

        signInWithPhoneAuthCredential(credential);
    }

    @Override
    public void onVerificationFailed(FirebaseException e) {
        // This callback is invoked in an invalid request for verification is made,
        // for instance if the the phone number format is not valid.
        Log.w(TAG, "onVerificationFailed", e);

        if (e instanceof FirebaseAuthInvalidCredentialsException) {
            // Invalid request
            // ...
        } else if (e instanceof FirebaseTooManyRequestsException) {
            // The SMS quota for the project has been exceeded
            // ...
        }

        // Show a message and update the UI
        // ...
    }

    @Override
    public void onCodeSent(String verificationId,
                           PhoneAuthProvider.ForceResendingToken token) {
        // The SMS verification code has been sent to the provided phone number, we
        // now need to ask the user to enter the code and then construct a credential
        // by combining the code with a verification ID.
        Log.d(TAG, "onCodeSent:" + verificationId);

        // Save verification ID and resending token so we can use them later
        mVerificationId = verificationId;
        mResendToken = token;

        // ...
    }
};

確認のコールバック

ほとんどのアプリにおいて、onVerificationCompletedonVerificationFailed、および onCodeSent のコールバックを実装することになります。アプリの要件によっては、onCodeAutoRetrievalTimeOut を実装する場合もあります。

onVerificationCompleted(PhoneAuthCredential)

このメソッドは、次の 2 つの状況で呼び出されます。

  • 即時確認: 確認コードを送信または入力することなく電話番号を即座に確認することができる場合があります。
  • 自動取得: 一部の端末では、受信した確認 SMS を Google Play 開発者サービスが自動的に検出して確認を実行することができます。この場合、ユーザーの操作は不要です(この機能は、一部の携帯通信会社では使用できない場合があります)。
いずれの場合も、ユーザーの電話番号が正常に確認されたら、コールバックに渡された PhoneAuthCredential オブジェクトを使用してユーザーのログインを行うことができます。

onVerificationFailed(FirebaseException)

このメソッドは、無効な確認リクエスト(無効な電話番号や確認コードが指定されたリクエストなど)に対するレスポンスとして呼び出されます。

onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken)

省略可。このメソッドは、確認コードが指定の電話番号に SMS で送信された後で呼び出されます。

このメソッドが呼び出されると、ほとんどのアプリでは、SMS メッセージに示されている確認コードの入力をユーザーに要求する UI が表示されます(これと同時に、バックグラウンドで自動確認が処理されている場合があります)。次に、ユーザーが確認コードを入力したら、この確認コードとメソッドに渡された確認 ID を使用して PhoneAuthCredential オブジェクトを作成し、ユーザーのログインを行うことができます。ただし、アプリによっては、onCodeAutoRetrievalTimeOut が呼び出されるのを待ってから確認コードの UI を表示する場合がありますが、これは推奨されません。

onCodeAutoRetrievalTimeOut(String verificationId)

省略可。このメソッドは、onVerificationCompleted がトリガーされずに、verifyPhoneNumber に指定されているタイムアウト時間が経過した場合に呼び出されます。SIM カードが挿入されていない端末では、SMS の自動取得を実行できないため、このメソッドが直ちに呼び出されます。

一部のアプリでは、自動確認時間がタイムアウトになるまでユーザーの入力はブロックされ、その後で SMS メッセージに示されている確認コードの入力をユーザーに要求する UI が表示されますが、これは推奨されません。

PhoneAuthCredential オブジェクトを作成する

Firebase からユーザーのスマートフォンに送信された確認コードをユーザーが入力したら、確認コードと onCodeSent コールバックまたは onCodeAutoRetrievalTimeOut コールバックに渡された確認 ID を使用して、PhoneAuthCredential オブジェクトを作成します(onVerificationCompleted が呼び出された場合は PhoneAuthCredential オブジェクトが直接取得されるため、この手順はスキップできます)。

PhoneAuthCredential オブジェクトを作成するには、PhoneAuthProvider.getCredential を呼び出します。

PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);

ユーザーのログインを行う

PhoneAuthCredential オブジェクトを取得したら、onVerificationCompleted コールバックを使用するか、または PhoneAuthProvider.getCredential を呼び出して、PhoneAuthCredential オブジェクトを FirebaseAuth.signInWithCredential に渡しログインフローを完了します。

private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d(TAG, "signInWithCredential:success");

                        FirebaseUser user = task.getResult().getUser();
                        // ...
                    } else {
                        // Sign in failed, display a message and update the UI
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                        if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid
                        }
                    }
                }
            });
}

ホワイトリストに登録された電話番号でテストする

Firebase コンソールを使用して、開発用の電話番号をホワイトリストに登録できます。電話番号をホワイトリストに登録すると、次のメリットがあります。

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

ホワイトリストに登録する電話番号は、次の要件を満たしている必要があります。

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

電話番号と確認コードをホワイトリストに登録する

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

手動テスト

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

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

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

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

統合テスト

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

Android では、API 呼び出しを追加することなく、ホワイトリストに登録された電話番号を簡単に使用できます。ホワイトリストに登録された番号を使用して verifyPhoneNumber を呼び出すと、onCodeSent コールバックがトリガーされ、対応する確認コードを提供する必要が生じます。これにより、Android エミュレータでのテストが可能になります。

String phoneNum = "+16505554567";
String testVerificationCode = "123456";

// Whenever verification is triggered with the whitelisted number,
// provided it is not set for auto-retrieval, onCodeSent will be triggered.
PhoneAuthProvider.getInstance().verifyPhoneNumber(
        phoneNum, 30L /*timeout*/, TimeUnit.SECONDS,
        this, new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            @Override
            public void onCodeSent(String verificationId,
                                   PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                // Save the verification id somewhere
                // ...

                // The corresponding whitelisted code above should be used to complete sign-in.
                MainActivity.this.enableUserManuallyInputCode();
            }

            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                // Sign in with the credential
                // ...
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {
                 // ...
            }

        });

さらに、setAutoRetrievedSmsCodeForPhoneNumber を呼び出すことにより、ホワイトリストに登録された番号とそれに対応する確認コードを自動取得用に設定すると、Android で自動取得のフローをテストできます。

verifyPhoneNumber が呼び出されると、PhoneAuthCredential を使用して onVerificationCompleted が直接トリガーされます。この動作は、ホワイトリストに登録された電話番号でのみ機能します。

アプリケーションを Google Play ストアで公開する際には、この動作が無効になっていること、およびホワイトリストに登録された電話番号がアプリにハードコードされていないことを確認してください。

// The test phone number and code should be whitelisted in the console.
String phoneNumber = "+16505554567";
String smsCode = "123456";

FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
FirebaseAuthSettings firebaseAuthSettings = firebaseAuth.getFirebaseAuthSettings();

// Configure faking the auto-retrieval with the whitelisted numbers.
firebaseAuthSettings.setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode);

PhoneAuthProvider phoneAuthProvider = PhoneAuthProvider.getInstance();
phoneAuthProvider.verifyPhoneNumber(
        phoneNumber,
        60L,
        TimeUnit.SECONDS,
        this, /* activity */
        new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential credential) {
                // Instant verification is applied and a credential is directly returned.
                // ...
            }

            // ...
        });

次のステップ

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

  • アプリでは、FirebaseUser オブジェクトからユーザーの基本的なプロフィール情報を取得できます。ユーザーの管理についての記事をご覧ください。

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

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

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

FirebaseAuth.getInstance().signOut();

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。