Аутентификация с помощью Firebase с использованием ссылки электронной почты в Android

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

Вход по электронной почте имеет множество преимуществ:

  • Регистрация и вход с низким коэффициентом трения.
  • Меньший риск повторного использования пароля в разных приложениях, что может подорвать безопасность даже хорошо подобранных паролей.
  • Возможность аутентифицировать пользователя, а также подтверждать, что пользователь является законным владельцем адреса электронной почты.
  • Пользователю нужна только доступная учетная запись электронной почты для входа в систему. Владение номером телефона или учетной записью в социальных сетях не требуется.
  • Пользователь может безопасно войти в систему без необходимости вводить (или запоминать) пароль, что может быть громоздко на мобильном устройстве.
  • Существующий пользователь, который ранее входил в систему с идентификатором электронной почты (пароль или федерация), может быть обновлен, чтобы входить только с электронной почтой. Например, пользователь, который забыл свой пароль, все еще может войти в систему, не сбрасывая пароль.

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

Настройте свой Android-проект

  1. Если вы еще этого не сделали, добавьте Firebase в свой проект Android .

  2. Используя Firebase Android BoM , объявите зависимость для библиотеки Firebase Authentication Android в файле Gradle вашего модуля (на уровне приложения) (обычно app/build.gradle ).

    Кроме того, в рамках настройки аутентификации Firebase вам необходимо добавить SDK сервисов Google Play в свое приложение.

    Java

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:30.3.1')
    
        // Declare the dependency for the Firebase Authentication library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth'
    // Also declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.2.0'
    }

    Используя Firebase Android BoM , ваше приложение всегда будет использовать совместимые версии библиотек Firebase Android.

    (Альтернатива) Объявите зависимости библиотеки Firebase без использования BoM

    Если вы решите не использовать Firebase BoM, вы должны указать каждую версию библиотеки Firebase в строке зависимостей.

    Обратите внимание: если вы используете несколько библиотек Firebase в своем приложении, мы настоятельно рекомендуем использовать BoM для управления версиями библиотек, что гарантирует совместимость всех версий.

    dependencies {
        // Declare the dependency for the Firebase Authentication library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth:21.0.7'
    // Also declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.2.0'
    }

    Kotlin+KTX

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:30.3.1')
    
        // Declare the dependency for the Firebase Authentication library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth-ktx'
    // Also declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.2.0'
    }

    Используя Firebase Android BoM , ваше приложение всегда будет использовать совместимые версии библиотек Firebase Android.

    (Альтернатива) Объявите зависимости библиотеки Firebase без использования BoM

    Если вы решите не использовать Firebase BoM, вы должны указать каждую версию библиотеки Firebase в строке зависимостей.

    Обратите внимание: если вы используете несколько библиотек Firebase в своем приложении, мы настоятельно рекомендуем использовать BoM для управления версиями библиотек, что гарантирует совместимость всех версий.

    dependencies {
        // Declare the dependency for the Firebase Authentication library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth-ktx:21.0.7'
    // Also declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.2.0'
    }

Чтобы входить в систему по ссылке электронной почты, вы должны сначала включить поставщика электронной почты и метод входа по ссылке электронной почты для своего проекта Firebase:

  1. В консоли Firebase откройте раздел Auth .
  2. На вкладке « Метод входа » включите поставщика электронной почты/пароля . Обратите внимание, что для входа по электронной почте необходимо включить вход по электронной почте/паролю.
  3. В том же разделе включите метод входа по ссылке электронной почты (вход без пароля) .
  4. Нажмите Сохранить .

Чтобы инициировать процесс аутентификации, предоставьте пользователю интерфейс, который предлагает пользователю указать свой адрес электронной почты, а затем вызовите sendSignInLinkToEmail , чтобы запросить, чтобы Firebase отправила ссылку для аутентификации на электронную почту пользователя.

  1. Создайте объект ActionCodeSettings , который предоставит Firebase инструкции по созданию ссылки электронной почты. Задайте следующие поля:

    • url : глубокая ссылка для встраивания и любое дополнительное состояние, которое необходимо передать. Домен ссылки должен быть внесен в белый список в списке авторизованных доменов Firebase Console, который можно найти, перейдя на вкладку «Метод входа» («Аутентификация» -> «Метод входа»). Ссылка перенаправит пользователя на этот URL-адрес, если приложение не установлено на его устройстве и установить его не удалось.
    • androidPackageName и IOSBundleId : приложения, используемые при открытии ссылки для входа на устройстве Android или Apple. Узнайте больше о том, как настроить динамические ссылки Firebase для открытия ссылок действий в электронной почте через мобильные приложения.
    • handleCodeInApp : установите значение true. Операция входа всегда должна выполняться в приложении, в отличие от других внеплановых действий с электронной почтой (сброс пароля и проверка электронной почты). Это связано с тем, что в конце потока ожидается, что пользователь войдет в систему, а его состояние аутентификации сохранится в приложении.
    • dynamicLinkDomain : если для проекта определено несколько настраиваемых доменов динамической ссылки, укажите, какой из них использовать, когда ссылка должна быть открыта через указанное мобильное приложение (например, example.page.link ). В противном случае автоматически выбирается первый домен.

    Java

    ActionCodeSettings actionCodeSettings =
            ActionCodeSettings.newBuilder()
                    // URL you want to redirect back to. The domain (www.example.com) for this
                    // URL must be whitelisted in the Firebase Console.
                    .setUrl("https://www.example.com/finishSignUp?cartId=1234")
                    // This must be true
                    .setHandleCodeInApp(true)
                    .setIOSBundleId("com.example.ios")
                    .setAndroidPackageName(
                            "com.example.android",
                            true, /* installIfNotAvailable */
                            "12"    /* minimumVersion */)
                    .build();

    Kotlin+KTX

    val actionCodeSettings = actionCodeSettings {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be whitelisted in the Firebase Console.
        url = "https://www.example.com/finishSignUp?cartId=1234"
        // This must be true
        handleCodeInApp = true
        setIOSBundleId("com.example.ios")
        setAndroidPackageName(
                "com.example.android",
                true, /* installIfNotAvailable */
                "12" /* minimumVersion */)
    }

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

  2. Спросите у пользователя адрес электронной почты.

  3. Отправьте ссылку для проверки подлинности на электронную почту пользователя и сохраните электронную почту пользователя на случай, если пользователь завершит вход по электронной почте на том же устройстве.

    Java

    FirebaseAuth auth = FirebaseAuth.getInstance();
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Email sent.");
                    }
                }
            });

    Kotlin+KTX

    Firebase.auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Email sent.")
                }
            }

Вопросы безопасности

Чтобы предотвратить использование ссылки для входа в качестве непреднамеренного пользователя или на непредусмотренном устройстве, Firebase Auth требует предоставления адреса электронной почты пользователя при завершении процесса входа. Для успешного входа этот адрес электронной почты должен совпадать с адресом, на который изначально была отправлена ​​ссылка для входа.

Вы можете оптимизировать этот процесс для пользователей, которые открывают ссылку для входа на том же устройстве, на котором они запрашивают ссылку, сохраняя их адрес электронной почты локально (например, с помощью SharedPreferences) при отправке сообщения электронной почты для входа. Затем используйте этот адрес для завершения потока. Не передавайте адрес электронной почты пользователя в параметрах URL-адреса перенаправления и не используйте его повторно, так как это может привести к внедрению сеансовых инъекций.

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

Также убедитесь, что вы используете URL-адрес HTTPS в рабочей среде, чтобы избежать потенциального перехвата вашей ссылки промежуточными серверами.

Завершение входа в приложение для Android

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

Firebase Auth использует динамические ссылки Firebase при отправке ссылки, которая предназначена для открытия в мобильном приложении. Чтобы использовать эту функцию, динамические ссылки должны быть настроены в консоли Firebase.

  1. Включить динамические ссылки Firebase:

    1. В консоли Firebase откройте раздел Dynamic Links .
    2. Если вы еще не приняли условия динамических ссылок и не создали домен динамических ссылок, сделайте это сейчас.

      Если вы уже создали домен динамических ссылок, обратите на это внимание. Домен динамических ссылок обычно выглядит следующим образом:

      example.page.link

      Это значение понадобится вам при настройке приложения Apple или Android для перехвата входящей ссылки.

  2. Настройка Android-приложений:

    1. Чтобы обрабатывать эти ссылки из вашего приложения для Android, необходимо указать имя пакета Android в настройках проекта Firebase Console. Кроме того, необходимо предоставить SHA-1 и SHA-256 сертификата приложения.
    2. Теперь, когда вы добавили домен динамической ссылки и убедились, что ваше приложение для Android настроено правильно, динамическая ссылка будет перенаправлять на ваше приложение, начиная с действия средства запуска.
    3. Если вы хотите, чтобы динамическая ссылка перенаправляла на определенное действие, вам необходимо настроить фильтр намерений в файле AndroidManifest.xml . Это можно сделать, указав домен динамической ссылки или обработчик действий электронной почты в фильтре намерений. По умолчанию обработчик действий электронной почты размещается в домене, как показано в следующем примере:
      PROJECT_ID.firebaseapp.com/
      .
    4. Предостережения:
      1. Не указывайте URL-адрес, который вы установили в actionCodeSettings в своем фильтре намерений.
      2. При создании домена с динамической ссылкой вы также могли создать короткую URL-ссылку. Этот короткий URL-адрес не будет передан; не настраивайте фильтр намерений так, чтобы он перехватывал его с помощью атрибута android:pathPrefix . Это означает, что вы не сможете поймать разные динамические ссылки в разных частях вашего приложения. Однако вы можете проверить параметр запроса mode в ссылке, чтобы увидеть, какую операцию пытаются выполнить, или использовать методы SDK, такие как isSignInWithEmailLink , чтобы узнать, делает ли ссылка, полученная вашим приложением, то, что вам нужно.
    5. Дополнительные сведения о получении динамических ссылок см. в инструкциях по получению динамических ссылок Android .

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

Java

FirebaseAuth auth = FirebaseAuth.getInstance();
Intent intent = getIntent();
String emailLink = intent.getData().toString();

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    String email = "someemail@domain.com";

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Successfully signed in with email link!");
                        AuthResult result = task.getResult();
                        // You can access the new user via result.getUser()
                        // Additional user info profile *not* available via:
                        // result.getAdditionalUserInfo().getProfile() == null
                        // You can check if the user is new or existing:
                        // result.getAdditionalUserInfo().isNewUser()
                    } else {
                        Log.e(TAG, "Error signing in with email link", task.getException());
                    }
                }
            });
}

Kotlin+KTX

val auth = Firebase.auth
val intent = intent
val emailLink = intent.data.toString()

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    val email = "someemail@domain.com"

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Successfully signed in with email link!")
                    val result = task.result
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error signing in with email link", task.exception)
                }
            }
}

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

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

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

Разница будет во второй половине операции:

Java

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Link the credential to the current user.
auth.getCurrentUser().linkWithCredential(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Successfully linked emailLink credential!");
                    AuthResult result = task.getResult();
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error linking emailLink credential", task.getException());
                }
            }
        });

Kotlin+KTX

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Link the credential to the current user.
Firebase.auth.currentUser!!.linkWithCredential(credential)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Successfully linked emailLink credential!")
                val result = task.result
                // You can access the new user via result.getUser()
                // Additional user info profile *not* available via:
                // result.getAdditionalUserInfo().getProfile() == null
                // You can check if the user is new or existing:
                // result.getAdditionalUserInfo().isNewUser()
            } else {
                Log.e(TAG, "Error linking emailLink credential", task.exception)
            }
        }

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

Java

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Re-authenticate the user with this credential.
auth.getCurrentUser().reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // User is now successfully reauthenticated
                } else {
                    Log.e(TAG, "Error reauthenticating", task.getException());
                }
            }
        });

Kotlin+KTX

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Re-authenticate the user with this credential.
Firebase.auth.currentUser!!.reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // User is now successfully reauthenticated
            } else {
                Log.e(TAG, "Error reauthenticating", task.exception)
            }
        }

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

Если вы поддерживаете вход по электронной почте как по паролю, так и по ссылке, чтобы различать метод входа для пользователя по паролю/ссылке, используйте fetchSignInMethodsForEmail . Это полезно для потоков с идентификатором в первую очередь, когда пользователя сначала просят указать адрес электронной почты, а затем предлагают метод входа:

Java

auth.fetchSignInMethodsForEmail(email)
        .addOnCompleteListener(new OnCompleteListener<SignInMethodQueryResult>() {
            @Override
            public void onComplete(@NonNull Task<SignInMethodQueryResult> task) {
                if (task.isSuccessful()) {
                    SignInMethodQueryResult result = task.getResult();
                    List<String> signInMethods = result.getSignInMethods();
                    if (signInMethods.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)) {
                        // User can sign in with email/password
                    } else if (signInMethods.contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
                        // User can sign in with email/link
                    }
                } else {
                    Log.e(TAG, "Error getting sign in methods for user", task.getException());
                }
            }
        });

Kotlin+KTX

Firebase.auth.fetchSignInMethodsForEmail(email)
        .addOnSuccessListener { result ->
            val signInMethods = result.signInMethods!!
            if (signInMethods.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)) {
                // User can sign in with email/password
            } else if (signInMethods.contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
                // User can sign in with email/link
            }
        }
        .addOnFailureListener { exception ->
            Log.e(TAG, "Error getting sign in methods for user", exception)
        }

Как описано выше, адрес электронной почты/пароль и адрес электронной почты/ссылка считаются одним и тем же EmailAuthProvider (один и тот же PROVIDER_ID ) с разными методами входа.

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

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

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

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

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

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

Java

FirebaseAuth.getInstance().signOut();

Kotlin+KTX

Firebase.auth.signOut()