Firebase Authenticate
Getting Started
We're going to augment our TodoMVC app that we tested in Part 1 to add basic Firebase authentication using Google Sign In. I'm going to be directing you to visit some of the Google code labs for the Firebase set up, so there will be a little jumping back and forth.
Download
Visit the GitHub repository to clone the source. This is highly recommended so you can play with the app. The final source code for this part is tagged as firebase_auth
.
After cloning, you can checkout the code with git checkout firebase_auth
.
NOTE: the source has only been tested to work on Android devices.
Add Firebase to your Project
Follow the steps from this portion of Google's code lab to configure your project to use Firebase. At the final step ("Add plugins to Friendlychat"), only the following plugins need to be added to pubspec.yaml
:
google_sign_in
firebase_auth
https://codelabs.developers.google.com/codelabs/flutter-firebase/#4
Before continuing, make sure you have used your IDE to do a packages get
, or that you have run flutter packages get
on the command line.
Configuring Google Sign In
Next, to configure Google sign in for your app, follow the steps until you hit the "Sign in to Google" step: https://codelabs.developers.google.com/codelabs/flutter-firebase/#5
Test Compile
Without changing anything other than this, let's run the application to make sure everything is configured correctly.
If you get an error on Android regarding firebase_auth
, you may need to upgrade your Gradle version.
Once everything is configured and running, we can now get our app to authenticate the user.
Adding Google Authentication
Firstly, we need to add the new imports to our App's library.
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
We need async
for our Future
handling. The other two are the plugins we just configured above with the code labs.
Helper Utility
We're going to add a helper class to manage the code for signing in and out.
part of todomvc;
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = new GoogleSignIn();
Future<FirebaseUser> signInWithGoogle() async {
// Attempt to get the currently authenticated user
GoogleSignInAccount currentUser = _googleSignIn.currentUser;
if (currentUser == null) {
// Attempt to sign in without user interaction
currentUser = await _googleSignIn.signInSilently();
}
if (currentUser == null) {
// Force the user to interactively sign in
currentUser = await _googleSignIn.signIn();
}
final GoogleSignInAuthentication auth = await currentUser.authentication;
// Authenticate with firebase
final FirebaseUser user = await _auth.signInWithGoogle(
idToken: auth.idToken,
accessToken: auth.accessToken,
);
assert(user != null);
assert(!user.isAnonymous);
return user;
}
Future<Null> signOutWithGoogle() async {
// Sign out with firebase
await _auth.signOut();
// Sign out with google
await _googleSignIn.signOut();
}
signInWithGoogle
is where all the magic happens. We first authenticate the user with Google, then use the currentUser
to generate a new OAuth signin token. We then pass that token information to Firebase to get (or create) our user.
signOutWithGoogle
is pretty simple. We first sign out of Firebase, then from Google. It's important that we remember to sign out of Google as well, otherwise _googleSignIn.currentUser
will remain valid and we will just be immediately logged back in as that user.
Finally, add this utility class to the App's library:
part 'util/authentication.dart';
Splash Page
We're going to update the app to use a splash page to manage the sign in functionality, rather than trying to smash it all into the Todo List page. We'll first create the page, then integrate it with the rest of our app.
part of todomvc;
class SplashPage extends StatefulWidget {
State createState() => new _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
void initState() {
super.initState();
// Listen for our auth event (on reload or start)
// Go to our /todos page once logged in
_auth.onAuthStateChanged
.firstWhere((user) => user != null)
.then((user) {
Navigator.of(context).pushReplacementNamed('/todos');
});
// Give the navigation animations, etc, some time to finish
new Future.delayed(new Duration(seconds: 1))
.then((_) => signInWithGoogle());
}
Widget build(BuildContext context) {
return new Scaffold(
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new CircularProgressIndicator(),
new SizedBox(width: 20.0),
new Text("Please wait..."),
],
),
],
),
);
}
}
The magic in our splash page happens in initState
. First, we subscribe to the onAuthStateChanged
Stream and wait until we get a non-null user (meaning we've logged in). Once we have that user, we navigate to our TodoList
page that we are going to set up as the named route /todos
. That page will be able to read the user straight from _auth
, so no additional context is needed.
For our next step, we need to update our home
to the splash page and create the new route for our TodoList
. Inside our MaterialApp
, change the following:
home: new SplashPage(), // This used to be new TodoList()
routes: <String, WidgetBuilder>{
'/todos': (BuildContext context) => new TodoList(),
},
The only part left for our authentication is logging out. We're going to add this as a menu item in a Drawer on the TodoList
page. Add the following property to its Scaffold
:
drawer: new Drawer(
child: new ListView(
primary: false,
children: <Widget>[
new DrawerHeader(
child: new Center(
child: new Text(
"Todo MVC",
style: Theme.of(context).textTheme.title,
),
),
),
new ListTile(
title: new Text('Logout', textAlign: TextAlign.right),
trailing: new Icon(Icons.exit_to_app),
onTap: () async {
await signOutWithGoogle();
Navigator.of(context).pushReplacementNamed('/');
},
),
],
),
),
We now have a drawer and a "Logout" button. When the button is clicked, we call our signOutWithGoogle
method from our Authentication Utility, then we navigate back to the splash page.
This has the side effect of attempting to sign user right back in, but they can pick a different google account after signing out, if they choose. Additionally, if we wanted to provide multiple signin methods, this would give them that option.
Our app now has access to a user associated with a google account. In our next part, we'll move our storage out of the state and into Firebase.