Skip to content

Demo Project to show how to use Phone Authentication in Flutter with Firebase backend

Notifications You must be signed in to change notification settings

mohith7548/Firebase-Phone-Auth-Demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Firebase Phone Authentication using Firebase

Demo Project to show how to do Phone Authentication in Flutter using Firebase backend.

Screenshots

At First PhoneNo is entered and submitted
At First PhoneNo is entered and submitted
After OTP is entered & submitted App is restarted
After OTP is entered & submitted App is restarted

Motivation

I felt there is no setp-by-step documentation for Firebase Phone Authentication in Flutter. There were couple of implementations on Medium but are out-dated and one article got it but that failed to explain some crucial concepts. AndroidX brought a lot of changes in addition to FlutterFire plugins update. I asked this question on stackoverflow almost 2 years ago, but failed to get step-by-step answer. So I created this demo project.

Setup Firebase with Flutter

You can setup Firebase by following this guide: https://firebase.google.com/docs/flutter/setup

If you encounter errors with MultiDex or AndroidX make sure you follow below steps

// Inside android folder in app level build.gradle file
...
android {
  ...
  defaultConfig {
    ...
    multiDexEnabled true // add this
  }
  ...
}
...
dependencies {
  // implementation 'com.android.support:multidex:1.0.3' // No AndroidX support
  implementation 'com.android.support:multidex:2.0.1' // Add this to support AndroidX
  implementation 'androidx.browser:browser:1.2.0' // To display verification webpage in the user's default browser
  ...
}
...

Steps

  1. Ask for user's phoneNumber
  2. Get OTP from Firebase
  3. SignIn to Firebase

Rules

  • SignIn/Login is done in the same way.
  • The OTP is only used to get AuthCrendential object
  • AuthCredential object is the only thing that is used to signIn the user. It is obtained either from verificationCompleted callback function in verifyPhoneNumber or from the PhoneAuthProvider. (Don't worry if it's confusing, keep reading, you'll get it)

Workflow

  1. User gives the phoneNumber
  2. Firebase sends OTP
  3. SignIn the user
    • If the SIM card with the phoneNumber is not in the device that is currently running the app,
      • We have to first ask the OTP and get AuthCredential object
      • Next we can use that AuthCredential to signIn This method works even if the phoneNumber is in the device
    • Else if user provided SIM phoneNumber is in the device running the app,
      • We can signIn without the OTP.
      • because the verificationCompleted callback from submitPhoneNumber function gives the AuthCredential object which is needed to signIn the user
      • But in the previous case it ain't called because the SIM is not in the phone.

Functions

  • SubmitPhoneNumber
Future<void> _submitPhoneNumber() async {
    /// NOTE: Either append your phone number country code or add in the code itself
    /// Since I'm in India we use "+91 " as prefix `phoneNumber`
    String phoneNumber = "+91 " + _phoneNumberController.text.toString().trim();
    print(phoneNumber);

    /// The below functions are the callbacks, separated so as to make code more redable
    void verificationCompleted(AuthCredential phoneAuthCredential) {
      print('verificationCompleted');
      ...
      this._phoneAuthCredential = phoneAuthCredential;
      print(phoneAuthCredential);
    }

    void verificationFailed(AuthException error) {
      ...
      print(error);
    }

    void codeSent(String verificationId, [int code]) {
      ...
      print('codeSent');
    }

    void codeAutoRetrievalTimeout(String verificationId) {
      ...
      print('codeAutoRetrievalTimeout');
    }

    await FirebaseAuth.instance.verifyPhoneNumber(
      /// Make sure to prefix with your country code
      phoneNumber: phoneNumber,

      /// `seconds` didn't work. The underlying implementation code only reads in `millisenconds`
      timeout: Duration(milliseconds: 10000),

      /// If the SIM (with phoneNumber) is in the current device this function is called.
      /// This function gives `AuthCredential`. Moreover `login` function can be called from this callback
      verificationCompleted: verificationCompleted,

      /// Called when the verification is failed
      verificationFailed: verificationFailed,

      /// This is called after the OTP is sent. Gives a `verificationId` and `code`
      codeSent: codeSent,

      /// After automatic code retrival `tmeout` this function is called
      codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
    ); // All the callbacks are above
  }
  • SubmitOTP
void _submitOTP() {
    /// get the `smsCode` from the user
    String smsCode = _otpController.text.toString().trim();

    /// when used different phoneNumber other than the current (running) device
    /// we need to use OTP to get `phoneAuthCredential` which is inturn used to signIn/login
    this._phoneAuthCredential = PhoneAuthProvider.getCredential(
        verificationId: this._verificationId, smsCode: smsCode);

    _login();
  }
  • SignIn/LogIn
Future<void> _login() async {
    /// This method is used to login the user
    /// `AuthCredential`(`_phoneAuthCredential`) is needed for the signIn method
    /// After the signIn method from `AuthResult` we can get `FirebaserUser`(`_firebaseUser`)
    try {
      await FirebaseAuth.instance
          .signInWithCredential(this._phoneAuthCredential)
          .then((AuthResult authRes) {
        _firebaseUser = authRes.user;
        print(_firebaseUser.toString());
      });
      ...
    } catch (e) {
      ...
      print(e.toString());
    }
  }
  • Logout
  Future<void> _logout() async {
    /// Method to Logout the `FirebaseUser` (`_firebaseUser`)
    try {
      // signout code
      await FirebaseAuth.instance.signOut();
      _firebaseUser = null;
      ...
    } catch (e) {
      ...
      print(e.toString());
    }
  }

For more details on implementation please refer to the lib/main.dart file.

Contributions

  • Please feel free to contribute to this README (Pull Requests) and this stackoverflow answer
  • If you find this README useful please star the repository