Как выполнить аутентификацию с помощью OpenID Connect на устройстве Android

Если вы перешли на версию Firebase Authentication with Identity Platform, то можете аутентифицировать пользователей в Firebase с помощью любого поставщика, совместимого с OpenID Connect (OIDC). Это позволяет использовать поставщиков идентификационной информации, которые не поддерживаются Firebase изначально.

Подготовка

Чтобы пользователи могли входить в аккаунт с помощью поставщика OIDC, сначала нужно собрать у него следующую информацию:

  • Идентификатор клиента – уникальная строка, которая идентифицирует ваше приложение. Поставщик может назначить вам разные идентификаторы клиента для каждой поддерживаемой платформы. Это одно из значений утверждения aud в токенах идентификаторов, выпущенных вашим поставщиком.

  • Секретный код клиента. Секретная строка, которую поставщик использует, чтобы подтвердить право собственности на идентификатор клиента. Для каждого идентификатора клиента потребуется соответствующий секретный код клиента. (Это значение требуется, только если вы используете обработку кода авторизации, что настоятельно рекомендуется.)

  • Эмитент. Строка, идентифицирующая вашего поставщика. Это значение должно быть URL, который при добавлении к нему символа косой черты (/.well-known/openid-configuration) указывает на местоположение документа обнаружения OIDC поставщика. Например, если издатель – https://auth.example.com, документ обнаружения должен быть доступен по адресу https://auth.example.com/.well-known/openid-configuration.

Когда у вас будет вся необходимая информация, включите OpenID Connect в качестве поставщика услуг входа для проекта Firebase:

  1. Добавьте Firebase в проект для Android.

  2. Если вы ещё не перешли на версию Firebase Authentication with Identity Platform, сделайте это. Аутентификация OpenID Connect доступна только в проектах, обновленных до этой версии.

  3. В консоли Firebase выберите Безопасность > Аутентификация.

  4. На вкладке Способ входа нажмите Добавить нового поставщика и выберите OpenID Connect.

  5. Выберите, какой тип авторизации вы будете использовать: обработку кода авторизации или неявное предоставление разрешения.

    Если ваш поставщик поддерживает обработку кода, всегда используйте ее. Неявное предоставление доступа менее безопасно, поэтому его использование настоятельно не рекомендуется.

  6. Укажите название поставщика. Запишите сгенерированный идентификатор поставщика, например oidc.example-provider. Он понадобится вам, когда вы будете добавлять в приложение код для входа в аккаунт.

  7. Укажите идентификатор и секретный код клиента, а также строку издателя поставщика. Эти значения должны в точности совпадать с теми, которые вам предоставил поставщик.

  8. Сохраните изменения.

Как обрабатывать процесс входа с помощью Firebase SDK

Если вы создаете приложение для Android, самый простой способ аутентификации пользователей в Firebase с помощью поставщика OIDC – реализовать весь процесс входа с помощью Firebase Android SDK.

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

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

    Kotlin

    val providerBuilder = OAuthProvider.newBuilder("oidc.example-provider")

    Java

    OAuthProvider.Builder providerBuilder = OAuthProvider.newBuilder("oidc.example-provider");

  2. Необязательно. Укажите дополнительные специальные параметры OAuth, которые вы хотите отправить с запросом OAuth.

    Kotlin

    // Target specific email with login hint.
    providerBuilder.addCustomParameter("login_hint", "user@example.com")

    Java

    // Target specific email with login hint.
    providerBuilder.addCustomParameter("login_hint", "user@example.com");

    Узнайте у поставщика OIDC, какие параметры он поддерживает. Обратите внимание, что с помощью setCustomParameters() нельзя передавать параметры, обязательные для Firebase. Это параметры client_id, response_type, redirect_uri, state, scope и response_mode.

  3. Необязательно. Укажите дополнительные области действия OAuth 2.0, помимо базового профиля, которые вы хотите запросить у поставщика услуг аутентификации.

    Kotlin

    // Request read access to a user's email addresses.
    // This must be preconfigured in the app's API permissions.
    providerBuilder.scopes = listOf("mail.read", "calendars.read")

    Java

    // Request read access to a user's email addresses.
    // This must be preconfigured in the app's API permissions.
    List<String> scopes =
            new ArrayList<String>() {
                {
                    add("mail.read");
                    add("calendars.read");
                }
            };
    providerBuilder.setScopes(scopes);

    Узнайте у поставщика OIDC, какие области он использует.

  4. Выполните аутентификацию в Firebase, используя объект поставщика OAuth. Обратите внимание, что в отличие от других операций FirebaseAuth, эта операция будет управлять вашим интерфейсом, открывая вкладку Custom Chrome Tab. Поэтому не ссылайтесь на Activity в OnSuccessListener и OnFailureListener, которые вы прикрепляете, поскольку они будут немедленно откреплены, когда операция запустит интерфейс.

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

    Чтобы проверить, есть ли ожидающий результат, вызовите функцию getPendingAuthResult:

    Kotlin

    val pendingResultTask = firebaseAuth.pendingAuthResult
    if (pendingResultTask != null) {
        // There's something already here! Finish the sign-in for your user.
        pendingResultTask
            .addOnSuccessListener {
                // User is signed in.
                // IdP data available in
                // authResult.getAdditionalUserInfo().getProfile().
                // The OAuth access token can also be retrieved:
                // ((OAuthCredential)authResult.getCredential()).getAccessToken().
                // The OAuth secret can be retrieved by calling:
                // ((OAuthCredential)authResult.getCredential()).getSecret().
            }
            .addOnFailureListener {
                // Handle failure.
            }
    } else {
        // There's no pending result so you need to start the sign-in flow.
        // See below.
    }

    Java

    Task<AuthResult> pendingResultTask = firebaseAuth.getPendingAuthResult();
    if (pendingResultTask != null) {
        // There's something already here! Finish the sign-in for your user.
        pendingResultTask
                .addOnSuccessListener(
                        new OnSuccessListener<AuthResult>() {
                            @Override
                            public void onSuccess(AuthResult authResult) {
                                // User is signed in.
                                // IdP data available in
                                // authResult.getAdditionalUserInfo().getProfile().
                                // The OAuth access token can also be retrieved:
                                // ((OAuthCredential)authResult.getCredential()).getAccessToken().
                                // The OAuth secret can be retrieved by calling:
                                // ((OAuthCredential)authResult.getCredential()).getSecret().
                            }
                        })
                .addOnFailureListener(
                        new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                // Handle failure.
                            }
                        });
    } else {
        // There's no pending result so you need to start the sign-in flow.
        // See below.
    }

    Чтобы начать процесс входа, вызовите startActivityForSignInWithProvider:

    Kotlin

    firebaseAuth
        .startActivityForSignInWithProvider(activity, provider.build())
        .addOnSuccessListener {
            // User is signed in.
            // IdP data available in
            // authResult.getAdditionalUserInfo().getProfile().
            // The OAuth access token can also be retrieved:
            // ((OAuthCredential)authResult.getCredential()).getAccessToken().
            // The OAuth secret can be retrieved by calling:
            // ((OAuthCredential)authResult.getCredential()).getSecret().
        }
        .addOnFailureListener {
            // Handle failure.
        }

    Java

    firebaseAuth
            .startActivityForSignInWithProvider(/* activity= */ this, provider.build())
            .addOnSuccessListener(
                    new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            // User is signed in.
                            // IdP data available in
                            // authResult.getAdditionalUserInfo().getProfile().
                            // The OAuth access token can also be retrieved:
                            // ((OAuthCredential)authResult.getCredential()).getAccessToken().
                            // The OAuth secret can be retrieved by calling:
                            // ((OAuthCredential)authResult.getCredential()).getSecret().
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Handle failure.
                        }
                    });

  5. В приведенных выше примерах основное внимание уделяется процессам входа в систему, но вы также можете связать поставщика OIDC с существующим пользователем, используя startActivityForLinkWithProvider. Например, вы можете связать несколько поставщиков с одним пользователем, чтобы он мог входить в систему с помощью любого из них.

    Kotlin

    // The user is already signed-in.
    val firebaseUser = firebaseAuth.currentUser!!
    firebaseUser
        .startActivityForLinkWithProvider(activity, provider.build())
        .addOnSuccessListener {
            // Provider credential is linked to the current user.
            // IdP data available in
            // authResult.getAdditionalUserInfo().getProfile().
            // The OAuth access token can also be retrieved:
            // authResult.getCredential().getAccessToken().
            // The OAuth secret can be retrieved by calling:
            // authResult.getCredential().getSecret().
        }
        .addOnFailureListener {
            // Handle failure.
        }

    Java

    // The user is already signed-in.
    FirebaseUser firebaseUser = firebaseAuth.getCurrentUser();
    
    firebaseUser
            .startActivityForLinkWithProvider(/* activity= */ this, provider.build())
            .addOnSuccessListener(
                    new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            // Provider credential is linked to the current user.
                            // IdP data available in
                            // authResult.getAdditionalUserInfo().getProfile().
                            // The OAuth access token can also be retrieved:
                            // authResult.getCredential().getAccessToken().
                            // The OAuth secret can be retrieved by calling:
                            // authResult.getCredential().getSecret().
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Handle failure.
                        }
                    });

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

    Kotlin

    // The user is already signed-in.
    val firebaseUser = firebaseAuth.currentUser!!
    firebaseUser
        .startActivityForReauthenticateWithProvider(activity, provider.build())
        .addOnSuccessListener {
            // User is re-authenticated with fresh tokens and
            // should be able to perform sensitive operations
            // like account deletion and email or password
            // update.
        }
        .addOnFailureListener {
            // Handle failure.
        }

    Java

    // The user is already signed-in.
    FirebaseUser firebaseUser = firebaseAuth.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.
                        }
                    });

Как обрабатывать процесс входа вручную

Если вы уже реализовали в приложении процесс входа через OpenID Connect, то можете использовать токен идентификатора напрямую для аутентификации в Firebase:

Kotlin

val providerId = "oidc.example-provider" // As registered in Firebase console.
val credential = oAuthCredential(providerId) {
    setIdToken(idToken) // ID token from OpenID Connect flow.
}
Firebase.auth
    .signInWithCredential(credential)
    .addOnSuccessListener { authResult ->
        // User is signed in.

        // IdP data available in:
        //    authResult.additionalUserInfo.profile
    }
    .addOnFailureListener { e ->
        // Handle failure.
    }

Java

AuthCredential credential = OAuthProvider
        .newCredentialBuilder("oidc.example-provider")  // As registered in Firebase console.
        .setIdToken(idToken)  // ID token from OpenID Connect flow.
        .build();
FirebaseAuth.getInstance()
        .signInWithCredential(credential)
        .addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                // User is signed in.

                // IdP data available in:
                //    authResult.getAdditionalUserInfo().getProfile()
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Handle failure.
            }
        });

Дальнейшие действия

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

  • В своих приложениях вы можете получить основную информацию профиля пользователя из объекта FirebaseUser. Подробнее о том, как управлять пользователями…

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

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

Чтобы выйти из аккаунта пользователя, вызовите функцию signOut:

Kotlin

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();