Flutter x Firebase | Authentication
This month of February will be all about Flutter & Firebase. In the next couple of weeks, we’re going to be looking at different Firebase services and how to use them with Flutter.
Firebase is an app development platform developed by Google for building, creating and growing mobile and web applications. With Firebase, developers need not focus so much on the backend — servers, APIs, storage, analytics, authentication etc, as the platform takes care of all these needs.
In one of my previous articles — here, I shared how one can integrate their Flutter application with Firebase, so we’ll not be covering that in this series. Aside from the method we covered in that article, one can also use FlutterFire (a set of Flutter plugins which connect your Flutter application to Firebase) to configure the same using the CLI. In summary, the CLI route would look like this …
# first create the Firebase project (firebase.google.com)
# then create the Flutter project
$ flutter create --org com.flutter your_project_name --empty
$ cd your_project_name
$ flutterfire configure
# select the Firebase project and the platform options
# flutter pub add firebase_core to add firebase_core to your pubspec.yaml file.
This will generate a firebase_options.dart
file for use with your Firebase apps. Initialise Firebase in the main.dart file to be able to use it in the subsequent operations. This will ensure that if Firebase tries to call the app before it is ready, it wouldn’t crash.
void main() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
In this article, we’re going to specifically look at authentication with Firebase.
You can use Firebase Authentication to allow users to sign in to your app using one or more sign-in methods, including native providers e.g., email address and password sign-in or using federated identity providers such as Google Sign-in, Twitter and Facebook Login.
To get started with Firebase authentication, once you have connected your Flutter application with Firebase, enable authentication from the Firebase console.
For this tutorial, we’re going to only work with authentication via email/password and Google. So enable these providers on the Firebase console. Aside from that, also fetch the firebase auth dependency to your pubspec.yaml file flutter pub add firebase_auth
and import it in your dart file.
Then proceed to create your UI which will have the required fields and buttons needed to register and log in the users. This is the simple user interface I have created for the sign in and registration screens for my application— catsagram (IG … but for cats 😂 ).
As it is, there are currently no users for the application since we haven’t registered any. You can manually add the users from the console or through the UI we have created for the app, users can create the accounts from their end.
Email/password authentication
Registration
If you have set field validations on the form e.g. confirm password etc, then the following registration logic will be executed when the create account
button is clicked, if there are no form validation errors. We’re also catching other auth errors which will be displayed in a toast message for the user. If the registration is successful, then we’ll route to the next page.
onTap: () async {
String message = '';
if (_form.currentState!.validate()) {
try {
await _firebaseAuth.createUserWithEmailAndPassword( // instantiated earlier on: final _firebaseAuth = FirebaseAuth.instance;
email: email.trim(),
password: password.trim(),
);
Future.delayed(const Duration(seconds: 3), () {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (_) => const Feed(),
),
);
});
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
message = 'The password provided is too weak.';
} else if (e.code == 'email-already-in-use') {
message = 'An account already exists with that email.';
}
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 14.0,
);
} catch (e) {
Fluttertoast.showToast(
msg: "Failed: $e",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 14.0,
);
}
}
},
That is pretty much the registration logic. If there are no errors, then the user will be created in the Firebase console and the next screen will be displayed.
Login
Only users who have been registered will be able to login the application. So to login successfully, ensure you have the credentials of a user already in the database. The code for logging in is similar to that of registering. The difference is in the use of the signInWithEmailAndPassword
as opposed to createUserWithEmailAndPassword
onTap: () async {
String message = '';
if (_form.currentState!.validate()) {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
Future.delayed(const Duration(seconds: 3), () {
print('success');
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (_) => const Feed(),
),
);
});
} on FirebaseAuthException catch (e) {
if (e.code == 'INVALID_LOGIN_CREDENTIALS') {
message = 'Invalid login credentials.';
} else {
message = e.code;
}
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 14.0,
);
}
}
},
I noted something interesting while implementing this bit: With logging in, I was only able to get one FirebaseAuthException error i.e., INVALID_LOGIN_CREDENTIALS
. On research, I saw that this is probably due to email enumeration. Email enumeration is a type of brute-force attack in which a malicious actor attempts to guess or confirm users in a system by passing an email address to the API and checking the response. So as at 15th September 2023, the email enumeration protection feature has been enabled by default on Firebase. With this feature, the platform will return information that can’t be used in an email enumeration attack. You can choose to disable email enumeration protection so that the API reverts to its previous behaviour. However, in doing that, your project/users become susceptible to the risks of an email enumeration attack.
Ps. Once your user is registered or logged in, you might want to keep signed in until they choose to log out. You can use local storage to save the user in the app e.g., SharedPrefs, SQFLite or Hive.
Logging out
To log out a user, simply call the signOut()
method from the log out button, icon or menu item. (This will also apply for Google authentication signing out)
IconButton(
onPressed: () async {
try {
await FirebaseAuth.instance.signOut();
Future.delayed(const Duration(seconds: 3), () {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (_) => const LoginScreen(),
),
);
});
} on Exception catch (e) {
Fluttertoast.showToast(
msg: e.toString(),
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 14.0,
);
}
},
icon: const Icon(CupertinoIcons.square_arrow_right),
),
Google authentication
As mentioned at the start of the article, ensure the “Google” sign-in provider is enabled on the Firebase Console and save it. You will get a prompt to download the latest configuration files as they get updated when you enable Google sign-in for the first time which creates new OAuth clients. Replace your config files with the new downloaded files (Or re-run flutterfire configure and overwrite the previously generated files).
In addition to that, while enabling this provider, you’ll see something about adding SHA-1 certificate fingerprints. Follow the instructions here to generate this either using Keytool or Grade’s Signing report. I followed the later. Chanhe directories to the android folder in your project and run ./gradlew signingReport
then add the SHA-1 certificate generated to your projects configuration.
Now to the dart files. An additional google sign in dependency is needed so run flutter pub add google_sign_in
to fetch it and import it accordingly to the registration and login dart files.
Then finally, this is the method you’ll need to call from the Sign up with Google
or Sign up with Google
button to register the user using Google.
Future<dynamic> signInWithGoogle() async {
try {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication? googleAuth = await googleUser?.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
return await FirebaseAuth.instance.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
Fluttertoast.showToast(
msg: e.toString(),
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.SNACKBAR,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 14.0,
);
}
}
With Android, everything worked well and as expected as shown from the series of screenshots below. Once you verify the Google account to Sign up with, you’ll be taken to the screen specified.
The user will also be added to the Firebase users DB, with the provider specified.
I was however having getting an issue when running the code as is on iOS. This was the PlatformException error No active configuration. Make sure GIDClientID is set in Info.plist
To fix this go to GoogleService-Info.plist
file under the ios > Runner folders. Copy the String value from the REVERSED_CLIENT_ID
key. Then, open the Runner.xcworkspace
folder in Finder (Folder right below the Runner folder) and double click the xcode workspace so as to open it in Xcode. Then within Runner, go to the Info tab. Under the URL types, add a new URL type. Under the URL scheme, paste the string copied earlier.
Then close the xcode window. This will make some updates to the Info.plist
and GoogleService-Info.plist
files. E.g., these lines of code in Info.plist
. (You can also manually add this lines as shared in this doc)
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>THE_REVERSED_CLIENT_ID_YOU_PASTED</string>
</array>
</dict>
</array>
Sign up with Google should now run successfully in iOS as below.
So there you have it. Authentication with Flutter and Firebase. Let me know if this tutorial was helpful in one way or another. I’d love your feedback.
Join me next week as we extend the functionality of our app with another Firebase services. Any guesses?
“Time spent with cats is never wasted.”
— Sigmund Freud.
Thank you for reading! ❤
References
1. https://firebase.google.com/docs/auth/flutter/password-auth
2. https://firebase.google.com/codelabs/firebase-auth-in-flutter-apps#0
3. https://firebase.flutter.dev/docs/auth/overview/
4. https://firebase.google.com/docs/auth/flutter/start
5. https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection#overview
6. https://developers.google.com/android/guides/client-auth
7. https://developers.google.com/identity/sign-in/ios/start-integrating