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

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

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

準備

  1. Firebase を iOS プロジェクトに追加します
  2. Podfile に次のポッドをインクルードします。
    pod 'Firebase/Auth'
    
  3. アプリを Firebase プロジェクトに接続していない場合は、Firebase コンソールで接続します。

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

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

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

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

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

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

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

アプリの確認を有効にする

電話番号認証を使用するには、アプリから送信される電話番号ログイン リクエストを、Firebase が確認できるようにしなければなりません。Firebase Authentication でこれを可能にするには、2 つの方法があります。

  • サイレント APNs 通知: ユーザーが端末で初めて電話番号を使用してログインすると、Firebase Authentication はサイレント プッシュ通知を使用して端末にトークンを送信します。アプリが Firebase からの通知を正常に受信した場合、電話番号ログインを続行できます。

    iOS 8.0 以降のサイレント通知は、明示的なユーザーの同意を必要としないため、ユーザーがアプリで APNs 通知の受信を拒否してもその影響を受けません。したがって、アプリは Firebase 電話番号認証を実装する際に、プッシュ通知を受信する許可をユーザーに求める必要はありません。

  • reCAPTCHA 検証: サイレント通知を送信または受信できない場合(たとえば、ユーザーがアプリのバックグラウンド更新を無効にしている場合や、iOS シミュレータでアプリをテストしている場合など)、Firebase Authentication では reCAPTCHA 検証を使用して電話番号ログインフローを実行します。通常、reCAPTCHA チャレンジを完了するためにユーザーが解決しなければならないものはありません。

サイレント プッシュ通知が適切に設定されていれば、reCAPTCHA フローが発生するユーザーの割合はわずかです。それでも、サイレント プッシュ通知を使用できるかどうかにかかわらず、電話番号ログインが正常に機能することを確認してください。

サイレント通知の受信を開始する

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

  1. Xcode で、プロジェクトのプッシュ通知を有効にします
  2. APNs 認証キーを Firebase にアップロードします。APNs 認証キーがない場合は、FCM での APNs の構成に関する説明をご覧ください。

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

    2. [iOS アプリの設定] の下の [APNs 認証キー] で [アップロード] ボタンをクリックします。

    3. キーが保存されている場所に移動し、キーを選択して [開く] をクリックします。キーのキー ID(Apple Developer Member Center の [Certificates, Identifiers & Profiles] で確認できます)を追加し、[Upload] をクリックします。

    APNs 証明書を用意している場合には、証明書をアップロードできます。

reCAPTCHA 検証を設定する

Firebase SDK で reCAPTCHA 検証を使用できるようにするには:

  1. Xcode プロジェクトにカスタム URL スキームを追加します。
    1. プロジェクトの設定を開きます(左側のツリービューでプロジェクト名をダブルクリックします)。[ターゲット] セクションでアプリを選択し、[情報] タブを開いて [URL タイプ] セクションを展開します。
    2. [+] ボタンをクリックし、反転クライアント ID の URL スキームを追加します。この値を確認するには、GoogleService-Info.plist 構成ファイルを開き、REVERSED_CLIENT_ID キーを見つけます。見つかったキーの値をコピーし、設定ページの [URL スキーム] ボックスに貼り付けます。その他の入力欄は空白にしておきます。

      完了すると、設定は次のようになります(ただし、値はアプリケーションによって異なります)。

  2. 省略可: アプリがユーザーに reCAPTCHA を表示する際の SFSafariViewController または UIWebView の提示方法をカスタマイズするには、FIRAuthUIDelegate プロトコルに従ったカスタムクラスを作成して、そのクラスを verifyPhoneNumber:UIDelegate:completion: に渡します。

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

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

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

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

  2. verifyPhoneNumber:UIDelegate:completion: を呼び出し、ユーザーの電話番号を渡します。

    Swift

    PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { (verificationID, error) in
      if let error = error {
        self.showMessagePrompt(error.localizedDescription)
        return
      }
      // Sign in using the verificationID and the code sent to the user
      // ...
    }

    Objective-C

    [[FIRPhoneAuthProvider provider] verifyPhoneNumber:userInput
                                            UIDelegate:nil
                                            completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
      if (error) {
        [self showMessagePrompt:error.localizedDescription];
        return;
      }
      // Sign in using the verificationID and the code sent to the user
      // ...
    }];

    verifyPhoneNumber:UIDelegate:completion: を呼び出すと、Firebase からアプリにサイレント プッシュ通知が送信されるか、ユーザーに対して reCAPTCHA チャレンジが発行されます。アプリが通知を受信した後、またはユーザーが reCAPTCHA チャレンジを完了した後、Firebase から認証コードを含む SMS メッセージが指定の電話番号に送信され、確認 ID が補完関数に渡されます。ユーザーをログインさせるには、確認コードと確認 ID の両方が必要です。

    Firebase によって送信される SMS メッセージはローカライズすることもできます。それには、Auth インスタンスの languageCode プロパティを使用して auth 言語を指定します。

    Swift

     // Change language code to french.
     Auth.auth().languageCode = "fr";
    

    Objective-C

     // Change language code to french.
     [FIRAuth auth].languageCode = @"fr";
    
  3. 確認 ID を保存し、アプリの読み込み時に復元します。これにより、ユーザーがログインフローを完了する前にアプリが終了した場合でも(たとえば SMS アプリへの切り替え時など)、有効な確認 ID を残すことができます。

    確認 ID は任意の方法で保持できます。簡単な方法は、確認 ID を NSUserDefaults オブジェクトに保存することです。

    Swift

    UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
    

    Objective-C

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:verificationID forKey:@"authVerificationID"];
    

    これで、保存された値を復元できます。

    Swift

    let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
    

    Objective-C

    NSString *verificationID = [defaults stringForKey:@"authVerificationID"];
    

verifyPhoneNumber:UIDelegate:completion: の呼び出しが成功したら、SMS メッセージで受け取った確認コードを入力するようユーザーに要求できます。

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

ユーザーが SMS メッセージで受信した確認コードをアプリに入力したら、確認コードと確認 ID から FIRPhoneAuthCredential オブジェクトを作成し、そのオブジェクトを signInWithCredential:completion: に渡します。

  1. ユーザーから確認コードを取得します。
  2. 確認コードと確認 ID から FIRPhoneAuthCredential オブジェクトを作成します。

    Swift

    let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationID,
        verificationCode: verificationCode)

    Objective-C

    FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider]
        credentialWithVerificationID:verificationID
                    verificationCode:userInput];
  3. FIRPhoneAuthCredential オブジェクトを使用してユーザーをログインさせます。

    Swift

    Auth.auth().signInAndRetrieveData(with: credential) { (authResult, error) in
      if let error = error {
        // ...
        return
      }
      // User is signed in
      // ...
    }

    Objective-C

    [[FIRAuth auth] signInAndRetrieveDataWithCredential:credential
                                             completion:^(FIRAuthDataResult * _Nullable authResult,
                                                          NSError * _Nullable error) {
      if (error) {
        // ...
        return;
      }
      // User successfully signed in. Get user data from the FIRUser object
      if (authResult == nil) { return; }
      FIRUser *user = authResult.user;
      // ...
    }];

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

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 で即時確認のフローをテストするための機能も備わっています。

iOS では、verifyPhoneNumber を呼び出す前に appVerificationDisabledForTesting 設定を TRUE に設定する必要があります。これは、バックグラウンドで APN トークンを要求したりサイレント プッシュ通知を送信することなく処理されたりするため、シミュレータでのテストが容易になります。これにより、reCAPTCHA フォールバック フローも無効になります。

アプリの確認が無効になっている場合、ホワイトリストに登録されていない電話番号を使用するとログインが完了しません。この API では、ホワイトリストに登録された電話番号のみ使用できます。

Swift

let phoneNumber = "+16505554567"

// This test verification code is specified for the given test phone number in the developer console.
let testVerificationCode = "123456"

Auth.auth().settings.isAppVerificationDisabledForTesting = TRUE
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate:nil) {
                                                            verificationID, error in
    if (error) {
      // Handles error
      self.handleError(error)
      return
    }
    let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID ?? "",
                                                               verificationCode: testVerificationCode)
    Auth.auth().signInAndRetrieveData(with: credential) { authData, error in
      if (error) {
        // Handles error
        self.handleError(error)
        return
      }
      _user = authData.user
    }];
}];

Objective-C

NSString *phoneNumber = @"+16505554567";

// This test verification code is specified for the given test phone number in the developer console.
NSString *testVerificationCode = @"123456";

[FIRAuth auth].settings.appVerificationDisabledForTesting = YES;
[[FIRPhoneAuthProvider provider] verifyPhoneNumber:phoneNumber
                                        completion:^(NSString *_Nullable verificationID,
                                                     NSError *_Nullable error) {
    if (error) {
      // Handles error
      [self handleError:error];
      return;
    }
    FIRAuthCredential *credential =
        [FIRPhoneAuthProvider credentialWithVerificationID:verificationID
                                          verificationCode:testVerificationCode];
    [FIRAuth auth] signInWithAndRetrieveDataWithCredential:credential
                                                completion:^(FIRUser *_Nullable user,
                                                             NSError *_Nullable error) {
      if (error) {
        // Handles error
        [self handleError:error];
        return;
      }
      _user = user;
    }];
}];

付録: 実装入れ替えなしで電話番号ログインを使用する

Firebase Authentication は、メソッドの実装入れ替えを使用して、アプリの APNs トークンの自動取得、Firebase がアプリに送信するサイレント プッシュ通知の処理、検証中に reCAPTCHA 検証ページからリダイレクトされるカスタム スキームの自動インターセプトを行います。

実装入れ替えを使用しない場合は、FirebaseAppDelegateProxyEnabled フラグをアプリの Info.plist ファイルに追加し、それを NO に設定することで入れ替えを無効化できます。このフラグを NO に設定すると、Firebase Cloud Messaging を含む他の Firebase 製品の実装入れ替えも無効になります。

実装入れ替えを無効にする場合は、明示的に APNs 端末トークン、プッシュ通知、カスタム スキームのリダイレクト URL を Firebase Authentication に渡す必要があります。

APNs 端末トークンを取得するには、application:didRegisterForRemoteNotificationsWithDeviceToken: メソッドを実装し、その中で端末トークンを FIRAuthsetAPNSToken:type: メソッドに渡します。

Swift

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  // Pass device token to auth
  Auth.auth().setAPNSToken(deviceToken, type: AuthAPNSTokenTypeProd)

  // Further handling of the device token if needed by the app
  // ...
}

Objective-C

- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  // Pass device token to auth.
  [[FIRAuth auth] setAPNSToken:deviceToken type:FIRAuthAPNSTokenTypeProd];
  // Further handling of the device token if needed by the app.
}

プッシュ通知を処理するには、application:didReceiveRemoteNotification:fetchCompletionHandler: メソッドで、FIRAuthcanHandleNotification: メソッドを呼び出して Firebase Authentication 関連の通知を確認します。

Swift

func application(_ application: UIApplication,
    didReceiveRemoteNotification notification: [AnyHashable : Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  if Auth.auth().canHandleNotification(notification) {
    completionHandler(UIBackgroundFetchResultNoData)
    return
  }
  // This notification is not auth related, developer should handle it.
}

Objective-C

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)notification
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  // Pass notification to auth and check if they can handle it.
  if ([[FIRAuth auth] canHandleNotification:notification]) {
    completionHandler(UIBackgroundFetchResultNoData);
    return;
  }
  // This notification is not auth related, developer should handle it.
}

カスタム スキームのリダイレクト URL を処理するには、application:openURL:sourceApplication:annotation: メソッド(iOS 8 以前で稼働している端末の場合)または application:openURL:options: メソッド(iOS 9 以降で稼働している端末の場合)を実装し、そのメソッドの中で FIRAuthcanHandleURL メソッドに URL を渡します。

Swift

// For iOS 9+
func application(_ application: UIApplication, open url: URL,
    options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
  if Auth.auth().canHandle(url) {
    return true
  }
  // URL not auth related, developer should handle it.
}

// For iOS 8-
func application(_ application: UIApplication,
                 open url: URL,
                 sourceApplication: String?,
                 annotation: Any) -> Bool {
  if Auth.auth().canHandle(url) {
    Return true
  }
  // URL not auth related, developer should handle it.
}

Objective-C

// For iOS 9+
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  if ([[FIRAuth auth] canHandleURL:url]) {
    return YES;
  }
  // URL not auth related, developer should handle it.
}

// For iOS 8-
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
  if ([[FIRAuth auth] canHandleURL:url]) {
    return YES;
  }
  // URL not auth related, developer should handle it.
}

次のステップ

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

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

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

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

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

Swift

    let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print ("Error signing out: %@", signOutError)
}
  

Objective-C

    NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

さまざまな認証エラーに対応できるようにエラー処理コードを追加することもできます。エラーの処理についての記事をご覧ください。

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

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