18

I am using Firebase Authentication on my Flutter Web app, but the session is not persisted during refresh.

This is the package I am using.

https://pub.dev/packages/firebase

This is how I am authenticating with Firebase

  static Future<User> handleSignInEmail(String email, String password) async {
    await init();

    final UserCredential userCredential =
        await auth().signInWithEmailAndPassword(email.trim(), password);

    assert(userCredential.user != null);
    assert(await userCredential.user.getIdToken() != null);

    final User currentUser = await userCredential.user;
    assert(userCredential.user.uid == currentUser.uid);

    print('signInEmail succeeded: $userCredential.user');

    return userCredential.user;
  }

If I refresh the page and call the following method, the user that is returned is null:

  static Future<User> getFirebaseUser() async {
    await init();
    return await auth().currentUser;
  }

A similar implementation using Flutter Mobile works as expected. What am I missing on the Flutter Web implementation?

dazza5000
  • 7,075
  • 9
  • 44
  • 89

4 Answers4

29

Login happens automatically and it is handled by Firebase, however in async way. More details can be found in the official documentation:

https://firebase.google.com/docs/auth/web/auth-state-persistence

The trick is very simple. You need to wait for the first state change.

  static Future<User> getFirebaseUser() async {
    await init();
    //return await auth().currentUser;
    return await auth().onAuthStateChanged.first;
  }

It returns null if there is no signed-in user: https://firebase.google.com/docs/reference/js/firebase.auth.Auth.html#onauthstatechanged

My firebase version:

firebase: 5.0.4 #https://pub.dartlang.org/packages/firebase

I wrote this implementation which works on both mobile and web:

  static Future<FirebaseUser> getFirebaseUser() async {
    FirebaseUser firebaseUser = await FirebaseAuth.instance.currentUser();
    if (firebaseUser == null) {
      firebaseUser = await FirebaseAuth.instance.onAuthStateChanged.first;
    }
    return firebaseUser;
  }
dazza5000
  • 7,075
  • 9
  • 44
  • 89
Gedeon Gaal
  • 306
  • 3
  • 4
  • 1
    Thanks! Can confirm this working with `package:firebase_auth/firebase_auth.dart` & `firebase_auth: 0.15.5+2` – Oleksii Shliama Mar 05 '20 at 18:57
  • I was a bit confused by the answer (which did ended up working). You have to switch from: **return await auth().currentUser;** to: **return await auth().onAuthStateChanged.first;** This has nothing to do with await init(). Do we have to check for await FirebaseAuth.instance.currentUser? what is the benefit? Thank you! – snicz Jun 07 '20 at 10:58
  • 1
    For me persistence in maintained in same instance only. When I close browser window & relaunch the application session is not persisted. – Rushikesh Talokar Jun 28 '20 at 01:18
  • Any idea how to implement this if I use StreamProvider with value of Stream ? – adadion Jan 14 '21 at 07:46
  • 1
    @RushikeshTalokar did you solve this? I've the same issue that "when i close the browser window and then relaunch it session is not persisted" – Pablo Pantaleon Jun 06 '21 at 20:01
  • 1
    FirebaseAuth still loses its auth sesession after page refresh. I'm using firebase_auth: ^3.0.1. There's an open ticket for this issue: github.com/FirebaseExtended/flutterfire/issues/3356 – Omatt Aug 08 '21 at 08:43
  • 1
    I've the same problem :/ ... any workaround? – Pablo Pantaleon Aug 18 '21 at 03:45
  • On firebase_auth 3.3.9 the workaround would be: `final FirebaseAuth _auth = FirebaseAuth.instance; await _auth.authStateChanges().first;` – josue.0 Mar 08 '22 at 16:46
  • @RushikeshTalokar https://stackoverflow.com/a/73437019/11440760 check this to persist authentication after closing Chrome and opening a new debug session – Tiago Santos Aug 21 '22 at 17:59
4

Extension of https://stackoverflow.com/a/58158636

In the new library version, onAuthStateChanged getter is not available. Instead of that use authStateChanges().

User firebaseUser = await FirebaseAuth.instance.currentUser;
  if (firebaseUser == null) {
    firebaseUser = await FirebaseAuth.instance.authStateChanges().first;
  }

https://pub.dev/documentation/firebase_auth/latest/firebase_auth/FirebaseAuth/authStateChanges.html

Gowtham P
  • 41
  • 2
2

Firebase Dart Package

Import this pub and check the OS wherever auth is used

import 'package:firebase/firebase.dart' as firebase;
import 'package:firebase_auth/firebase_auth.dart';

if(Platform.isIOS || Platform.isAndroid) {
    FirebaseAuth.instance.createUserWithEmailAndPassword(email: email, password: password).then(() {
        //do something
    });
} else {
    firebase.auth().createUserWithEmailAndPassword(email, password).then(() {
        //do something
    });
}
JAY MEHTA
  • 89
  • 1
  • 3
  • This solved my problem thank you. DO NOT USE THE firebase_auth PACKAGE FOR WEB!! use the normal firebase package instead! – Emmett Deen Jan 24 '21 at 21:43
  • firebase_dart package is discontinued. Refrain from using it for new projects. – SNce Sep 04 '22 at 18:46
1

I'm using Flutter 3.0.5 and firebase_auth 3.6.4 and that's the only way I could make it work for web (Chrome):

class HomePage extends StatefulWidget {
  const HomePage();

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  User? currentUser;
  late StreamSubscription<User?> userSubscription;

  @override
  void initState() {
    super.initState();
    userSubscription = FirebaseAuth.instance.authStateChanges().listen((user) {
      if (user != null) {
        setState(() {
          currentUser = user;
        });
      }
    });
  }

  @override
  void dispose() {
    userSubscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return currentUser != null
        ? Text('User is logged in')
        : Text('User is not logged in');
  }
}

As I understand, you need to wait until FirebaseAuth.instance.authStateChanges() returns non null value, but in the the beginning it returns null because there's something that still initializes in the background. Same goes for FirebaseAuth.instance.currentUser, it will be null until some point, so if you check it immediately, you'll be stuck in unauthorized state unless you recheck it later somehow.

Katia
  • 11
  • 1