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サイトで、次の操作を行います。
Web 用に Apple でサインインを構成する の最初のセクションで説明されているように、Web サイトをアプリに関連付けます。プロンプトが表示されたら、次の URL をリターン URL として登録します。
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
Firebase コンソールの設定ページで Firebase プロジェクト ID を取得できます。
完了したら、次のセクションで必要になる新しいサービス ID をメモします。
- Sign In with Apple private key を作成します。次のセクションで、新しい秘密鍵とキー ID が必要になります。
メール リンクのサインイン、メール アドレスの確認、アカウント変更の取り消しなど、ユーザーにメールを送信する Firebase Authentication の機能のいずれかを使用する場合は、Apple プライベート メール リレー サービスを構成し、
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(またはカスタマイズしたメール テンプレート ドメイン)を使用して、Apple が Firebase Authentication から送信されたメールを匿名の Apple メール アドレスにリレーできるようにします。
Apple をサインイン プロバイダーとして有効にする
- Android プロジェクトに Firebase を追加します。 Firebase コンソールでアプリをセットアップするときは、必ずアプリの SHA-1 署名を登録してください。
- 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 でサインイン フローを処理するには、次の手順に従います。
プロバイダー ID が
apple.com
の Builder を使用してOAuthProvider
のインスタンスを作成します。Kotlin+KTX
val provider = OAuthProvider.newBuilder("apple.com")
Java
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
オプション:認証プロバイダーから要求するデフォルト以外の追加の 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 にスコープを要求しません。
オプション: 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");
OAuth プロバイダー オブジェクトを使用して Firebase で認証します。他の
FirebaseAuth
操作とは異なり、これはカスタム Chrome タブを開くことで UI を制御することに注意してください。したがって、アタッチしたOnSuccessListener
とOnFailureListener
で 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 で認証することもできます。
サインイン リクエストごとに、ランダムな文字列 (「ナンス」) を生成します。これを使用して、アプリの認証リクエストに応じて取得した 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 から渡された値と比較することで、応答を検証します。
OAuth ライブラリまたはその他の方法を使用して、Apple のサインイン フローを開始します。ハッシュ化されたノンスをパラメーターとしてリクエストに必ず含めてください。
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();
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();