如何捕获 Firebase Auth 特定的异常

How to catch a Firebase Auth specific exceptions

如何使用 Firebase 捕获特定异常并优雅地告诉用户?例如:

FirebaseAuthInvalidCredentialsException: The email address is badly formatted.

我正在使用下面的代码使用电子邮件和密码注册用户,但我在 java 方面并不那么先进。

mAuth.createUserWithEmailAndPassword(email, pwd)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {

    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        if (!task.isSuccessful()) {
            //H.toast(c, task.getException().getMessage());
            Log.e("Signup Error", "onCancelled", task.getException());
        } else {
            FirebaseUser user = mAuth.getCurrentUser();
            String uid = user.getUid();
        }
    }    
});

您应该使用 ((FirebaseAuthException)task.getException()).getErrorCode() 来获取错误类型,如果这是错误格式电子邮件的错误代码,您应该优雅地失败。

很遗憾,我找不到 Firebase 使用的错误代码列表。 触发一次异常,记下错误代码和相应的代码。

如果您要从用户向云端发送上游消息,请实施 firebase 回调函数 onMessageSentonSendError 以检查上游消息的状态。在错误情况下,onSendError returns 带有错误代码的 SendException

例如,如果客户端在达到 20 条消息限制后尝试发送更多消息,它 returns SendException#ERROR_TOO_MANY_MESSAGES.

除了@pdegand59 的回答,我在 Firebase 库中发现了一些错误代码,并在 Android(返回的错误代码)上进行了测试。希望这对您有所帮助,问候。

 ("ERROR_INVALID_CUSTOM_TOKEN", "The custom token format is incorrect. Please check the documentation."));
 ("ERROR_CUSTOM_TOKEN_MISMATCH", "The custom token corresponds to a different audience."));
 ("ERROR_INVALID_CREDENTIAL", "The supplied auth credential is malformed or has expired."));
 ("ERROR_INVALID_EMAIL", "The email address is badly formatted."));
 ("ERROR_WRONG_PASSWORD", "The password is invalid or the user does not have a password."));
 ("ERROR_USER_MISMATCH", "The supplied credentials do not correspond to the previously signed in user."));
 ("ERROR_REQUIRES_RECENT_LOGIN", "This operation is sensitive and requires recent authentication. Log in again before retrying this request."));
 ("ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL", "An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address."));
 ("ERROR_EMAIL_ALREADY_IN_USE", "The email address is already in use by another account."));
 ("ERROR_CREDENTIAL_ALREADY_IN_USE", "This credential is already associated with a different user account."));
 ("ERROR_USER_DISABLED", "The user account has been disabled by an administrator."));
 ("ERROR_USER_TOKEN_EXPIRED", "The user\'s credential is no longer valid. The user must sign in again."));
 ("ERROR_USER_NOT_FOUND", "There is no user record corresponding to this identifier. The user may have been deleted."));
 ("ERROR_INVALID_USER_TOKEN", "The user\'s credential is no longer valid. The user must sign in again."));
 ("ERROR_OPERATION_NOT_ALLOWED", "This operation is not allowed. You must enable this service in the console."));
 ("ERROR_WEAK_PASSWORD", "The given password is invalid."));
 ("ERROR_MISSING_EMAIL", "An email address must be provided.";

您可以在 try 块中抛出 task.getException 返回的异常,并捕获您正在使用的方法可能抛出的每种类型的异常。

这是 OnCompleteListenercreateUserWithEmailAndPassword 方法的示例。

if(!task.isSuccessful()) {
    try {
        throw task.getException();
    } catch(FirebaseAuthWeakPasswordException e) {
        mTxtPassword.setError(getString(R.string.error_weak_password));
        mTxtPassword.requestFocus();
    } catch(FirebaseAuthInvalidCredentialsException e) {
        mTxtEmail.setError(getString(R.string.error_invalid_email));
        mTxtEmail.requestFocus();
    } catch(FirebaseAuthUserCollisionException e) {
        mTxtEmail.setError(getString(R.string.error_user_exists));
        mTxtEmail.requestFocus();
    } catch(Exception e) {
        Log.e(TAG, e.getMessage());
    }
}

您可以使用 steve-guidetti 或 pdegand59 方法。我用的是steve-guidetti的方法(少了两个例外)

对于所有可能的例外情况,请在下面找到参考。

这里有详细的记录。

https://firebase.google.com/docs/reference/js/firebase.auth.Auth

搜索 "createUserWithEmailAndPassword" 并找到

Error Codes

auth/email-already-in-use

Thrown if there already exists an account with the given email address. 

auth/invalid-email

Thrown if the email address is not valid.

auth/operation-not-allowed

Thrown if email/password accounts are not enabled. Enable email/password accounts in the Firebase Console, under the Auth tab.

auth/weak-password

Thrown if the password is not strong enough. 

对于所有五个例外情况:请在此处查看

https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseAuthException

在这里您可以找到 5 种不同类型的 AuthException。 4 个已知的直接子类和 1 个间接子类

您可以使用 steve-guidetti 或 pdegand59 方法。

我尝试了其他解决方案,但不喜欢它们。

这个怎么样:

if (!task.isSuccessful()) {

    Exception exc = task.getException();

    if (exc.getMessage().contains("The email address is badly formatted.")) {
        etUser.setError(getString(R.string.error_wrong_email));
        etUser.requestFocus();
    }
    else
    if (exc.getMessage().contains("There is no user record corresponding to this identifier. The user may have been deleted.")) {
        etUser.setError(getString(R.string.error_user_not_exist));
        etUser.requestFocus();
    }
    else
    if (exc.getMessage().contains("The password is invalid or the user does not have a password")) {
        etPass.setError(getString(R.string.error_wrong_password));
        etPass.requestFocus();
    }


    Log.w(TAG, "signInWithEmail:failed", task.getException());


    Toast.makeText(AuthActivity.this, R.string.auth_failed,
            Toast.LENGTH_SHORT).show();
}

如果您只是想向用户显示一条消息,这可行。简洁大方:

if (!task.isSuccessful()) {
    Log.w(TAG, "signInWithEmail:failed", task.getException());
    Toast.makeText(LoginActivity.this, "User Authentication Failed: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}

似乎 .getMessage() 方法已经将异常转换为我们可用的格式,我们所要做的就是在某处向用户显示。

(第一次发表评论,请提出建设性意见)

    try {
            throw task.getException();
        } catch(FirebaseAuthException e) {
           switch (e.getErrorCode()){
                        case "ERROR_WEAK_PASSWORD":
                      Toast.makeText(this, "The given password is invalid.", Toast.LENGTH_SHORT).show();
                             break;
                      //and other
                    }
        }

错误代码:

捕获 firebase 异常很容易,您应该在添加 .addOnCompleteListener 之后添加 .addOnFailureListener,如下所示:

 private void login_user(String email, String password) {

    mAuth.signInWithEmailAndPassword(email,password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
        @Override
        public void onComplete(@NonNull Task<AuthResult> task) {
           if(task.isSuccessful()){
               Intent intent = new Intent(getApplicationContext(),MainActivity.class);
               startActivity(intent);
               finish();
           }if(!task.isSuccessful()){


                // To know The Excepton 
                //Toast.makeText(LoginActivity.this, ""+task.getException(), Toast.LENGTH_LONG).show();

           }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            if( e instanceof FirebaseAuthInvalidUserException){
                Toast.makeText(LoginActivity.this, "This User Not Found , Create A New Account", Toast.LENGTH_SHORT).show();
            }
            if( e instanceof FirebaseAuthInvalidCredentialsException){
                Toast.makeText(LoginActivity.this, "The Password Is Invalid, Please Try Valid Password", Toast.LENGTH_SHORT).show();
            }
            if(e instanceof FirebaseNetworkException){
                Toast.makeText(LoginActivity.this, "Please Check Your Connection", Toast.LENGTH_SHORT).show();
            }
        }
    });

LOGIN_EXCEPTIONS

FirebaseAuthException - 与 Firebase 身份验证相关的一般异常。查看错误代码和消息以获取更多详细信息。

ERROR_USER_DISABLED 如果用户已被禁用(例如,在 Firebase 控制台中)

ERROR_USER_NOT_FOUND 如果用户已被删除(例如,在 Firebase 控制台中,或在此应用程序的另一个实例中)

ERROR_USER_TOKEN_EXPIRED 如果用户的令牌已在后端被撤销。如果用户的凭据在另一台设备上发生更改(例如,密码更改事件),则会自动发生这种情况。

ERROR_INVALID_USER_TOKEN 如果用户的令牌格式不正确。这在正常情况下不应该发生。

mAuth.signInWithEmailAndPassword(login, pass)
  .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
        @Override
        public void onComplete(@NonNull Task<AuthResult> task) {
          if(task.isSuccessful())
            {

            }else if (task.getException() instanceof FirebaseAuthInvalidUserException) {

            }else if(((FirebaseAuthException) task.getException()).getErrorCode().equals("ERROR_USER_DISABLED"))
            {

           }else if(((FirebaseAuthException) task.getException()).getErrorCode().equals("ERROR_USER_NOT_FOUND "))
          {

          }else if(((FirebaseAuthException) task.getException()).getErrorCode().equals("ERROR_USER_TOKEN_EXPIRED "))
         {

         }else if(((FirebaseAuthException) task.getException()).getErrorCode().equals("ERROR_INVALID_USER_TOKEN "))
         {
         }
 }
});

REGISTER_EXCEPTIONS

FirebaseAuthEmailException

表示尝试通过 Firebase Auth 发送电子邮件(例如密码重置电子邮件)导致的异常

FirebaseAuthInvalidCredentialsException - 当传递给方法的一个或多个凭据无法识别 and/or 验证该操作的用户主体时抛出。检查错误代码和消息以找出具体原因。

FirebaseAuthWeakPasswordException - 使用弱密码(少于 6 个字符)创建新帐户或更新现有帐户的密码时抛出。使用 getReason() 获取一条消息,其中包含您可以向用户显示的验证失败原因。

有许多与 firebase 身份验证相关的例外情况。 除了@kingspeech

您应该使用 ((FirebaseAuthException)task.getException()).getErrorCode() 获取 类型的错误 ,然后在 switch 中像这样处理它:

private void loginUser(String email, String password) {

        mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {

            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {

                if (task.isSuccessful()) {

                    startActivity(new Intent(MainActivity.this, Main2Activity.class));

                } else {

                    String errorCode = ((FirebaseAuthException) task.getException()).getErrorCode();

                    switch (errorCode) {

                        case "ERROR_INVALID_CUSTOM_TOKEN":
                            Toast.makeText(MainActivity.this, "The custom token format is incorrect. Please check the documentation.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_CUSTOM_TOKEN_MISMATCH":
                            Toast.makeText(MainActivity.this, "The custom token corresponds to a different audience.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_INVALID_CREDENTIAL":
                            Toast.makeText(MainActivity.this, "The supplied auth credential is malformed or has expired.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_INVALID_EMAIL":
                            Toast.makeText(MainActivity.this, "The email address is badly formatted.", Toast.LENGTH_LONG).show();
                            etEmail.setError("The email address is badly formatted.");
                            etEmail.requestFocus();
                            break;

                        case "ERROR_WRONG_PASSWORD":
                            Toast.makeText(MainActivity.this, "The password is invalid or the user does not have a password.", Toast.LENGTH_LONG).show();
                            etPassword.setError("password is incorrect ");
                            etPassword.requestFocus();
                            etPassword.setText("");
                            break;

                        case "ERROR_USER_MISMATCH":
                            Toast.makeText(MainActivity.this, "The supplied credentials do not correspond to the previously signed in user.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_REQUIRES_RECENT_LOGIN":
                            Toast.makeText(MainActivity.this, "This operation is sensitive and requires recent authentication. Log in again before retrying this request.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL":
                            Toast.makeText(MainActivity.this, "An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_EMAIL_ALREADY_IN_USE":
                            Toast.makeText(MainActivity.this, "The email address is already in use by another account.   ", Toast.LENGTH_LONG).show();
                            etEmail.setError("The email address is already in use by another account.");
                            etEmail.requestFocus();
                            break;

                        case "ERROR_CREDENTIAL_ALREADY_IN_USE":
                            Toast.makeText(MainActivity.this, "This credential is already associated with a different user account.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_USER_DISABLED":
                            Toast.makeText(MainActivity.this, "The user account has been disabled by an administrator.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_USER_TOKEN_EXPIRED":
                            Toast.makeText(MainActivity.this, "The user\'s credential is no longer valid. The user must sign in again.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_USER_NOT_FOUND":
                            Toast.makeText(MainActivity.this, "There is no user record corresponding to this identifier. The user may have been deleted.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_INVALID_USER_TOKEN":
                            Toast.makeText(MainActivity.this, "The user\'s credential is no longer valid. The user must sign in again.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_OPERATION_NOT_ALLOWED":
                            Toast.makeText(MainActivity.this, "This operation is not allowed. You must enable this service in the console.", Toast.LENGTH_LONG).show();
                            break;

                        case "ERROR_WEAK_PASSWORD":
                            Toast.makeText(MainActivity.this, "The given password is invalid.", Toast.LENGTH_LONG).show();
                            etPassword.setError("The password is invalid it must 6 characters at least");
                            etPassword.requestFocus();
                            break;

                    }
                }
            }
        });
    }

过去我们使用 getErrorCode() 来获取错误类型并优雅地失败。在 api 的较新版本中,不推荐使用 getErrorCode()。我们应该使用 response.getError().getErrorCode() 而不是

com.firebase.ui.auth.IdpResponse
@Deprecated 
public int getErrorCode()
Get the error code for a failed sign in

Deprecated use getError() instead

例如

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

                super.onActivityResult(requestCode, resultCode, data);

                if (requestCode == RC_SIGN_IN) {
                    IdpResponse response = IdpResponse.fromResultIntent(data);

                    // Successfully signed in
                    if (resultCode == RESULT_OK) {
                        //dbHandler = DBMS.getInstance(this);

                        FirebaseAuth auth = FirebaseAuth.getInstance();
                        FirebaseUser user = auth.getCurrentUser();
                        FirebaseUserMetadata metadata = auth.getCurrentUser().getMetadata();

                        // initialize profile first
                        if (metadata.getCreationTimestamp() == metadata.getLastSignInTimestamp()) {



                            //start main activity after profile setup
                            startActivity(new Intent(this, MainActivity.class));
                            return;
                        } else {
                            // This is an existing user
                            // show them a welcome back screen.

                            startActivity(new Intent(this, MainActivity.class));
                            return;
                        }
                    } else {
                        // Sign in failed
                        // check response for error code
                        if (response == null) {
                            // User pressed back button
                            showSnackbar(R.string.sign_in_cancelled);
                            return;
                        }

                        if (response.getError().getErrorCode() == ErrorCodes.NO_NETWORK) {
                            showSnackbar(R.string.no_internet_connection);
                            return;
                        }

                        if (response.getError().getErrorCode() == ErrorCodes.UNKNOWN_ERROR) {
                            showSnackbar(R.string.unknown_error);
                            return;
                        }
                    }
                    showSnackbar(R.string.unknown_sign_in_response);
                }
            }

尝试以下操作:

if (task.isSuccessful()) {
    //Toast.makeText(getContext(),"Registration successful", Toast.LENGTH_SHORT).show();
    try {
        Toast.makeText(getContext(),"Registration successful", Toast.LENGTH_SHORT).show();
        throw task.getException();
    }
    // if user enters wrong email.
    catch (FirebaseAuthWeakPasswordException weakPassword) {
        Log.d("Registration Error", "onComplete: weak_password");

        // TODO: take your actions!
    }
    // if user enters wrong password.
    catch (FirebaseAuthInvalidCredentialsException malformedEmail) {
        Log.d("Registration Error", "onComplete: malformed_email");

        // TODO: Take your action
    }
    catch (FirebaseAuthUserCollisionException existEmail) {
        Log.d("Registration Error", "onComplete: exist_email");

        // TODO: Take your action
    }
    catch (Exception e) {
        Log.d("Registration Error", "onComplete: " + e.getMessage());
    }
} else {
    //Toast.makeText(getContext(), "ERROR, Please try again.", Toast.LENGTH_SHORT).show();
    Toast.makeText(getContext(), task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}

你可以使用这个:

mAuth.getCurrentUser().linkWithCredential(authCredential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "linkWithCredential:success");
                    } else {
                        Log.w(TAG, "linkWithCredential:failure", task.getException());
                        Toast.makeText(getApplicationContext(), "Authentication failed. " + task.getException().toString, Toast.LENGTH_SHORT).show();

                    }

                    // ...
                }
            });

在我看来,默认消息的信息量已经足够了。所以我使用它而不是任何自定义消息,它对我来说效果很好。

if (!task.isSuccessful()) 
{ 
    // there was an error
    String yourString = task.getException().toString();
    String target = "Exception:";
    String error = yourString.substring(yourString.indexOf(target) + target.length() + 1, yourString.length());
    Toast.makeText(LoginScreen.this, "Error: "+error, Toast.LENGTH_LONG).show();
}

p.s。这是我对堆栈溢出的第一个答案。如果有帮助,请告诉我。

使用 Kotlin 的解决方案

 fun signInWithEmail(email: String, passKey: String) {
    FirebaseAuth.getInstance().signInWithEmailAndPassword(email, passKey).addOnSuccessListener {
        it.user?.let {
            authResultOperation.postValue(AuthResultOperation.OnSuccessSignIn)
        }
    }.addOnFailureListener {
        val errorCode = (it.exception as FirebaseAuthException).errorCode
        val errorMessage = authErrors[errorCode] ?: R.string.error_login_default_error
        Toast.makeText(context, context.getString(errorMessage),Toast.LENGTH_LONG).show()
    }
}

说明:基本上它只是一个将 firebase 错误代码与自定义字符串资源匹配的映射。

val authErrors = mapOf("ERROR_INVALID_CUSTOM_TOKEN" to R.string.error_login_custom_token,
        "ERROR_CUSTOM_TOKEN_MISMATCH" to R.string.error_login_custom_token_mismatch,
        "ERROR_INVALID_CREDENTIAL" to R.string.error_login_credential_malformed_or_expired,
        "ERROR_INVALID_EMAIL" to R.string.error_login_invalid_email,
        "ERROR_WRONG_PASSWORD" to R.string.error_login_wrong_password,
        "ERROR_USER_MISMATCH" to R.string.error_login_user_mismatch,
        "ERROR_REQUIRES_RECENT_LOGIN" to R.string.error_login_requires_recent_login,
        "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL" to R.string.error_login_accounts_exits_with_different_credential,
        "ERROR_EMAIL_ALREADY_IN_USE" to  R.string.error_login_email_already_in_use,
        "ERROR_CREDENTIAL_ALREADY_IN_USE" to R.string.error_login_credential_already_in_use,
        "ERROR_USER_DISABLED" to R.string.error_login_user_disabled,
        "ERROR_USER_TOKEN_EXPIRED" to R.string.error_login_user_token_expired,
        "ERROR_USER_NOT_FOUND" to R.string.error_login_user_not_found,
        "ERROR_INVALID_USER_TOKEN" to R.string.error_login_invalid_user_token,
        "ERROR_OPERATION_NOT_ALLOWED" to R.string.error_login_operation_not_allowed,
        "ERROR_WEAK_PASSWORD" to R.string.error_login_password_is_weak)

字符串资源(可以根据自己的需要随意更改)

  <resources>
    <string name="error_login_custom_token">The custom token format is incorrect. Please check the documentation.</string>
    <string name="error_login_custom_token_mismatch">The custom token corresponds to a different audience.</string>
    <string name="error_login_credential_malformed_or_expired">The supplied auth credential is malformed or has expired.</string>
    <string name="error_login_invalid_email">The email address is badly formatted.</string>
    <string name="error_login_wrong_password">The password is invalid or the user does not have a password.</string>
    <string name="error_login_user_mismatch">The supplied credentials do not correspond to the previously signed in user.</string>
    <string name="error_login_requires_recent_login">This operation is sensitive and requires recent authentication. Log in again before retrying this request.</string>
    <string name="error_login_accounts_exits_with_different_credential">An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.</string>
    <string name="error_login_email_already_in_use">The email address is already in use by another account.</string>
    <string name="error_login_credential_already_in_use">This credential is already associated with a different user account.</string>
    <string name="error_login_user_disabled">The user account has been disabled by an administrator.</string>
    <string name="error_login_user_not_found">There is no user record corresponding to this identifier. The user may have been deleted.</string>
    <string name="error_login_operation_not_allowed">This operation is not allowed. You must enable this service in the console.</string>
    <string name="error_login_password_is_weak">The given password is invalid.</string>
    <string name="error_login_user_token_expired">The user\'s credential is no longer valid. The user must sign in again</string>
    <string name="error_login_invalid_user_token">The user\'s credential is no longer valid. The user must sign in again.</string>
</resources>

要处理的 Firebase 身份验证异常太多,我不确定处理所有这些异常是否是一件好事,因为要添加大量代码(正如您从其他答案中看到的那样)。

我在 Task<AuthResult.isSuccessful() 返回 false 后得到一个 FirebaseTooManyRequestsException

    firebaseAuth.signInWithEmailAndPassword(email, password)
        .addOnCompleteListener {
            if (it.isSuccessful) {
                [...]
            } else {
                [FirebaseTooManyRequestsException and others that can be returned here]
            }
        }

com.google.firebase.FirebaseTooManyRequestsException: We have blocked all requests from this device due to unusual activity. Try again later. [ Access to this account has been temporarily disabled due to many failed login attempts. You can immediately restore it by resetting your password or you can try again later. ]

因此,如果您想捕获所有这些或至少捕获对您的逻辑更重要的那些,我希望在此列表中引入另一个 FirebaseAuth 异常对您有所帮助。