Пользователь Firebase: isEmailVerified возвращает false

# #android #firebase #kotlin #firebase-authentication

Вопрос:

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

 Firebase.auth.currentUser?.let {user ->
        user.sendEmailVerification().addOnCompleteListener {verificationTask ->
            if(verificationTask.isSuccessful){
                _verificationSuccess.value = true
            } else {
                _verificationError.value = verificationTask.exception
            }
        }
    }
 

В моем манифесте я добавил фильтр намерений, который открывает мое приложение, когда пользователь пытается открыть ссылку для проверки.

 <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data
                    android:host="test-project.firebaseapp.com"
                    android:scheme="https"/>
            </intent-filter>
 

Проблема в том, что, когда ссылка открывает мое приложение, электронная почта не проверяется. У меня есть эта проверка, которая всегда возвращает false

 if(Firebase.auth.currentUser.isEmailVerified){
                        //Email verified
                    } else {
                        throw Exception("Email not verified")
                    }
 

Только когда ссылка для проверки открыта в веб-обозревателе, проверка возвращает значение true. Как я могу проверить электронную почту в своем приложении?

Ответ №1:

Применив фильтр намерений, который у вас есть, вы сообщили Android, что будете обрабатывать все целевые веб-запросы https://test-project.firebaseapp.com . Теперь, когда ваше приложение заблокировало просмотр по этому URL-адресу, теперь вам нужно сделать запрос на сервер от имени пользователя.

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

 https://<PROJECT_ID>.firebaseapp.com/__/auth/action?mode=verifyEmailamp;oobCode=<VERIFICATION_CODE>amp;apiKey=<API_KEY>amp;lang=<LANGUAGE>
 

Примечание .Эти ссылки можно настроить с помощью пользовательских обработчиков действий или использовать динамические ссылки, чтобы не перехватывать все, к чему вы стремитесь https://test-project.firebaseapp.com .

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

 <intent-filter android:label="@string/filter_view_handle_action_code">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https"
          android:host="test-project.firebaseapp.com"
          android:pathPrefix="/__/auth/action" />
</intent-filter>
 

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

Если вы перехватываете такой URL-адрес, вам необходимо обработать его в onCreate методе:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent intent = this.getIntent();
    if (intent.hasCategory(android.intent.category.BROWSABLE)) {
        // intent came from browser
        Uri data = intent.getData();
        String mode = data.getQueryParameter("mode");
        String oobCode = data.getQueryParameter("oobCode");
        if (mode != "verifyEmail" || oobCode == null) {
            Log.i("MyActivity", "Started with unexpected browsable intent");
            return;
        }

        handleVerifyEmailActionCode(oobCode);
    }
}

private handleVerifyEmailActionCode(String oobCode) {
    String email = null;
    FirebaseAuth.getInstance().checkActionCode(oobCode)
        .onSuccessTask( actionCodeResult -> {
            if (actionCodeResult.getOperation() != ActionCodeResult.VERIFY_EMAIL) {
                throw new UnsupportedActionCodeOperationException(actionCodeResult.getOperation());
            }

            email = actionCodeResult.getInfo().getEmail();
            return FirebaseAuth.getInstance().applyActionCode(oobCode);
        })
        .addOnCompleteListener( applyActionCodeTask -> {
            if (!applyActionCodeTask.isSuccessful()) {
                Exception ex = applyActionCodeTask.getException();
                // TODO: Handle exceptions
                Toast.makeText(getApplicationContext(), "Failed to verify email. "   ex.getMessage(), Toast.LENGTH_SHORT).show();
                return;
            }

            // email verified successfully!
            // do something interesting
            Toast.makeText(getApplicationContext(), email   " was verified!", Toast.LENGTH_SHORT).show();
            FirebaseUser user = FirebaseAuth.getCurrentUser(); // WARN: May not have finished initializing yet?
            if (user != null amp;amp; user.getEmail() == email) {
                user.reload()
                    .addOnFailureListener(ex -> {
                        Log.e(MyActivity.class, "Failed to refresh user token: "   ex.getMessage());
                    });
            } else {
                Log.i(
                    MyActivity.class,
                    user == null
                        ? "User wasn't signed in, skipping reload"
                        : "User email mismatch, skipping reload"
                );
            }
    });
}
 
 /**
 * Custom exception to be used when a OOB code's `ActionCodeResult.Operation`
 * doesn't match what it was expected to be.
 * 
 * @see https://firebase.google.com/docs/reference/android/com/google/firebase/auth/ActionCodeResult#constant-summary
 */
public class UnsupportedActionCodeOperationException extends Exception { 
    protected int operation;

    public UnsupportedActionCodeOperationException(int operation) {
        super("The ActionCodeResult.Operation value of "   operation   " is not supported");
        this.operation = operation;
    }

    public int getOperation() {
        return this.operation;
    }
}