Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

iOSアプリに多要素認証を追加する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Firebase Authentication with Identity Platform にアップグレードした場合は、iOS アプリに SMS 多要素認証を追加できます。

多要素認証により、アプリのセキュリティが向上します。攻撃者はパスワードやソーシャル アカウントを侵害することがよくありますが、テキスト メッセージを傍受することはより困難です。

あなたが始める前に

  1. 多要素認証をサポートするプロバイダーを少なくとも 1 つ有効にします。電話認証、匿名認証、Apple Game Centerを除くすべてのプロバイダーが MFA をサポートしています。

  2. アプリがユーザーのメールを確認していることを確認してください。 MFA にはメール認証が必要です。これにより、悪意のあるアクターが所有していない電子メールでサービスに登録し、2 番目の要素を追加して実際の所有者をロックアウトすることを防ぎます。

多要素認証の有効化

  1. Firebase コンソールの[認証] > [サインイン方法] ページを開きます。

  2. [詳細]セクションで、 SMS 多要素認証を有効にします。

    アプリのテストに使用する電話番号も入力する必要があります。オプションですが、開発中のスロットリングを避けるために、テスト電話番号を登録することを強くお勧めします。

  3. アプリのドメインをまだ承認していない場合は、Firebase コンソールの[認証] > [設定]ページで許可リストに追加します。

アプリの確認

Firebase は、SMS リクエストがアプリからのものであることを確認する必要があります。これは、次の 2 つの方法で行うことができます。

  • サイレント APNs 通知: ユーザーが初めてサインインするときに、Firebase はサイレント プッシュ通知をユーザーのデバイスに送信できます。アプリが通知を受信すると、認証を続行できます。 iOS 8.0 以降では、プッシュ通知でこのメソッドを使用することを許可するようにユーザーに依頼する必要はありません。

  • reCAPTCHA 検証: サイレント通知を送信できない場合 (たとえば、ユーザーがバックグラウンド更新を無効にしている、iOS シミュレーターでアプリをテストしているなど)、reCAPTCHA を使用できます。多くの場合、reCAPTCHA はユーザーの操作なしで自動的に解決されます。

サイレント通知の使用

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

  1. Xcode で、プロジェクトのプッシュ通知を有効にします。

  2. Firebase コンソールを使用して APNs 認証キーをアップロードします (変更は自動的に Google Cloud Firebase に引き継がれます)。 APNs 認証キーをまだ持っていない場合は、「 FCM を使用した APNs の構成」を参照して取得方法を確認してください。

    1. Firebase コンソールを開きます。

    2. プロジェクト設定に移動します。

    3. [クラウド メッセージング] タブを選択します。

    4. [ APNs authentication key ] の下の [ iOS app configuration ] セクションで、[ Upload ] をクリックします。

    5. キーを選択します。

    6. キーのキー ID を追加します。キー ID は、 Apple Developer Member CenterCertificates, Identifiers & Profilesの下にあります。

    7. [アップロード]をクリックします。

APNs 証明書が既にある場合は、代わりに証明書をアップロードできます。

reCAPTCHA 検証の使用

クライアント SDK で reCAPTCHA を使用できるようにするには:

  1. Xcode でプロジェクト構成を開きます。

  2. 左側のツリー ビューでプロジェクト名をダブルクリックします。

  3. [ターゲット]セクションからアプリを選択します。

  4. [情報] タブを選択します。

  5. [ URL タイプ]セクションを展開します。

  6. +ボタンをクリックします。

  7. [ URL スキーム]フィールドに反転したクライアント ID を入力します。この値は、 GoogleService-Info.plist構成ファイルにREVERSED_CLIENT_IDとしてリストされています。

完了すると、構成は次のようになります。

カスタム スキーム

必要に応じて、reCAPTCHA を表示するときにアプリがSFSafariViewControllerまたはUIWebViewを表示する方法をカスタマイズできます。これを行うには、 FIRAuthUIDelegateプロトコルに準拠するカスタム クラスを作成し、それをverifyPhoneNumber:UIDelegate:completion:に渡します。

登録パターンの選択

アプリが多要素認証を必要とするかどうか、ユーザーを登録する方法と時期を選択できます。いくつかの一般的なパターンは次のとおりです。

  • ユーザーの第 2 要素を登録の一部として登録します。アプリですべてのユーザーに対して多要素認証が必要な場合は、この方法を使用します。 2 番目の要素を登録するには、アカウントに確認済みのメール アドレスが必要であるため、登録フローはこれに対応する必要があることに注意してください。

  • 登録時に第 2 要素を登録するためのスキップ可能なオプションを提供します。多要素認証を推奨するが必須ではないアプリは、このアプローチを好む可能性があります。

  • サインアップ画面ではなく、ユーザーのアカウントまたはプロファイル管理ページから 2 番目の要素を追加する機能を提供します。これにより、登録プロセス中の摩擦を最小限に抑えながら、セキュリティに敏感なユーザーが多要素認証を利用できるようにします.

  • ユーザーがセキュリティ要件の高い機能にアクセスしたい場合は、第 2 要素を段階的に追加する必要があります。

第 2 要素の登録

ユーザーの新しい第 2 要素を登録するには:

  1. ユーザーを再認証します。

  2. ユーザーに電話番号を入力してもらいます。

  3. ユーザーの多要素セッションを取得します。

    迅速

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objective-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. ユーザーの電話に確認メッセージを送信します。電話番号の形式が+で始まり、その他の句読点や空白がないことを確認してください (例: +15105551234 )

    迅速

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objective-C

    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber
                                          UIDelegate:nil
                                  multiFactorSession:session
                                          completion:^(NSString * _Nullable verificationID,
                                                        NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.
    }];
    

    必須ではありませんが、SMS メッセージを受信することと、標準料金が適用されることを事前にユーザーに通知することをお勧めします。

    verifyPhoneNumber()メソッドは、サイレント プッシュ通知を使用してバックグラウンドでアプリの検証プロセスを開始します。サイレント プッシュ通知が利用できない場合は、代わりに reCAPTCHA チャレンジが発行されます。

  5. SMS コードが送信されたら、コードを確認するようユーザーに依頼します。次に、応答を使用してPhoneAuthCredentialを作成します。

    迅速

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. アサーション オブジェクトを初期化します。

    迅速

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. 登録を完了します。必要に応じて、2 番目の要素の表示名を指定できます。これは、認証フロー中に電話番号がマスクされるため (+1******1234 など)、複数の第 2 要素を持つユーザーに役立ちます。

    迅速

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objective-C

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    [authResult.user.multiFactor enrollWithAssertion:assertion
                                         displayName:nil
                                          completion:^(NSError * _Nullable error) {
        // ...
    }];
    

以下のコードは、2 番目の要素を登録する完全な例を示しています。

迅速

let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
  // Send SMS verification code.
  PhoneAuthProvider.provider().verifyPhoneNumber(
    phoneNumber,
    uiDelegate: nil,
    multiFactorSession: session
  ) { (verificationId, error) in
    // verificationId will be needed for enrollment completion.
    // Ask user for the verification code.
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: phoneSecondFactorVerificationCode)
    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
  }
})

Objective-C

FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                              NSError * _Nullable error) {
    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumber:phoneNumber
      UIDelegate:nil
      multiFactorSession:session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.

        // Ask user for the verification code.
        // ...

        // Then:
        FIRPhoneAuthCredential *credential =
            [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
                                                        verificationCode:kPhoneSecondFactorVerificationCode];
        FIRMultiFactorAssertion *assertion =
            [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

        // Complete enrollment. This will update the underlying tokens
        // and trigger ID token change listener.
        [user.multiFactor enrollWithAssertion:assertion
                                  displayName:displayName
                                    completion:^(NSError * _Nullable error) {
            // ...
        }];
    }];
}];

おめでとう!ユーザーの 2 番目の認証要素が正常に登録されました。

第 2 要素によるユーザーのサインイン

2 要素 SMS 検証を使用してユーザーをサインインするには:

  1. 最初の要素でユーザーをサインインさせ、多要素認証が必要であることを示すエラーをキャッチします。このエラーには、リゾルバー、登録された第 2 要素に関するヒント、およびユーザーが第 1 要素で正常に認証されたことを証明する基になるセッションが含まれています。

    たとえば、ユーザーの最初の要素が電子メールとパスワードである場合:

    迅速

    Auth.auth().signIn(
      withEmail: email,
      password: password
    ) { (result, error) in
      let authError = error as NSError
      if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver =
          authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        // ...
      } else {
        // Handle other errors such as wrong password.
      }
    }
    

    Objective-C

    [FIRAuth.auth signInWithEmail:email
                         password:password
                       completion:^(FIRAuthDataResult * _Nullable authResult,
                                    NSError * _Nullable error) {
        if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
            // User is not enrolled with a second factor and is successfully signed in.
            // ...
        } else {
            // The user is a multi-factor user. Second factor challenge is required.
        }
    }];
    

    ユーザーの最初の要素が OAuth などのフェデレーション プロバイダーである場合は、 getCredentialWith()を呼び出した後にエラーをキャッチします。

  2. ユーザーが複数の二次要素を登録している場合は、どれを使用するかを尋ねます。マスクされた電話番号はresolver.hints[selectedIndex].phoneNumberで取得でき、表示名はresolver.hints[selectedIndex].displayNameで取得できます。

    迅速

    // Ask user which second factor to use. Then:
    if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    

    Objective-C

    FIRMultiFactorResolver *resolver =
        (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
    
    // Ask user which second factor to use. Then:
    FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
    if (hint.factorID == FIRPhoneMultiFactorID) {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    
  3. ユーザーの電話に確認メッセージを送信します。

    迅速

    // Send SMS verification code.
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      // verificationId will be needed for sign-in completion.
    }
    

    Objective-C

    // Send SMS verification code
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumberWithMultiFactorInfo:hint
      UIDelegate:nil
      multiFactorSession:resolver.session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        if (error != nil) {
            // Failed to verify phone number.
        }
    }];
    
  4. SMS コードが送信されたら、コードを確認し、それを使用してPhoneAuthCredentialを作成するようにユーザーに依頼します。

    迅速

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. クレデンシャルを使用してアサーション オブジェクトを初期化します。

    迅速

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. サインインを解決します。その後、標準のプロバイダー固有のデータと認証資格情報を含む元のサインイン結果にアクセスできます。

    迅速

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(with: assertion) { (authResult, error) in
      // authResult will also contain the user, additionalUserInfo, optional
      // credential (null for email/password) associated with the first factor sign-in.
    
      // For example, if the user signed in with Google as a first factor,
      // authResult.additionalUserInfo will contain data related to Google provider that
      // the user signed in with.
    
      // user.credential contains the Google OAuth credential.
      // user.credential.accessToken contains the Google OAuth access token.
      // user.credential.idToken contains the Google OAuth ID token.
    }
    

    Objective-C

    // Complete sign-in.
    [resolver resolveSignInWithAssertion:assertion
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                            NSError * _Nullable error) {
        if (error != nil) {
            // User successfully signed in with the second factor phone number.
        }
    }];
    

以下のコードは、多要素ユーザーのサインインの完全な例を示しています。

迅速

Auth.auth().signIn(
  withEmail: email,
  password: password
) { (result, error) in
  let authError = error as NSError?
  if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
    let resolver =
      authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver

    // Ask user which second factor to use.
    // ...

    // Then:
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo

    // Send SMS verification code
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      if error != nil {
        // Failed to verify phone number.
      }
      // Ask user for the SMS verification code.
      // ...

      // Then:
      let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationId!,
        verificationCode: verificationCodeFromUser)
      let assertion = PhoneMultiFactorGenerator.assertion(with: credential)

      // Complete sign-in.
      resolver.resolveSignIn(with: assertion) { (authResult, error) in
        if error != nil {
          // User successfully signed in with the second factor phone number.
        }
      }
    }
  }
}

Objective-C

[FIRAuth.auth signInWithEmail:email
                     password:password
                   completion:^(FIRAuthDataResult * _Nullable authResult,
                               NSError * _Nullable error) {
    if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
        // User is not enrolled with a second factor and is successfully signed in.
        // ...
    } else {
        FIRMultiFactorResolver *resolver =
            (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];

        // Ask user which second factor to use.
        // ...

        // Then:
        FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];

        // Send SMS verification code
        [FIRPhoneAuthProvider.provider
          verifyPhoneNumberWithMultiFactorInfo:hint
                                    UIDelegate:nil
                            multiFactorSession:resolver.session
                                    completion:^(NSString * _Nullable verificationID,
                                                NSError * _Nullable error) {
            if (error != nil) {
                // Failed to verify phone number.
            }

            // Ask user for the SMS verification code.
            // ...

            // Then:
            FIRPhoneAuthCredential *credential =
                [FIRPhoneAuthProvider.provider
                  credentialWithVerificationID:verificationID
                              verificationCode:kPhoneSecondFactorVerificationCode];
            FIRMultiFactorAssertion *assertion =
                [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

            // Complete sign-in.
            [resolver resolveSignInWithAssertion:assertion
                                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                                    NSError * _Nullable error) {
                if (error != nil) {
                    // User successfully signed in with the second factor phone number.
                }
            }];
        }];
    }
}];

おめでとう!多要素認証を使用してユーザーのサインインに成功しました。

次は何ですか