Аутентификация с помощью Apple на Android

Вы можете разрешить своим пользователям проходить аутентификацию в Firebase, используя их Apple ID, используя Firebase SDK для выполнения сквозного процесса входа OAuth 2.0.

Прежде чем вы начнете

Чтобы войти в систему с помощью Apple, сначала настройте «Вход через Apple» на сайте разработчика Apple, а затем включите Apple в качестве поставщика входа в систему для вашего проекта Firebase.

Присоединяйтесь к программе разработчиков Apple

Вход с Apple могут настроить только участники программы Apple Developer Program .

Настройте вход с помощью Apple

На сайте разработчика Apple выполните следующие действия:

  1. Свяжите свой веб-сайт со своим приложением, как описано в первом разделе статьи «Настройка входа с помощью Apple для Интернета» . При появлении запроса зарегистрируйте следующий URL-адрес в качестве URL-адреса возврата:

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

    Вы можете получить идентификатор проекта Firebase на странице настроек консоли Firebase .

    Когда вы закончите, запишите свой новый идентификатор службы, который вам понадобится в следующем разделе.

  2. Создайте вход с помощью закрытого ключа Apple . В следующем разделе вам понадобится новый закрытый ключ и идентификатор ключа.
  3. Если вы используете какие-либо функции Firebase Authentication, которые отправляют электронные письма пользователям, включая вход по ссылке электронной почты, проверку адреса электронной почты, отзыв изменения учетной записи и другие, настройте службу частной ретрансляции электронной почты Apple и зарегистрируйте noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (или ваш собственный домен шаблона электронной почты), чтобы Apple могла ретранслировать электронные письма, отправленные с помощью проверки подлинности Firebase, на анонимные адреса электронной почты Apple.

Включить Apple в качестве поставщика услуг входа

  1. Добавьте Firebase в свой проект Android . Обязательно зарегистрируйте подпись SHA-1 вашего приложения при настройке приложения в консоли Firebase.
  2. В консоли Firebase откройте раздел Auth . На вкладке «Метод входа» включите поставщика 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.

Управляйте процессом входа в систему с помощью Firebase SDK

На Android самый простой способ аутентификации ваших пользователей в Firebase с использованием их учетных записей Apple — это обработка всего процесса входа в систему с помощью Firebase Android SDK.

Чтобы обработать процесс входа с помощью Firebase Android SDK, выполните следующие действия:

  1. Создайте экземпляр OAuthProvider , используя его Builder с идентификатором поставщика apple.com :

    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);
    

    По умолчанию, когда включена одна учетная запись на адрес электронной почты , 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. Аутентификация в Firebase с использованием объекта провайдера OAuth. Обратите внимание, что в отличие от других операций FirebaseAuth , эта операция возьмет на себя управление вашим пользовательским интерфейсом, открыв специальную вкладку Chrome. Следовательно, не ссылайтесь на свою активность в OnSuccessListener и OnFailureListener , которые вы прикрепляете, поскольку они немедленно отсоединятся, когда операция запустит пользовательский интерфейс.

    Сначала вам следует проверить, получили ли вы уже ответ. При входе с помощью этого метода ваша активность переходит в фоновый режим, а это означает, что система может вернуть ее во время входа в систему. Чтобы убедиться, что вы не заставите пользователя повторить попытку, если это произойдет, вам следует проверить, присутствует ли уже результат.

    Чтобы проверить, есть ли ожидающий результат, вызовите 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() для привязки разных поставщиков удостоверений к существующим учетным записям.

Обратите внимание: 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.
        }
      }
    });

Дополнительно: обработка процесса входа вручную.

Вы также можете пройти аутентификацию в Firebase с помощью учетной записи Apple, обработав поток входа либо с помощью Apple Sign-In JS SDK, вручную создав поток OAuth, либо с помощью библиотеки OAuth, такой как AppAuth .

  1. Для каждого запроса на вход сгенерируйте случайную строку — «nonce», — которую вы будете использовать, чтобы убедиться, что полученный вами токен идентификатора был предоставлен именно в ответ на запрос аутентификации вашего приложения. Этот шаг важен для предотвращения атак повторного воспроизведения.

    Вы можете создать криптографически безопасный одноразовый номер на Android с помощью SecureRandom , как в следующем примере:

    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 nonce в виде шестнадцатеричной строки:

    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();
    }
    

    Вы отправите хеш SHA256 nonce вместе с запросом на вход, который Apple передаст в ответе без изменений. Firebase проверяет ответ, хэшируя исходный nonce и сравнивая его со значением, переданным Apple.

  2. Инициируйте процесс входа в систему Apple, используя библиотеку OAuth или другой метод. Обязательно включите хешированный nonce в качестве параметра в свой запрос.

  3. После получения ответа Apple получите из ответа токен идентификатора и используйте его и нехешированный одноразовый номер для создания 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 для получения данных учетной записи пользователя.

Отзыв токена

Apple требует, чтобы приложения, поддерживающие создание учетной записи, позволяли пользователям инициировать удаление своей учетной записи в приложении, как описано в Руководстве по проверке App Store.

Кроме того, приложения, поддерживающие вход с Apple, должны использовать API REST входа с Apple для отзыва токенов пользователя.

Чтобы удовлетворить этому требованию, выполните следующие шаги:

  1. Используйте метод startActivityForSignInWithProvider() для входа в систему с помощью Apple и получения AuthResult .

  2. Получите токен доступа для поставщика Apple.

    Kotlin+KTX

    val oauthCredential: OAuthCredential =  authResult.credential
    val accessToken = oauthCredential.accessToken
    

    Java

    OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential();
    String accessToken = oauthCredential.getAccessToken();
    
  3. Отмените токен с помощью API revokeAccessToken .

    Kotlin+KTX

    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 ...
              }
            }
      });
    
  1. Наконец, удалите учетную запись пользователя (и все связанные с ней данные).

    Следующие шаги

    После того, как пользователь входит в систему в первый раз, создается новая учетная запись пользователя, которая связывается с учетными данными (то есть именем пользователя и паролем, номером телефона или информацией поставщика аутентификации), с которыми пользователь вошел в систему. Эта новая учетная запись хранится как часть вашего проекта Firebase и может использоваться для идентификации пользователя в каждом приложении вашего проекта, независимо от того, как пользователь входит в систему.

    • В ваших приложениях вы можете получить базовую информацию профиля пользователя из объекта FirebaseUser . См. Управление пользователями .

    • В правилах безопасности базы данных реального времени и облачного хранилища Firebase вы можете получить уникальный идентификатор пользователя, вошедшего в систему, из переменной auth и использовать его для управления тем, к каким данным пользователь может получить доступ.

    Вы можете разрешить пользователям входить в ваше приложение с использованием нескольких поставщиков аутентификации, привязав учетные данные поставщика аутентификации к существующей учетной записи пользователя.

    Чтобы выйти из системы, вызовите signOut :

    Kotlin+KTX

    Firebase.auth.signOut()

    Java

    FirebaseAuth.getInstance().signOut();