Firebase User Authentication using Phone verification in Flutter

Maaz Aftab
CodeChai
7 min readJan 19, 2020

--

Mobile Application often requires User authentication in order to show the secured and relevant data to the authenticated user.

Luckily Firebase, which is Backend-as-a-Service (BaaS), provides User authentication service as well along with many other useful Backend services.

In order to use Firebase User Authentication Backend Service, you must need to integrate Firebase with the Flutter application. If you don’t know how to integrate Firebase with Flutter Application then read my article on Integration of Firebase with Flutter.

Now Let’s get started.

Firebase Authentication service provides different ways of verifying or authenticating the user, like Email and Password, Phone Number, Facebook account, Google account, and etc. However, in this article, we are interested in verifying the user using mobile phone Verification. Firebase first send the OTP Code (One Time Password) to the user’s device, and it verifies the user by retrieving the code from device automatically, or either by letting the user giving the code manually (If auto retrieval not works on a particular device).

Different Sign in Methods in Firebase Authentication Service

Enabling the Phone Sign in Method

After making Firebase Project from Firebase console, and integrating it with Flutter application, you must enable the Phone sign up method first. For this go in Firebase console, open the Authentication tab from the left panel. Click on the Setup Sign up method and enable the Phone verification.

Setting up the Sign up method.

Now open the pubspec.yaml file, and make sure that you added Firebase Authentication plugin and Firebase core plugin.

Firebase Authentication Dependency for Flutter application.

That’s Great! Now Let’s first work on the UI part of the application. The application would have just two screens the first one would be the Login Screen, which is to allow user to login, and the second one would be the Home Screen, which we will show once we verified the user. You might be wondering that where is the registration screen, so the answer is that when the user logs in first time using Mobile verification, so after verification Firebase registers that user in the authentication data automatically, but if the user logs in not for the first time it just do verification by sending the message.

Login Screen and Home Screen

Here is the Code for Login Screen and Home Screen.

LoginScreen.dart

class LoginScreen extends StatelessWidget {
final _phoneController = TextEditingController();
final _passController = TextEditingController();
//Place A @override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(32),
child: Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Login", style: TextStyle(color: Colors.lightBlue, fontSize: 36, fontWeight: FontWeight.w500),),

SizedBox(height: 16,),

TextFormField(
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.grey[200])
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.grey[300])
),
filled: true,
fillColor: Colors.grey[100],
hintText: "Phone Number"

),
controller: _phoneController,
),

SizedBox(height: 16,),

TextFormField(
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.grey[200])
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.grey[300])
),
filled: true,
fillColor: Colors.grey[100],
hintText: "Password"

),

controller: _passController,
),

SizedBox(height: 16,),

Container(
width: double.infinity,
child: FlatButton(
child: Text("Login"),
textColor: Colors.white,
padding: EdgeInsets.all(16),
onPressed: (){
//code for sign in
Place B
},
color: Colors.blue,
),
)
],
),
),
)
);
}
}

Home Screen

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {

final FirebaseUser user;

HomeScreen({this.user});

@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("You are Logged in succesfully", style: TextStyle(color: Colors.lightBlue, fontSize: 32),),
SizedBox(height: 16,),
Text("${user.phoneNumber}", style: TextStyle(color: Colors.grey, ),),
],
),
),
);
}
}

Now we have done with UI part, and now we can move to main thing that is verification of user based on the mobile phone number.

Inside the LoginScreen class at Place A, make a Future that will be used to verify the user.

Future registerUser(String mobile, BuildContext context) async{
//code
}

Now lets first create the instance of Firebase auth, inside the Future.

Future registerUser(String mobile, BuildContext context) async{

FirebaseAuth _auth = FirebaseAuth.instance;
}

Now Firebase Authentication Provides a Future for verifying a user using mobile phone called verifyPhoneNumber

Future registerUser(String mobile, BuildContext context) async{

FirebaseAuth _auth = FirebaseAuth.instance;

_auth.verifyPhoneNumber(
phoneNumber: null,
timeout: null,
verificationCompleted: null,
verificationFailed: null,
codeSent: null,
codeAutoRetrievalTimeout: null
);
}

Inside the verifyPhoneNumber, the phoneNumber is the mobile number through which you want to verify the user, timeout is the duration after which the code sent on the device would get expired. verficationCompleted is the callback which gets called once the verification is successfully completed. verificationFailed is also a callback which gets called if the verification is failed because of wrong code or incorrect mobile number. codeSent is a callback which gets called once the code is sent to the device. At last, codeAutoRetrievalTimeout is the callback which gets called when the time will be completed for the auto retrieval of code.

Lets first give the phoneNumber and the timeout.

Future registerUser(String mobile, BuildContext context) async{

FirebaseAuth _auth = FirebaseAuth.instance;

_auth.verifyPhoneNumber(
phoneNumber: mobile,
timeout: Duration(seconds: 60),
verificationCompleted: null,
verificationFailed: null,
codeSent: null,
codeAutoRetrievalTimeout: null
);
}

Before defining the verificationCompleted, let me tell you fact that this callback is called only when the verification is successfully completed automatically using Auto Retrieval (without the need of user input).

//...timeout: Duration(seconds: 60),
verificationCompleted: (AuthCredential authCredential){
//code for signing in}).catchError((e){
print(e);
});
},
verificationFailed: null,

This callback gives you the authCredential object which you can use to sign in. To sign in with authCredential, you need to use signInWithCredential Future provided by Firebase Authentication.

//...timeout: Duration(seconds: 60),
verificationCompleted: (AuthCredential authCredential){
_auth.signInWithCredential(_credential).then((AuthResult result){Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
verificationFailed: null,
//...

After signing in with signInWithCredential, we can pass the user instance returned by Firebase authentication and we can navigate to the home screen.

Now let’s define the verificationFailed callback. This callback is called when the verification is failed and the user is not logged in, and it gives you the AuthException object, and you can print that error to see what’s the issue or you can use this to acknowledge user about error.

//...verificationFailed: (AuthException authException){
print(authException.message);
},
//...

Now let’s define codeSent callback. Sometimes, as I mentioned above, auto retrieval not successfully works on a particular device. That’s why we need to manually ask user to input the code he received on the device by showing some dialog inside codeSent, and then we need to manually make the AuthCredential object that we received in the verificationCompleted callback from Firebase.

//...codeSent: (String verificationId, [int forceResendingToken]){
//show dialog to take input from the user
}//...

For making the AuthCredential, we need the verification id, which is generated by Firebase against every code, and you get it in the codeSent from Firebase, and the OTP Code that the user has received. So, we can show some dialog to take input the OTP Code code from the user.

//...codeSent: (String verificationId, [int forceResendingToken]){
//show dialog to take input from the user
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text("Enter SMS Code"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _codeController,
),

],
),
actions: <Widget>[
FlatButton(
child: Text("Done"),
textColor: Colors.white,
color: Colors.redAccent,
onPressed: () {
FirebaseAuth auth = FirebaseAuth.instance;

smsCode = _codeController.text.trim();

_credential = PhoneAuthProvider.getCredential(verificationId: verificationId, smsCode: smsCode);
auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
)
],
)
);
}//...

We have used below line to make AuthCredential from verification code and OTP Code that user received.

PhoneAuthProvider.getCredential(verificationId: verificationId, smsCode: smsCode);

At Last, just define the codeAutoRetrievalTimeout, it is called when the timeout occurs for Code Auto Retrieval. We are not doing anything just printing the acknowledgement that the time for Auto retrieval is finished.

//...codeAutoRetrievalTimeout: (String verificationId){
verificationId = verificationId;
print(verificationId);
print("Timout");
};
//...

So we have finished the Future for Registering or Login the user, now we just need to call it inside the onPressed of the FlatButton in the Login Screen at Place B.

//...Container(
width: double.infinity,
child: FlatButton(
child: Text("Login"),
textColor: Colors.white,
padding: EdgeInsets.all(16),
onPressed: (){
//code for sign in
final mobile = _phoneController.text.trim();
registerUser(mobile, context);
},
color: Colors.blue,
),
)
//...

So that’s it, We have done it!

That’s just the code implementation of User Authentication using mobile. You can make it more better by including some validation and saving some additional information like display name and display image with the user. If you are still not clear and want to see video tutorial of this, then you can head over to my YouTube channel Easy Approach, where you can find the complete Flutter series.

For Complete Code visit my GitHub.

Thanks.

--

--

Maaz Aftab
CodeChai

Founder Of Easy Approach, Flutter Lover, YouTuber, and a writer.