Skip to main content

Using Firebase Authentication

Notice

This page is archived and might not reflect the latest version of the FlutterFire plugins. You can find the latest information on firebase.google.com:

https://firebase.google.com/docs/auth/flutter/start (and elsewhere at https://firebase.google.com/docs/auth/flutter/*)

Once installed, you can access the firebase_auth plugin by importing it in your Dart code:

import 'package:firebase_auth/firebase_auth.dart';

Before using Firebase Auth, you must first have ensured you have initialized FlutterFire.

To create a new Firebase Auth instance, call the instance getter on FirebaseAuth:

FirebaseAuth auth = FirebaseAuth.instance;

By default, this allows you to interact with Firebase Auth using the default Firebase App used whilst installing FlutterFire on your platform. If however you'd like to use a secondary Firebase App, use the instanceFor method:

FirebaseApp secondaryApp = Firebase.app('SecondaryApp');
FirebaseAuth auth = FirebaseAuth.instanceFor(app: secondaryApp);

Authentication state#

Firebase Auth provides many methods and utilities for enabling you to integrate secure authentication into your new or existing Flutter application. In many cases, you will need to know about the authentication state of your user, such as whether they're logged in or logged out.

Firebase Auth enables you to subscribe in realtime to this state via a Stream. Once called, the stream provides an immediate event of the user's current authentication state, and then provides subsequent events whenever the authentication state changes.

There are three methods for listening to authentication state changes:

  1. authStateChanges()

To subscribe to these changes, call the authStateChanges() method on your FirebaseAuth instance:

FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});

Events are fired when the following occurs:

  • Right after the listener has been registered.
  • When a user is signed in.
  • When the current user is signed out.
  1. idTokenChanges()

To subscribe to these changes, call the idTokenChanges() method on your FirebaseAuth instance:

FirebaseAuth.instance
.idTokenChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});

Events are fired when the following occurs:

  • Right after the listener has been registered.
  • When a user is signed in.
  • When the current user is signed out.
  • When there is a change in the current user's token.

Warning: if you set custom claims from your own firebase admin sdk implementation, you will only see this event fire when the following occurs:

  • A user signs in or re-authenticates after the custom claims are modified. The ID token issued as a result will contain the latest claims.
  • An existing user session gets its ID token refreshed after an older token expires.
  • An ID token is force refreshed by calling FirebaseAuth.instance.currentUser.getIdTokenResult(true).

For further details, please visit Firebase admin propagating custom claims to the client

  1. userChanges()

To subscribe to these changes, call the userChanges() method on your FirebaseAuth instance:

FirebaseAuth.instance
.userChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});

Events are fired when the following occurs:

  • Right after the listener has been registered.
  • When a user is signed in.
  • When the current user is signed out.
  • When there is a change in the current user's token.
  • When the following methods provided by FirebaseAuth.instance.currentUser are called:
    • reload()
    • unlink()
    • updateEmail()
    • updatePassword()
    • updatePhoneNumber()
    • updateProfile()

Warning: idTokenChanges(), userChanges() & authStateChanges() will not fire if you update the User profile via your own firebase admin sdk implementation. You will have to force a reload using the following FirebaseAuth.instance.currentUser.reload() to retrieve the latest User profile.

idTokenChanges(), userChanges() & authStateChanges() will also not fire if you disable or delete the User via your own firebase admin sdk implementation or the Firebase console. You will have to force a reload using the following FirebaseAuth.instance.currentUser.reload() which will cause a user-disabled or user-not-found exception that you can catch and handle in your app code.

Persisting authentication state#

The Firebase SDKs for all platforms provide out of the box support for ensuring that your user's authentication state is persisted across app restarts or page reloads.

On native platforms such as Android & iOS, this behavior is not configurable and the user's authentication state will be persisted on-device between app restarts. The user can clear the apps cached data via the device settings which will wipe any existing state being stored.

On web platforms, the user's authentication state is stored in local storage. If required, you can change this default behavior to only persist authentication state for the current session, or not at all. To configure these settings, call the setPersistence() method (note; on native platforms an UnimplementedError will be thrown):

// Disable persistence on web platforms
await FirebaseAuth.instance.setPersistence(Persistence.NONE);

Sign-in methods#

Firebase provides a number of ways to sign users into your application, from anonymous users, password authentication, phone authentication and using OAuth/social providers.

Before using any sign-in methods, ensure you have configured the sign-in methods on the Firebase console.

Anonymous sign-in#

Even though many applications do not require the user to explicitly sign into an application, it is important that you are able to uniquely identify your users (for both analytical and security reasons). Anonymous sign-in provides an extra layer of security if using Firebase Firestore, Realtime Database or even an external API, since you're able to detect whether a request comes from an authenticated user.

If no previous anonymous account on the platform (for your specific application) has been created, when signing in anonymously Firebase will create a new user that will be persisted across app restarts/page reloads. If the user signs in anonymously multiple times, they will be signed-in with the initially created account. It is however important to remember the anonymous account created will not be persisted if the user signs out or uninstalls the application, clears their browser storage or uses a private browsing method (e.g. incognito mode on Google Chrome). Performing any of these operations means that the user won't be able to access the same account anymore.

To get started, call the signInAnonymously() method on the FirebaseAuth instance:

UserCredential userCredential = await FirebaseAuth.instance.signInAnonymously();

Once successfully resolved, the user will be granted an anonymous account. If you are listening to changes in authentication state, a new event will be sent to your listeners.

To learn more about how you can handle any errors which are thrown from the method, view the Error Handling documentation.

Email/Password Registration & Sign-in#

Email/Password is a common user sign in method for most applications. This requires the user to provide an email address and secure password. Users can register new accounts with a method called createUserWithEmailAndPassword() or sign in to an existing account with signInWithEmailAndPassword().

Registration#

To create a new account on your Firebase project call the createUserWithEmailAndPassword() method with the user's email address and password:

try {
UserCredential userCredential = await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: "barry.allen@example.com",
password: "SuperSecretPassword!"
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}

The method is a two-step operation; it will first create the new account (if it does not already exist and the password is valid) and then automatically sign in the user in to that account. If you are listening to changes in authentication state, a new event will be sent to your listeners.

To learn more about how you can handle any errors which are thrown from the method, view the Error Handling documentation.

Sign-in#

To sign-in to an existing account, call the signInWithEmailAndPassword() method:

try {
UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: "barry.allen@example.com",
password: "SuperSecretPassword!"
);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
}
caution

Note: uninstalling your application on iOS or macOS can still preserve your users authentication state between app re-installs as the underlying Firebase iOS SDK persists authentication state to keychain. See #4661 for more information.

Once successful, if you are listening to changes in authentication state, a new event will be sent to your listeners.

Verifying a users email#

Even though the above authentication flows sign a user into your application, they can provide any valid email address even if they do not have access to that email address. In many cases, you may wish to make a user verify the provided email address before you grant them full access to your application.

The User class provides a sendEmailVerification() method and emailVerified property which you can use to handle this scenario.

For example, once signed in you can check the property and send an email verification to the user:

User? user = FirebaseAuth.instance.currentUser;
if (user!= null && !user.emailVerified) {
await user.sendEmailVerification();
}

Firebase will send an automated email to the user with a link. User's email will be verified when the link is opened in a browser.

Open link in app#

You can pass state via a continue URL when sending email actions for verifying a user's email. This provides the user the ability to go back to the app after the action is completed. In addition, you can specify whether to handle the email action link directly from a mobile application when it is installed instead of a web page.

The following example illustrates how to send an email verification link that will open in a mobile app first as a Firebase Dynamic Link using the custom dynamic link domain example.page.link (iOS app com.example.ios or Android app com.example.android where the app will install if not already installed and the minimum version is 12). The deep link will contain the continue URL payload https://www.example.com/?email=user@example.com.

User? user = FirebaseAuth.instance.currentUser;
if (user != null && !user.emailVerified) {
var actionCodeSettings = ActionCodeSettings(
url: 'https://www.example.com/?email=${user.email}',
dynamicLinkDomain: 'example.page.link',
androidPackageName: 'com.example.android',
androidInstallApp: true,
androidMinimumVersion: '12',
iOSBundleId: 'com.example.ios',
handleCodeInApp: true,
);
await user.sendEmailVerification(actionCodeSettings);
}

The code then can be received in app by parsing the Firebase Dynamic Link. Refer - Handling email actions in a mobile application to know how to handle the link in app. You can use firebase_dynamic_links flutter package to get the oobCode from the link and apply actionCode as follow.

FirebaseAuth auth = FirebaseAuth.instance;
//Get actionCode from the dynamicLink
final Uri deepLink = dynamicLink?.link;
var actionCode = deepLink.queryParameters['oobCode'];
try {
await auth.checkActionCode(actionCode);
await auth.applyActionCode(actionCode);
// If successful, reload the user:
auth.currentUser.reload();
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-action-code') {
print('The code is invalid.');
}
}

To learn more about how you can handle any errors which are thrown from the method, view the Error Handling documentation.

Email Link Authentication#

You can use Firebase Authentication to sign in a user by sending them an email containing a link, which they can click to sign in. In the process, the user's email address is also verified.

Enable Email Link sign-in for your Firebase project#

Ensure that passwordless sign-in is enabled in the project. Follow the guide here.

Send an authentication link to the user's email address#

To initiate the authentication flow, present an interface that prompts the user to provide their email address and then call sendSignInLinkToEmail to request that Firebase send the authentication link to the user's email.

Construct the ActionCodeSettings object, which provides Firebase with instructions on how to construct the email link. Set the following fields:

  • url: The deep link to embed and any additional state to be passed along. The link's domain has to be whitelisted in the Firebase Console list of authorized domains, which can be found by going to the Sign-in method tab (Authentication -> Sign-in method). The link will redirect the user to this URL if the app is not installed on their device and the app was not able to be installed.

  • androidPackageName and IOSBundleId: The apps to use when the sign-in link is opened on an Android or iOS device. Learn more on how to configure Firebase Dynamic Links to open email action links via mobile apps.

  • handleCodeInApp: Set to true. The sign-in operation has to always be completed in the app unlike other out of band email actions (password reset and email verifications). This is because, at the end of the flow, the user is expected to be signed in and their Auth state persisted within the app.

  • dynamicLinkDomain: When multiple custom dynamic link domains are defined for a project, specify which one to use when the link is to be opened via a specified mobile app (for example, example.page.link). Otherwise the first domain is automatically selected.

var acs = ActionCodeSettings(
// URL you want to redirect back to. The domain (www.example.com) for this
// URL must be whitelisted in the Firebase Console.
url: 'https://www.example.com/finishSignUp?cartId=1234',
// This must be true
handleCodeInApp: true,
iOSBundleId: 'com.example.ios',
androidPackageName: 'com.example.android',
// installIfNotAvailable
androidInstallApp: true,
// minimumVersion
androidMinimumVersion: '12');

Ask the user for their email.

Send the authentication link to the user's email, and save the user's email in case the user completes the email sign-in on the same device.

var emailAuth = 'someemail@domain.com';
FirebaseAuth.instance.sendSignInLinkToEmail(
email: emailAuth, actionCodeSettings: acs)
.catchError((onError) => print('Error sending email verification $onError'))
.then((value) => print('Successfully sent email verification'));
});

Verify email link and sign in#

Firebase Authentication uses Firebase Dynamic Links to send the email link to a mobile device. For sign-in completion via mobile application, the application has to be configured to detect the incoming application link, parse the underlying deep link and then complete the sign-in.

Follow the setup process for Firebase Dynamic Links on Flutter in this guide and ensure you have correctly integrated Dynamic Links for Android and Apple devices.

After you received the link, verify that it is meant for email link authentication and complete the sign in.

var auth = FirebaseAuth.instance;
// Retrieve the email from wherever you stored it
var emailAuth = 'someemail@domain.com';
// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
// The client SDK will parse the code from the link for you.
auth.signInWithEmailLink(email: emailAuth, emailLink: emailLink).then((value) {
// You can access the new user via value.user
// Additional user info profile *not* available via:
// value.additionalUserInfo.profile == null
// You can check if the user is new or existing:
// value.additionalUserInfo.isNewUser;
var userEmail = value.user;
print('Successfully signed in with email link!');
}).catchError((onError) {
print('Error signing in with email link $onError');
});
}

Signing Out#

To sign a user out, call the signOut() method:

await FirebaseAuth.instance.signOut();

If you are listening to changes in authentication state, a new event will be sent to your listeners.

Other sign-in methods#

Firebase also supports authenticating with external provides. To learn more, view the documentation for your authentication method:

User management#

Once authenticated, FlutterFire provides you access to the user via the User class. The class stores the current information about the user such as their unique user ID, any linked provider accounts and methods to manage the user.

The User class is returned from any authentication state listeners, or as part of a UserCredential when using authentication methods. If you are sure the user is currently signed-in, you can also access the User via the currentUser property on the FirebaseAuth instance. The examples below show how to access the user:

var currentUser = FirebaseAuth.instance.currentUser;
if (currentUser != null) {
print(currentUser.uid);
}

Deleting a user#

If your user wishes to delete their account from your project, this can be achieved with the delete() method. Since this is a security-sensitive operation, it requires that user must have recently signed-in. You can handle this scenario by catching the error, for example:

try {
await FirebaseAuth.instance.currentUser!.delete();
} on FirebaseAuthException catch (e) {
if (e.code == 'requires-recent-login') {
print('The user must reauthenticate before this operation can be executed.');
}
}

Reauthenticating a user#

As mentioned above, some operations such as deleting the user, updating their email address or providers require the user to have recently signed in. Rather than signing the user out and back in again, the reauthenticateWithCredential() method can be called. If a recent login is required, create a new AuthCredential and pass it to the method. For example, to reauthenticate with email & password, create a new EmailAuthCredential:

// Prompt the user to enter their email and password
String email = 'barry.allen@example.com';
String password = 'SuperSecretPassword!';
// Create a credential
AuthCredential credential = EmailAuthProvider.credential(email: email, password: password);
// Reauthenticate
await FirebaseAuth.instance.currentUser!.reauthenticateWithCredential(credential);

Reauthentication also works if you are using an OAuth credential instead.

Linking user accounts#

You can allow your users to sign into your application using multiple providers by linking authentication credentials to existing user accounts. Users can then be identified using their Firebase UID, regardless of the provider they used to sign in.

The user must first be logged in to one of the accounts you want to link the new provider to. You can then log the user into the second provider, and pass that AuthCredential to the linkWithCredential method from the first UserCredential:

Future<void> linkGoogleAndTwitter() async {
// Trigger the Google Authentication flow.
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request.
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
// Create a new credential.
final GoogleAuthCredential googleCredential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Sign in to Firebase with the Google [UserCredential].
final UserCredential googleUserCredential =
await FirebaseAuth.instance.signInWithCredential(googleCredential);
// Now let's link Twitter to the currently signed in account.
// Create a [TwitterLogin] instance.
final TwitterLogin twitterLogin = new TwitterLogin(
consumerKey: consumerKey,
consumerSecret: consumerSecret
);
// Trigger the sign-in flow.
final TwitterLoginResult loginResult = await twitterLogin.authorize();
// Get the logged in session.
final TwitterSession twitterSession = loginResult.session;
// Create a [AuthCredential] from the access token.
final AuthCredential twitterAuthCredential =
TwitterAuthProvider.credential(
accessToken: twitterSession.token,
secret: twitterSession.secret
);
// Link the Twitter account to the Google account.
await googleUserCredential.user.linkWithCredential(twitterAuthCredential);
}

Emulator Usage#

If you are using the local Authentication emulator, then it is possible to connect to this using the useAuthEmulator method. Ensure you pass the correct port on which the Firebase emulator is running on. Ensure you have enabled network connections to the emulators in your apps following the emulator usage instructions in the general FlutterFire installation notes for each operating system.

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Ideal time to initialize
await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
//...
}