Codementor Events

Firebase Auth Exceptions handling | Flutter

Published May 29, 2020
Firebase Auth Exceptions handling | Flutter

Clear instruction messages on exceptions will save you from losing trusted users.

Pre-Reqs:

This guide expects you already know flutter basics, and if you don't, then here is a Flutter Learning Roadmap.

Background:

In this guide, I have modularized all the functionalities into different classes and enums with good practices to have maintainable and extendable code. All login and Result status handling logic is completely isolated from the UI.
Auth Result Status Enum:
Enums are very effective if you have multiple results statuses as in our case.
As the name suggests, AuthResultStatus will have the result status of firebase auth.

enum AuthResultStatus {
  successful,
  emailAlreadyExists,
  wrongPassword,
  invalidEmail,
  userNotFound,
  userDisabled,
  operationNotAllowed,
  tooManyRequests,
  undefined,
}

Auth Exception Handler:

As the name suggests, the AuthExceptionHandler class handles the exception by reading the error code of the firebase exception object, returns the AuthResultStatus based on the error code plus also generates an error message for every AuthResultStatus.

class AuthExceptionHandler {
  static handleException(e) {
    print(e.code);
    var status;
    switch (e.code) {
      case "ERROR_INVALID_EMAIL":
        status = AuthResultStatus.invalidEmail;
        break;
      case "ERROR_WRONG_PASSWORD":
        status = AuthResultStatus.wrongPassword;
        break;
      case "ERROR_USER_NOT_FOUND":
        status = AuthResultStatus.userNotFound;
        break;
      case "ERROR_USER_DISABLED":
        status = AuthResultStatus.userDisabled;
        break;
      case "ERROR_TOO_MANY_REQUESTS":
        status = AuthResultStatus.tooManyRequests;
        break;
      case "ERROR_OPERATION_NOT_ALLOWED":
        status = AuthResultStatus.operationNotAllowed;
        break;
      case "ERROR_EMAIL_ALREADY_IN_USE":
        status = AuthResultStatus.emailAlreadyExists;
        break;
      default:
        status = AuthResultStatus.undefined;
    }
    return status;
  }

  ///
  /// Accepts AuthExceptionHandler.errorType
  ///
  static generateExceptionMessage(exceptionCode) {
    String errorMessage;
    switch (exceptionCode) {
      case AuthResultStatus.invalidEmail:
        errorMessage = "Your email address appears to be malformed.";
        break;
      case AuthResultStatus.wrongPassword:
        errorMessage = "Your password is wrong.";
        break;
      case AuthResultStatus.userNotFound:
        errorMessage = "User with this email doesn't exist.";
        break;
      case AuthResultStatus.userDisabled:
        errorMessage = "User with this email has been disabled.";
        break;
      case AuthResultStatus.tooManyRequests:
        errorMessage = "Too many requests. Try again later.";
        break;
      case AuthResultStatus.operationNotAllowed:
        errorMessage = "Signing in with Email and Password is not enabled.";
        break;
      case AuthResultStatus.emailAlreadyExists:
        errorMessage =
            "The email has already been registered. Please login or reset your password.";
        break;
      default:
        errorMessage = "An undefined Error happened.";
    }

    return errorMessage;
  }
}

Firebase Auth Helper:

FirebaseAuthHelper class will handle the communication with Firebase Auth Service and just returns the result status to the UI. Through this class Authentication logic is isolated from the UI code.

class FirebaseAuthHelper {
  final _auth = FirebaseAuth.instance;
  AuthResultStatus _status;

  ///
  /// Helper Functions
  ///
  Future<void> createAccount({email, pass}) async {
    try {
      AuthResult authResult = await _auth.createUserWithEmailAndPassword(
          email: email, password: pass);
      if (authResult.user != null) {
        _status = AuthResultStatus.successful;
      } else {
        _status = AuthResultStatus.undefined;
      }
    } catch (e) {
      print('Exception @createAccount: $e');
      _status = AuthExceptionHandler.handleException(e);
    }
    return _status;
  }

  Future<AuthResultStatus> login({email, pass}) async {
    try {
      final authResult =
          await _auth.signInWithEmailAndPassword(email: email, password: pass);

      if (authResult.user != null) {
        _status = AuthResultStatus.successful;
      } else {
        _status = AuthResultStatus.undefined;
      }
    } catch (e) {
      print('Exception @createAccount: $e');
      _status = AuthExceptionHandler.handleException(e);
    }
    return _status;
  }

  logout() {
    _auth.signOut();
  }
}

Implementation:

Here is the final implementation for account creation and login. And based on the result status, either error dialog is shown or the user proceeds to the success page.

_createAccount() async {
      final status = await FirebaseAuthHelper().createAccount(
          email: email, pass: password);
      if (status == AuthResultStatus.successful) {
        // Navigate to success page
        } else {
        final errorMsg = AuthExceptionHandler.generateExceptionMessage(
            status);
        _showAlertDialog(errorMsg);
      }
  }
_login() async {
      final status = await FirebaseAuthHelper().login(
          email: email, pass: password);
      if (status == AuthResultStatus.successful) {
        // Navigate to success page
        } else {
        final errorMsg = AuthExceptionHandler.generateExceptionMessage(
            status);
        _showAlertDialog(errorMsg);
      }
  }

Complete Source on Github:

The complete GitHub project can be found here with a good UI plus some other best practices in the UI.
Looking forward to your thoughts, feedback for improvement.

Discover and read more posts from Haroon khan
get started
post comments2Replies
krisztianodor
4 years ago

This won’t work anymore, because the new FirebaseAuthException class throws different error codes.

NEW: Added a new FirebaseAuthException class (extends FirebaseException).
All errors are now returned as a FirebaseAuthException, allowing you to access the code & message associated with the error.
In addition, it is now possible to access the email and credential properties on exceptions if they exist.

change example: ERROR_WRONG_PASSWORD -> wrong-password

The new error codes can be found on this page - i didn’t found the list of these anywhere else, only in the javascript documentation…

Haroon khan
4 years ago

Yes, they have soon updated most of firebase APIs and yes we need an update to this blog as well. Thank you for reminding.