Reputation: 987
I'm using Firebase Auth plugin for authentication of my Flutter app.
Until the upgrade (not sure if relevant) to the latest Firebase auth version:
firebase_core: ^0.5.0
firebase_auth: ^0.18.0+1
everything worked fine.
Now I got, for the first time, Sentry error:
FirebaseAuthException: [firebase_auth/user-token-expired] The user's credential is no longer valid. The user must sign in again.
File "exception.dart", line 20, in catchPlatformException
File "zone.dart", line 1198, in _rootRunUnary
File "zone.dart", line 1100, in _CustomZone.runUnary
File "future_impl.dart", line 160, in _FutureListener.handleError
File "future_impl.dart", line 708, in Future._propagateToListeners.handleError
File "future_impl.dart", line 729, in Future._propagateToListeners
File "future_impl.dart", line 537, in Future._completeError
File "async_patch.dart", line 47, in _AsyncAwaitCompleter.completeError
File "platform_channel.dart", in MethodChannel.invokeMapMethod
File "<asynchronous suspension>"
File "unparsed"
How can this happen? The user said, that he didn't use this app for a few days. As I understand Firebase Authentication documentation, the auth token automatically gets refreshed with the refresh token.
How can I mitigate this issue?
Where/how can I catch this exception to redirect a user to the login screen?
Upvotes: 0
Views: 4605
Reputation: 785
You can do couple of things ,
First:
when a user starts your app , you should check first whether the user is already signed in or not , if he is ,then sign in silently ,if not , then send the user to sign in page , below is an example from an app I build four months ago (you will have to add google_sign_in dependency to your app)
class _StartingPageState extends State<StartingPage> {
Future<dynamic> decideStartingPage() async {
bool isUserSignedIn = await googleSignIn.isSignedIn();
if (isUserSignedIn == true) {
FirebaseUser futurefbuser =await getCurrentFirebaseUser();
assignFireBaseUser(futurefbuser);
await googleSignIn.signInSilently();
return HomeTabView();
}
else
return LoginPage();
}
Future<dynamic> startingpage;
@override
void initState(){
super.initState();
startingpage=decideStartingPage();
FirebaseAdMob.instance.initialize(appId: "ca-app-pub-...........");
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future:startingpage,
builder: (BuildContext ctx, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center( child : const CircularProgressIndicator());
}
else
return snapshot.data;
}
);
}
}
Second:
The second thing you can do is to save the access token and(or) refresh token you get when a user signs in, when that expires you can get an another access token using the refresh token . That means a little extra work for you as you will have to save the token in shared preferences or in a json file .
Now where to add the code for that handles the above procedure ? You have to identify the first thing that only an authenticated user can do like writing to the database , it is quite possible that your app is throwing exception at that because the user is unauthenticated for that operation . Using try , catch and finally you can sign in the user again without needing the user to do anything . for more info visit this
In first case user would need to explicitly sign in again , the second method requires extra work for you but more convenient for the user . Choice is yours .
Upvotes: 3