Google は、黒人コミュニティのための人種的公平の促進に取り組んでいます。詳細をご覧ください。

AndroidでAppleを使用して認証する

Firebase SDK を使用してエンド ツー エンドの OAuth 2.0 サインイン フローを実行することにより、ユーザーが Apple ID を使用して Firebase で認証できるようにすることができます。

あなたが始める前に

Apple を使用してユーザーをサインインするには、まず Apple の開発者サイトで Sign In with Apple を構成してから、Firebase プロジェクトのサインイン プロバイダーとして Apple を有効にします。

Apple デベロッパ プログラムに参加する

Sign In with Apple は、 Apple Developer Programのメンバーのみが構成できます。

Apple でサインインを構成する

Apple Developerサイトで、次の操作を行います。

  1. Web 用に Apple でサインインを構成する の最初のセクションで説明されているように、Web サイトをアプリに関連付けます。プロンプトが表示されたら、次の URL をリターン URL として登録します。

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    Firebase コンソールの設定ページで Firebase プロジェクト ID を取得できます。

    完了したら、次のセクションで必要になる新しいサービス ID をメモします。

  2. Sign In with Apple private key を作成します。次のセクションで、新しい秘密鍵とキー ID が必要になります。
  3. メール リンクのサインイン、メール アドレスの確認、アカウント変更の取り消しなど、ユーザーにメールを送信する Firebase Authentication の機能のいずれかを使用する場合は、Apple プライベート メール リレー サービスを構成し、 noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (またはカスタマイズしたメール テンプレート ドメイン)を使用して、Apple が Firebase Authentication から送信されたメールを匿名の Apple メール アドレスにリレーできるようにします。

Apple をサインイン プロバイダーとして有効にする

  1. Android プロジェクトに Firebase を追加します。 Firebase コンソールでアプリをセットアップするときは、必ずアプリの SHA-1 署名を登録してください。
  2. Firebase コンソールで、 Authセクションを開きます。 [サインイン方法] タブで、 Appleプロバイダーを有効にします。前のセクションで作成したサービス ID を指定します。また、 OAuth コード フロー構成セクションで、Apple チーム ID と、前のセクションで作成した秘密鍵と鍵 ID を指定します。

Apple の匿名データ要件に準拠する

Sign In with Apple では、ユーザーはサインイン時にメール アドレスなどのデータを匿名化するオプションを利用できます。このオプションを選択したユーザーは、ドメインがprivaterelay.appleid.comのメール アドレスを持っています。アプリで Sign In with Apple を使用する場合、これらの匿名化された Apple ID に関して適用される開発者ポリシーまたは Apple の条件に従う必要があります。

これには、直接識別される個人情報を匿名化された Apple ID に関連付ける前に、必要なユーザーの同意を得ることも含まれます。 Firebase Authentication を使用する場合、これには次のアクションが含まれる場合があります。

  • メールアドレスを匿名化された Apple ID に、またはその逆にリンクします。
  • 電話番号を匿名化された Apple ID にリンクする、またはその逆を行う
  • 非匿名のソーシャル認証情報 (Facebook、Google など) を匿名化された Apple ID にリンクするか、その逆を行います。

上記のリストは網羅的なものではありません。アプリが Apple の要件を満たしていることを確認するには、開発者アカウントのメンバーシップ セクションにある Apple Developer Program License Agreement を参照してください。

Firebase SDK でログイン フローを処理する

Android では、Apple アカウントを使用して Firebase でユーザーを認証する最も簡単な方法は、Firebase Android SDK でサインイン フロー全体を処理することです。

Firebase Android SDK でサインイン フローを処理するには、次の手順に従います。

  1. プロバイダー ID がapple.comの Builder を使用してOAuthProviderのインスタンスを作成します。

    Kotlin+KTX

    val provider = OAuthProvider.newBuilder("apple.com")
    

    Java

    OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
    
  2. オプション:認証プロバイダーから要求するデフォルト以外の追加の OAuth 2.0 スコープを指定します。

    Kotlin+KTX

    provider.setScopes(arrayOf("email", "name"))
    

    Java

    List<String> scopes =
        new ArrayList<String>() {
          {
            add("email");
            add("name");
          }
        };
    provider.setScopes(scopes);
    

    デフォルトでは、メール アドレスごとに 1 つのアカウントが有効になっている場合、Firebase はメールと名前のスコープをリクエストします。この設定を [電子メール アドレスごとに複数のアカウント] に変更すると、指定しない限り、Firebase は Apple にスコープを要求しません。

  3. オプション: Apple のサインイン画面を英語以外の言語で表示する場合は、 localeパラメーターを設定します。サポートされているロケールについては、 Apple でサインインに関するドキュメントを参照してください。

    Kotlin+KTX

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr")
    

    Java

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr");
    
  4. OAuth プロバイダー オブジェクトを使用して Firebase で認証します。他のFirebaseAuth操作とは異なり、これはカスタム Chrome タブを開くことで UI を制御することに注意してください。したがって、アタッチしたOnSuccessListenerOnFailureListenerで Activity を参照しないでください。これらは、操作が UI を開始するとすぐにデタッチされます。

    最初に、すでに応答を受け取っているかどうかを確認する必要があります。この方法でサインインすると、Activity がバックグラウンドに置かれます。つまり、サインイン フロー中にシステムによって回収される可能性があります。これが発生した場合にユーザーに再試行させないようにするために、結果が既に存在するかどうかを確認する必要があります。

    保留中の結果があるかどうかを確認するには、 getPendingAuthResult()を呼び出します。

    Kotlin+KTX

    val pending = auth.pendingAuthResult
    if (pending != null) {
        pending.addOnSuccessListener { authResult ->
            Log.d(TAG, "checkPending:onSuccess:$authResult")
            // Get the user profile with authResult.getUser() and
            // authResult.getAdditionalUserInfo(), and the ID
            // token from Apple with authResult.getCredential().
        }.addOnFailureListener { e ->
            Log.w(TAG, "checkPending:onFailure", e)
        }
    } else {
        Log.d(TAG, "pending: null")
    }
    

    Java

    mAuth = FirebaseAuth.getInstance();
    Task<AuthResult> pending = mAuth.getPendingAuthResult();
    if (pending != null) {
        pending.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                Log.d(TAG, "checkPending:onSuccess:" + authResult);
                // Get the user profile with authResult.getUser() and
                // authResult.getAdditionalUserInfo(), and the ID
                // token from Apple with authResult.getCredential().
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "checkPending:onFailure", e);
            }
        });
    } else {
        Log.d(TAG, "pending: null");
    }
    

    保留中の結果がない場合は、 startActivityForSignInWithProvider()を呼び出してサインイン フローを開始します。

    Kotlin+KTX

    auth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener { authResult ->
                // Sign-in successful!
                Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}")
                val user = authResult.user
                // ...
            }
            .addOnFailureListener { e ->
                Log.w(TAG, "activitySignIn:onFailure", e)
            }
    

    Java

    mAuth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener(
                    new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            // Sign-in successful!
                            Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser());
                            FirebaseUser user = authResult.getUser();
                            // ...
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.w(TAG, "activitySignIn:onFailure", e);
                        }
                    });
    

    Firebase Auth でサポートされている他のプロバイダーとは異なり、Apple は写真の URL を提供していません。

    また、ユーザーが自分の電子メールをアプリと共有しないことを選択した場合、Apple はそのユーザーに固有の電子メール アドレス ( xyz@privaterelay.appleid.comの形式) をプロビジョニングし、それをアプリと共有します。プライベート メール リレー サービスを設定した場合、Apple は匿名化されたアドレスに送信されたメールをユーザーの実際のメール アドレスに転送します。

    Apple は、ユーザーが初めてサインインしたときにのみ、表示名などのユーザー情報をアプリと共有します。通常、Firebase は、ユーザーが初めて Apple にサインインしたときに表示名を保存します。これはgetCurrentUser().getDisplayName()で取得できます。ただし、以前に Apple を使用して Firebase を使用せずにユーザーをアプリにサインインさせた場合、Apple は Firebase にユーザーの表示名を提供しません。

再認証とアカウントのリンク

同じパターンをstartActivityForReauthenticateWithProvider()で使用できます。これを使用して、最近のサインインが必要な機密操作の新しい資格情報を取得できます。

Kotlin+KTX

// The user is already signed-in.
val firebaseUser = auth.getCurrentUser()

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener( authResult -> {
        // User is re-authenticated with fresh tokens and
        // should be able to perform sensitive operations
        // like account deletion and email or password
        // update.
    })
    .addOnFailureListener( e -> {
        // Handle failure.
    })

Java

// The user is already signed-in.
FirebaseUser firebaseUser = mAuth.getCurrentUser();

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener(
        new OnSuccessListener<AuthResult>() {
          @Override
          public void onSuccess(AuthResult authResult) {
            // User is re-authenticated with fresh tokens and
            // should be able to perform sensitive operations
            // like account deletion and email or password
            // update.
          }
        })
    .addOnFailureListener(
        new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Handle failure.
          }
        });

また、 linkWithCredential()を使用して、さまざまな ID プロバイダーを既存のアカウントにリンクできます。

Apple では、Apple アカウントを他のデータにリンクする前に、ユーザーから明示的な同意を得る必要があることに注意してください。

たとえば、Facebook アカウントを現在の Firebase アカウントにリンクするには、ユーザーが Facebook にサインインして取得したアクセス トークンを使用します。

Kotlin+KTX

// Initialize a Facebook credential with a Facebook access token.
val credential = FacebookAuthProvider.getCredential(token.getToken())

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, task -> {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      });

Java

// Initialize a Facebook credential with a Facebook access token.
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
      @Override
      public void onComplete(@NonNull Task<AuthResult> task) {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      }
    });

高度: サインイン フローを手動で処理する

また、Apple サインイン JS SDK を使用してサインイン フローを処理するか、OAuth フローを手動で構築するか、 AppAuthなどの OAuth ライブラリを使用して、Apple アカウントを使用して Firebase で認証することもできます。

  1. サインイン リクエストごとに、ランダムな文字列 (「ナンス」) を生成します。これを使用して、アプリの認証リクエストに応じて取得した ID トークンが付与されたことを確認します。この手順は、リプレイ攻撃を防ぐために重要です。

    次の例のように、 SecureRandomを使用して Android で暗号的に安全なナンスを生成できます。

    Kotlin+KTX

    private fun generateNonce(length: Int): String {
        val generator = SecureRandom()
    
        val charsetDecoder = StandardCharsets.US_ASCII.newDecoder()
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE)
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE)
    
        val bytes = ByteArray(length)
        val inBuffer = ByteBuffer.wrap(bytes)
        val outBuffer = CharBuffer.allocate(length)
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes)
            inBuffer.rewind()
            charsetDecoder.reset()
            charsetDecoder.decode(inBuffer, outBuffer, false)
        }
        outBuffer.flip()
        return outBuffer.toString()
    }
    

    Java

    private String generateNonce(int length) {
        SecureRandom generator = new SecureRandom();
    
        CharsetDecoder charsetDecoder = StandardCharsets.US_ASCII.newDecoder();
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE);
    
        byte[] bytes = new byte[length];
        ByteBuffer inBuffer = ByteBuffer.wrap(bytes);
        CharBuffer outBuffer = CharBuffer.allocate(length);
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes);
            inBuffer.rewind();
            charsetDecoder.reset();
            charsetDecoder.decode(inBuffer, outBuffer, false);
        }
        outBuffer.flip();
        return outBuffer.toString();
    }
    

    次に、ノンスの SHA246 ハッシュを 16 進文字列として取得します。

    Kotlin+KTX

    private fun sha256(s: String): String {
        val md = MessageDigest.getInstance("SHA-256")
        val digest = md.digest(s.toByteArray())
        val hash = StringBuilder()
        for (c in digest) {
            hash.append(String.format("%02x", c))
        }
        return hash.toString()
    }
    

    Java

    private String sha256(String s) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest(s.getBytes());
        StringBuilder hash = new StringBuilder();
        for (byte c: digest) {
            hash.append(String.format("%02x", c));
        }
        return hash.toString();
    }
    

    サインイン要求で nonce の SHA256 ハッシュを送信すると、Apple はそれを応答で変更せずに渡します。 Firebase は、元のノンスをハッシュし、それを Apple から渡された値と比較することで、応答を検証します。

  2. OAuth ライブラリまたはその他の方法を使用して、Apple のサインイン フローを開始します。ハッシュ化されたノンスをパラメーターとしてリクエストに必ず含めてください。

  3. Apple の応答を受け取ったら、応答から ID トークンを取得し、それとハッシュされていないナンスを使用してAuthCredentialを作成します。

    Kotlin+KTX

    val credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build()
    

    Java

    AuthCredential credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build();
    
  4. Firebase 認証情報を使用して Firebase で認証します。

    Kotlin+KTX

    auth.signInWithCredential(credential)
          .addOnCompleteListener(this) { task ->
              if (task.isSuccessful) {
                // User successfully signed in with Apple ID token.
                // ...
              }
          }
    

    Java

    mAuth.signInWithCredential(credential)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
          @Override
          public void onComplete(@NonNull Task<AuthResult> task) {
            if (task.isSuccessful()) {
              // User successfully signed in with Apple ID token.
              // ...
            }
          }
        });
    

signInWithCredentialの呼び出しが成功した場合は、 getCurrentUserメソッドを使用してユーザーのアカウント データを取得できます。

次のステップ

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

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

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

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

ユーザーをサインアウトするには、 signOutを呼び出します。

Kotlin+KTX

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();