[Firebase Authentication]ログイン済みなのにgetCurrentUserがnullになるときの対処
はじめに
Firebase AuthenticationでgetCurrentUser
をつかったログインチェックを実装する際、ログイン済みなのにnullが返ることがあってつまずいたので、原因と対策を記載します。
原因
本事象の原因は、Authオブジェクトの初期化が完了していない段階でgetCurrentUser
を利用したことでした。Firebase公式ドキュメントにも記載がありました(小さい...)。
注: currentUser が null になる原因としては、Auth オブジェクトの初期化が完了していないことも考えられます。オブザーバーを使用してユーザーのログイン ステータスを追跡している場合は、この状況に対処する必要はありません。
一般的にgetCurrentUser
がnullを返すのはログインしていない状態ですが、実際には以下2つの状態が存在するということになります。
- ログインしていない状態
- ログインしている状態(Authオブジェクトの初期化が終わっていない場合)
対処
最も素直な対処としては、onAuthStateChanged
でStreamをつかったログイン状態のチェックをすることです。わざわざgetCurrentUser
を呼ばなくても、ユーザーのログインなどのイベントが発生したタイミングでuser
が通知されます。
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
ただ私のユースケースではアプリ内(Flutter Web)で任意・複数のタイミングでgetCurrentUser
を利用したいというニーズがあったことから、ルートレベルでStreamBuilderをかませてonAuthStateChanged
をしておき、ユーザーを取得したいタイミングではこれまで通りgetCurrentUser
する作りにしてみました。(もっとこうした方が良いなどあればアドバイスいただきたいです!)
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return StreamBuilder<User?>(
// Call Auth.onAuthStateChanged inside.
stream: loginController.authUserStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ChangeNotifierProvider<LoginViewModel>.value(
value: loginController,
child: MaterialApp.router(
// Use go_router for Navigator2.0.
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
title: 'My App',
),
);
},
);
}
// 中略
late final _router = GoRouter(
routes: [
GoRoute(
path: Paths.login,
pageBuilder: (context, state) => MaterialPage<void>(
key: state.pageKey,
child: const LoginScreen(),
),
),
],
redirect: (state) {
// Call Auth.getCurrentUser inside.
loginController.checkLogin();
if (loginController.isNotLoggedIn) {
return Paths.login;
}
return null;
},
);
}
別クラスに切り出しているので分かりづらいですが、ルートのbuild
メソッドのなかでonAuthStateChanged
を呼んで初期化の完了をまち、リダイレクト処理内(go_routerのredirect
パラメータ)からgetCurrentUser
を利用しています。
Discussion