Вы можете разрешить своим пользователям проходить аутентификацию в Firebase с использованием их Apple ID, используя Firebase SDK для реализации сквозного процесса входа OAuth 2.0.
Прежде чем начать
Чтобы выполнить вход пользователей с помощью Apple, сначала настройте функцию «Вход с Apple» на сайте разработчиков Apple, а затем включите Apple в качестве поставщика входа для вашего проекта Firebase.
Присоединяйтесь к программе разработчиков Apple
Функцию «Войти с Apple» могут настроить только участники программы разработчиков Apple .
Настроить вход с Apple
На сайте разработчиков Apple выполните следующие действия:
Свяжите свой веб-сайт с приложением, как описано в первом разделе статьи «Настройка входа с Apple для веб-сайта» . При появлении запроса зарегистрируйте следующий URL-адрес в качестве URL-адреса возврата:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
Идентификатор вашего проекта Firebase можно получить на странице настроек консоли Firebase .
Закончив, запишите свой новый идентификатор услуги, он понадобится вам в следующем разделе.
- Создайте вход с помощью закрытого ключа Apple . Вам понадобятся новый закрытый ключ и идентификатор ключа в следующем разделе.
Если вы используете какие-либо функции Firebase Authentication , которые отправляют электронные письма пользователям, включая вход по ссылке электронной почты, проверку адреса электронной почты, отзыв изменений учетной записи и другие, настройте службу ретрансляции частной электронной почты Apple и зарегистрируйте
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(или ваш собственный домен шаблона электронной почты), чтобы Apple могла ретранслировать электронные письма, отправленные Firebase Authentication , на анонимные адреса электронной почты Apple.
Включить Apple в качестве поставщика услуг входа
- Добавьте Firebase в свой проект Android . Обязательно зарегистрируйте подпись SHA-1 вашего приложения при настройке в консоли Firebase .
- В консоли Firebase откройте раздел «Аутентификация» . На вкладке «Метод входа» включите поставщика Apple . Укажите идентификатор сервиса, созданный в предыдущем разделе. Кроме того, в разделе «Конфигурация потока кода OAuth» укажите идентификатор вашей команды Apple Team ID, а также закрытый ключ и идентификатор ключа, созданные в предыдущем разделе.
Соблюдайте требования Apple к анонимным данным
Функция «Войти с Apple» предоставляет пользователям возможность анонимизировать свои данные, включая адрес электронной почты, при входе в систему. Адреса электронной почты пользователей, выбравших эту опцию, находятся в домене privaterelay.appleid.com
. При использовании функции «Войти с Apple» в вашем приложении вы должны соблюдать все применимые политики разработчиков или условия Apple в отношении этих анонимизированных идентификаторов Apple ID.
Это включает получение любого необходимого согласия пользователя перед связыванием какой-либо персональной информации, позволяющей идентифицировать пользователя, с анонимизированным Apple ID. При использовании аутентификации Firebase это может включать следующие действия:
- Свяжите адрес электронной почты с анонимным Apple ID и наоборот.
- Привязать номер телефона к анонимному Apple ID и наоборот
- Свяжите неанонимные учетные данные социальной сети (Facebook, Google и т. д.) с анонимным Apple ID или наоборот.
Приведённый выше список не является исчерпывающим. Чтобы убедиться, что ваше приложение соответствует требованиям Apple, ознакомьтесь с лицензионным соглашением программы Apple Developer Program в разделе «Участие» вашей учётной записи разработчика.
Управляйте процессом входа с помощью Firebase SDK
На устройствах Android самый простой способ аутентификации пользователей в Firebase с использованием их учетных записей Apple — это обработка всего процесса входа с помощью Firebase Android SDK.
Чтобы организовать процесс входа с помощью Firebase Android SDK, выполните следующие действия:
Создайте экземпляр
OAuthProvider
, используя его Builder с идентификатором провайдераapple.com
:Kotlin
val provider = OAuthProvider.newBuilder("apple.com")
Java
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
Необязательно: укажите дополнительные области OAuth 2.0, выходящие за рамки областей по умолчанию, которые вы хотите запросить у поставщика аутентификации.
Kotlin
provider.setScopes(arrayOf("email", "name"))
Java
List<String> scopes = new ArrayList<String>() { { add("email"); add("name"); } }; provider.setScopes(scopes);
По умолчанию, если включен параметр «Одна учётная запись на адрес электронной почты» , Firebase запрашивает области действия адресов электронной почты и имён. Если изменить этот параметр на «Несколько учётных записей на адрес электронной почты» , Firebase не будет запрашивать области действия у Apple, пока вы не укажете их.
Необязательно: если вы хотите, чтобы экран входа Apple отображался на языке, отличном от английского, задайте соответствующий
locale
стандарт. Поддерживаемые языковые стандарты см. в документации по входу с 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");
Выполните аутентификацию в Firebase с помощью объекта провайдера OAuth. Обратите внимание, что в отличие от других операций
FirebaseAuth
, эта операция перехватит управление вашим пользовательским интерфейсом, открыв пользовательскую вкладку Chrome. Поэтому не ссылайтесь на Activity в подключаемых слушателяхOnSuccessListener
иOnFailureListener
, так как они будут немедленно отключены при запуске пользовательского интерфейса.Сначала проверьте, получили ли вы ответ. Вход с помощью этого метода переводит вашу Activity в фоновый режим, что означает, что система может её восстановить во время процесса входа. Чтобы не заставлять пользователя повторять попытку в такой ситуации, проверьте, есть ли уже результат.
Чтобы проверить наличие ожидающего результата, вызовите
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"); }
Если ожидающих результатов нет, запустите процесс входа, вызвав
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); } });
В отличие от других поставщиков, поддерживаемых Firebase Auth, Apple не предоставляет URL-адрес фотографии.
Кроме того, когда пользователь решает не предоставлять приложению свой адрес электронной почты, Apple предоставляет ему уникальный адрес электронной почты (вида
xyz@privaterelay.appleid.com
), который затем предоставляется вашему приложению. Если вы настроили службу ретрансляции приватной электронной почты, Apple пересылает письма, отправленные на анонимный адрес, на реальный адрес электронной почты пользователя.Apple передает приложениям информацию о пользователе, такую как отображаемое имя, только при первом входе пользователя в систему. Обычно Firebase сохраняет отображаемое имя при первом входе пользователя в Apple. Получить его можно с помощью
getCurrentUser().getDisplayName()
. Однако, если вы ранее использовали Apple для входа пользователя в приложение без использования Firebase, Apple не предоставит Firebase отображаемое имя пользователя.
Повторная аутентификация и привязка аккаунта
Тот же шаблон можно использовать с startActivityForReauthenticateWithProvider()
, который можно использовать для получения новых учетных данных для конфиденциальных операций, требующих недавнего входа в систему:
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.
}
});
Кроме того, вы можете использовать linkWithCredential()
для привязки различных поставщиков удостоверений к существующим учетным записям.
Обратите внимание, что Apple требует от вас получить явное согласие пользователей, прежде чем связывать их учетные записи Apple с другими данными.
Например, чтобы связать учетную запись Facebook с текущей учетной записью Firebase, используйте токен доступа, полученный при входе пользователя в Facebook:
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.
}
}
});
Дополнительно: управление процессом входа вручную
Вы также можете выполнить аутентификацию в Firebase с помощью учетной записи Apple, обработав поток входа либо с помощью Apple Sign-In JS SDK, либо вручную построив поток OAuth, либо используя библиотеку OAuth, например AppAuth .
Для каждого запроса на вход генерируйте случайную строку — «nonce» — которую вы будете использовать, чтобы убедиться, что полученный вами токен ID был выдан именно в ответ на запрос аутентификации вашего приложения. Этот шаг важен для предотвращения атак повторного воспроизведения.
Вы можете создать криптографически безопасный одноразовый код на Android с помощью
SecureRandom
, как в следующем примере: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(); }
Затем получите хэш SHA246 одноразового номера в виде шестнадцатеричной строки:
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(); }
Вы отправите хэш SHA256 одноразового значения вместе с запросом на вход, который Apple передаст без изменений в ответе. Firebase проверяет ответ, хешируя исходное одноразовое значение и сравнивая его со значением, переданным Apple.
Инициируйте процесс входа Apple, используя вашу библиотеку OAuth или другой метод. Обязательно включите хешированный одноразовый код в качестве параметра в ваш запрос.
Получив ответ от Apple, извлеките из ответа токен ID и используйте его вместе с нехешированным одноразовым кодом для создания
AuthCredential
:Kotlin
val credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build()
Java
AuthCredential credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build();
Выполните аутентификацию в Firebase, используя учетные данные 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. // ... } } });
Если вызов signInWithCredential
выполнен успешно, вы можете использовать метод getCurrentUser
для получения данных учетной записи пользователя.
Отзыв токена
Apple требует, чтобы приложения, поддерживающие создание учетных записей, позволяли пользователям инициировать удаление своих учетных записей внутри приложения, как описано в Руководстве по обзору App Store.
Кроме того, приложения, поддерживающие функцию «Вход с Apple», должны использовать API REST «Вход с Apple» для отзыва токенов пользователя.
Для выполнения этого требования выполните следующие шаги:
Используйте метод
startActivityForSignInWithProvider()
для входа с помощью Apple и полученияAuthResult
.Получите токен доступа для провайдера Apple.
Kotlin
val oauthCredential: OAuthCredential = authResult.credential val accessToken = oauthCredential.accessToken
Java
OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential(); String accessToken = oauthCredential.getAccessToken();
Отзовите токен с помощью 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 ... } } });
- Наконец, удалите учетную запись пользователя (и все связанные с ней данные).
Следующие шаги
После первого входа пользователя в систему создаётся новая учётная запись, которая привязывается к учётным данным, использованным при входе (имя пользователя и пароль, номер телефона или информация о поставщике аутентификации). Эта новая учётная запись хранится в вашем проекте Firebase и может использоваться для идентификации пользователя в каждом приложении проекта, независимо от способа входа.
В своих приложениях вы можете получить основную информацию о профиле пользователя из объекта
FirebaseUser
. См. раздел Управление пользователями .В правилах безопасности Firebase Realtime Database и Cloud Storage вы можете получить уникальный идентификатор вошедшего в систему пользователя из переменной
auth
и использовать его для управления данными, к которым пользователь может получить доступ.
Вы можете разрешить пользователям входить в ваше приложение с использованием нескольких поставщиков аутентификации, связав учетные данные поставщика аутентификации с существующей учетной записью пользователя.
Чтобы выйти из системы пользователя, вызовите
signOut
:Kotlin
Firebase.auth.signOut()
Java
FirebaseAuth.getInstance().signOut();