Aby umożliwić użytkownikom uwierzytelnianie się w Firebase za pomocą Apple ID, możesz użyć pakietu Firebase SDK do przeprowadzenia kompleksowego procesu logowania OAuth 2.0.
Zanim zaczniesz
Aby umożliwić użytkownikom logowanie się za pomocą Apple, najpierw skonfiguruj logowanie z Apple na stronie dewelopera Apple, a następnie włącz Apple jako dostawcę logowania w swoim projekcie Firebase.
Dołącz do Programu deweloperów Apple
Logowanie przez Apple może skonfigurować tylko osoba należąca do Programu dla deweloperów Apple.
Konfigurowanie logowania przez Apple
Na stronie Apple Developer wykonaj te czynności:
-
Połącz witrynę z aplikacją zgodnie z opisem w pierwszej sekcji artykułu Konfigurowanie logowania z Apple w internecie. Gdy pojawi się taka prośba, zarejestruj ten adres URL jako powrotny adres URL:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
Identyfikator projektu Firebase znajdziesz na stronie ustawień Firebase konsoli Firebase.
Gdy skończysz, zanotuj nowy identyfikator usługi, którego będziesz potrzebować w następnej sekcji.
- Utwórz klucz prywatny „Zaloguj się przez Apple”. W następnej sekcji będziesz potrzebować nowego klucza prywatnego i identyfikatora klucza.
-
Jeśli używasz funkcji Firebase Authentication, które wysyłają e-maile do użytkowników, w tym funkcji logowania za pomocą linku e-mail, weryfikacji adresu e-mail, zmiany konta i cofnięcia uprawnień, skonfiguruj prywatną usługę przekaźnika poczty e-mail Apple i zarejestruj
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com
(lub swoją niestandardową domenę szablonu e-maila), aby Apple mógł przekazywać e-maile wysyłane przez Firebase Authentication na anonimowe adresy e-mail Apple.
Włączanie logowania przez Apple jako dostawcy
- Dodaj Firebase do projektu na Androida. Pamiętaj, aby podczas konfigurowania aplikacji w konsoli Firebase zarejestrować jej podpis SHA-1.
- W konsoli Firebase otwórz sekcję Autoryzacja. Na karcie Metoda logowania włącz dostawcę Apple. Podaj identyfikator usługi utworzony w poprzedniej sekcji. W sekcji Konfiguracja procedury kodu OAuth podaj identyfikator zespołu Apple oraz klucz prywatny i identyfikator klucza utworzone w poprzedniej sekcji.
Zapewnij zgodność z wymaganiami Apple dotyczącymi danych zanonimizowanych
Logowanie z Apple umożliwia użytkownikom anonimizację danych, w tym adresu e-mail, podczas logowania. Użytkownicy, którzy wybiorą tę opcję, będą mieć adresy e-mail w domenie privaterelay.appleid.com
. Jeśli w swojej aplikacji używasz funkcji Logowanie przez Apple, musisz przestrzegać wszystkich zasad lub warunków Apple dotyczących anonimowych identyfikatorów Apple.
Obejmuje to uzyskanie wymaganej zgody użytkownika przed powiązaniem jakichkolwiek danych osobowych umożliwiających bezpośrednią identyfikację z użyciem anonimowego identyfikatora Apple. Jeśli używasz Uwierzytelniania Firebase, może to obejmować te działania:
- Połącz adres e-mail z anonimowym identyfikatorem Apple ID lub odwrotnie.
- Łączenie numeru telefonu z utworzonym anonimowo identyfikatorem Apple ID lub na odwrót
- Połącz nieanonimowe dane logowania do sieci społecznościowej (Facebook, Google itp.) z anonimowym identyfikatorem Apple ID lub odwrotnie.
Powyższa lista nie jest wyczerpująca. Aby sprawdzić, czy Twoja aplikacja spełnia wymagania Apple, zapoznaj się z Apple Developer Program License Agreement w sekcji Membership na koncie dewelopera.
Obsługa procesu logowania za pomocą pakietu SDK Firebase
Na Androidzie najłatwiej uwierzytelnić użytkowników za pomocą Firebase przy użyciu ich kont Apple, jeśli przekażesz cały proces logowania do pakietu SDK Firebase na Androida.
Aby obsłużyć proces logowania za pomocą pakietu Firebase Android SDK, wykonaj te czynności:
Utwórz instancję
OAuthProvider
za pomocą kreatora z identyfikatorem dostawcyapple.com
:Kotlin
val provider = OAuthProvider.newBuilder("apple.com")
Java
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
Opcjonalnie: określ dodatkowe zakresy OAuth 2.0 poza domyślnymi, które chcesz poprosić od dostawcy uwierzytelniania.
Kotlin
provider.setScopes(arrayOf("email", "name"))
Java
List<String> scopes = new ArrayList<String>() { { add("email"); add("name"); } }; provider.setScopes(scopes);
Domyślnie, gdy włączona jest opcja Jedno konto na adres e-mail, Firebase prosi o zakresy adresów e-mail i nazwy. Jeśli zmienisz to ustawienie na Wiele kont na adres e-mail, Firebase nie będzie prosić Apple o żadne uprawnienia, chyba że je określisz.
Opcjonalnie: jeśli chcesz wyświetlić ekran logowania Apple w języku innym niż angielski, ustaw parametr
locale
. Informacje o obsługiwanych lokalizacjach znajdziesz w dokumentacji logowania za pomocą Apple.Kotlin
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr")
Java
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr");
Uwierzytelnij się w Firebase, używając obiektu dostawcy OAuth. Pamiętaj, że w odróżnieniu od innych operacji
FirebaseAuth
ta spowoduje przejęcie kontroli nad interfejsem przez otwarcie niestandardowej karty Chrome. Dlatego nie odwołuj się do swojej aktywności w dołączonych elementachOnSuccessListener
iOnFailureListener
, ponieważ zostaną one natychmiast odłączone, gdy operacja uruchomi interfejs użytkownika.Najpierw sprawdź, czy nie dotarła do Ciebie już odpowiedź. Logowanie się w ten sposób powoduje, że Aktywność działa w tle, co oznacza, że system może ją przywrócić podczas procesu logowania. Aby uniknąć konieczności ponownego próbowania przez użytkownika, sprawdź, czy wynik już nie istnieje.
Aby sprawdzić, czy nie ma oczekujących wyników, zadzwoń pod numer
getPendingAuthResult()
:Kotlin
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"); }
Jeśli nie ma oczekujących wyników, rozpocznij proces logowania, wywołując
startActivityForSignInWithProvider()
:Kotlin
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); } });
W przeciwieństwie do innych dostawców obsługiwanych przez Firebase Auth Apple nie udostępnia adresu URL zdjęcia.
Jeśli użytkownik nie chce udostępniać aplikacji swojego adresu e-mail, Apple udostępnia mu unikalny adres e-mail (w formie
xyz@privaterelay.appleid.com
), który przekazuje Twojej aplikacji. Jeśli skonfigurujesz prywatną usługę przekaźnika poczty e-mail, Apple przekierowuje e-maile wysłane na anonimowy adres na rzeczywisty adres e-mail użytkownika.Apple udostępnia aplikacjom informacje o użytkowniku, takie jak wyświetlana nazwa, tylko wtedy, gdy użytkownik zaloguje się po raz pierwszy. Zwykle Firebase przechowuje wyświetlaną nazwę podczas pierwszego logowania użytkownika za pomocą Apple. Możesz ją uzyskać za pomocą funkcji
getCurrentUser().getDisplayName()
. Jeśli jednak wcześniej zalogujesz użytkownika w aplikacji bez korzystania z Firebase, Apple nie przekaże Firebase jego imienia i nazwiska.
Ponowna weryfikacja i połączenie konta
Tego samego schematu można używać w przypadku startActivityForReauthenticateWithProvider()
, aby pobrać nowe dane logowania na potrzeby operacji związanych z poufnymi danymi, które wymagają niedawnego zalogowania:
Kotlin
// 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.
}
});
Możesz też użyć linkWithCredential()
, aby połączyć różnych dostawców tożsamości z dotychczasowymi kontami.
Pamiętaj, że zanim połączysz konta użytkowników Apple z innymi danymi, musisz uzyskać ich wyraźną zgodę.
Aby na przykład połączyć konto Facebook z bieżącym kontem Firebase, użyj tokena dostępu uzyskanego po zalogowaniu użytkownika na Facebooku:
Kotlin
// 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.
}
}
});
Zaawansowane: ręczne przetwarzanie procesu logowania
Możesz też uwierzytelnić się w Firebase za pomocą konta Apple, obsługując proces logowania za pomocą pakietu SDK Apple Sign-In JS, ręcznie tworząc proces OAuth lub używając biblioteki OAuth, np. AppAuth.
W przypadku każdego żądania logowania generuj losowy ciąg znaków (zwany „nonce”), który pozwoli Ci sprawdzić, czy otrzymany token tożsamości został przyznany w odpowiedzi na żądanie uwierzytelniania aplikacji. Ten krok jest ważny, aby zapobiec atakom metodą powtórzenia.
Na Androidzie możesz wygenerować zabezpieczony kryptograficznie nonce za pomocą funkcji
SecureRandom
, jak w tym przykładzie:Kotlin
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(); }
Następnie uzyskaj hasz SHA246 nonce’a jako ciąg szesnastkowy:
Kotlin
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(); }
Wraz z żądaniem logowania wyślesz hasz SHA-256 wartości jednorazowej, który Apple przekaże bez zmian w odpowiedzi. Firebase weryfikuje odpowiedź, szyfrując pierwotny losowy ciąg znaków i porównując go z wartością przekazaną przez Apple.
Zainicjuj proces logowania Apple za pomocą biblioteki OAuth lub innej metody. Pamiętaj, aby w żądaniu umieścić zaszyfrowany nonce jako parametr.
Po otrzymaniu odpowiedzi od Apple pobierz z niej token identyfikacyjny i użyj go razem z niezaszyfrowanym nonce, aby utworzyć
AuthCredential
:Kotlin
val credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build()
Java
AuthCredential credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build();
Uwierzytelnij się w Firebase, używając danych uwierzytelniających Firebase:
Kotlin
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. // ... } } });
Jeśli wywołanie metody signInWithCredential
się powiedzie, możesz użyć metody getCurrentUser
, aby pobrać dane konta użytkownika.
Unieważnienie tokena
Apple wymaga, aby aplikacje, które umożliwiają tworzenie kont, pozwalały użytkownikom na usunięcie konta z poziomu aplikacji zgodnie z opisem w wytycznych dotyczących weryfikacji App Store.
Aplikacje obsługujące logowanie przez Apple powinny też używać interfejsu REST API logowania przez Apple, aby cofać tokeny użytkowników.
Aby spełnić to wymaganie, wykonaj te czynności:
Zaloguj się za pomocą metody
startActivityForSignInWithProvider()
, aby uzyskaćAuthResult
.Uzyskaj token dostępu dla dostawcy Apple.
Kotlin
val oauthCredential: OAuthCredential = authResult.credential val accessToken = oauthCredential.accessToken
Java
OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential(); String accessToken = oauthCredential.getAccessToken();
Odwołaj token za pomocą interfejsu API
revokeAccessToken
.Kotlin
mAuth.revokeAccessToken(accessToken) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Access token successfully revoked // for the user ... } }
Java
mAuth.revokeAccessToken(accessToken) .addOnCompleteListener(this, new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { // Access token successfully revoked // for the user ... } } });
- Na koniec usuń konto użytkownika (i wszystkie powiązane dane).
Dalsze kroki
Gdy użytkownik zaloguje się po raz pierwszy, zostanie utworzone nowe konto użytkownika i połączone z danymi logowania, czyli nazwą użytkownika i hasłem, numerem telefonu lub informacjami dostawcy uwierzytelniania, za pomocą których użytkownik się zalogował. To nowe konto jest przechowywane w projekcie Firebase i może służyć do identyfikowania użytkownika we wszystkich aplikacjach w projekcie, niezależnie od tego, jak użytkownik się loguje.
-
W swoich aplikacjach możesz pobrać podstawowe informacje o profilu użytkownika z obiektu
FirebaseUser
. Zobacz sekcję Zarządzanie użytkownikami. W regułach Firebase Realtime Database i Cloud Storage Regułach bezpieczeństwa możesz pobrać z zmiennej
auth
unikalny identyfikator zalogowanego użytkownika i używać go do kontrolowania dostępu użytkownika do danych.
Możesz zezwolić użytkownikom na logowanie się w aplikacji za pomocą danych uwierzytelniających od różnych dostawców, łącząc je z dotychczasowym kontem użytkownika.
Aby wylogować użytkownika, wywołaj funkcję
signOut
:Kotlin
Firebase.auth.signOut()
Java
FirebaseAuth.getInstance().signOut();
-