-1

i am building application using Flutter, GraphQL and Firebase with NodeJS API. In my applicaiton i have implmented Firebase auth and token refresh and my nodejs api validates it. My token refresh is with intervals of 3500 seconds (where token is valid for 3600). However in some situation (i do not kno why) after i am leaving app open for half a day i am starting to receiving message from token validation that i have to refresh token (not sure how to catch it because while testing intervals refresh works just fine)...

So what is most common way to refresh token in apps? maybe apps are automatically hybernated while inactive and refresh timers stopped? If yes - than what event should i use to to catch app became active again?

Thanks

p.s. After some research i realised what is the problem with token refresh in interval - while phone is active refresh interval works fine. If I lock phones screen time interval stops. After oepning phone again time interval starts again FROM BEGINNING. Here main problem occurs - since FB token is valid for 1 hour so there is 99% that iser will turn his phone screen of and after he turns it again timer will start from the beginning - but token will be expired...

Do you know any flutter events that are triggered after phone / app become active again?

  • Please edit the question to limit yourself to just one question per post, or it might get closed as "needs focus". I will answer your first question here, and I suggest that you ask the second one separately. – Doug Stevenson Jul 30 '20 at 21:14

3 Answers3

2

An absolute golden rule in any OAuth client is to implement the following behaviour:

  • If you get a 401 from an API
  • Try to get a new access token - once only
  • Retry the API call - once only

Some sample Android code of mine that does this.

The requirement is universal though, regardless of type of client or technology used. In my own apps I never read the token expiry and just let 401s happen. This is a resilient solution, since 401s can occur for many reasons.

I would recommend focusing on the requirements first and the technology second

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
0

Firebase Auth will automatically refresh the user's ID token ever hour, or as needed. You don't have to write any code for that.

The Android, iOS, and JS libraries all provide a way to listen to changes to the ID token, but Flutter apparently does not expose this API. However, you can still periodically call getIdToken(false) to get the current ID token. Just don't hold on to it for longer than necessary.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • so basically on app open i have to fetch new token nad start listening to onAuthStateChanged (dart library still uses this stream) which passes me FirebaseUser as a result where i should do FirebaseAuth.instance.onAuthStateChanged.listen((event) async { token = (await event.getIdToken()).token; }); – Vytautas Pranskunas Jul 30 '20 at 21:22
  • Sorry, I edited my question because I don't see that Flutter exposes an ID token change listener. You can just call getIdToken periodically instead. – Doug Stevenson Jul 30 '20 at 21:22
  • You will want to get the ID token at the time you need it, so that you are certain you have a fresh token. – Doug Stevenson Jul 30 '20 at 21:23
  • As you can see it exposes AuthStateChanged - so it would not be triggered when token is refreshed? And why you are passing false to getIdToken? false means do not refresh - but i want to refresh it as in my original question. – Vytautas Pranskunas Jul 30 '20 at 21:27
  • I think onAuthStateChanged should do the same - "Adds an observer for changes to the signed-in user's ID token, which includes sign-in, sign-out, and token refresh events. This method has the same behavior as firebase.auth.Auth.onAuthStateChanged had prior to 4.0.0." – Vytautas Pranskunas Jul 30 '20 at 21:28
  • That's a very old behavior and I would be surprised if it works like this for Flutter. – Doug Stevenson Jul 30 '20 at 21:46
  • It works at least for sign in / sign out i will test for token refresh - but according to firebase it should. But as i mentioned in main question - my main concern running periodic interval for fetching token that after some time when leaving my app open for some reason i have not valid token anymore... Not sure if this because Android stops some processes in app or anything else... This issue forces me to look for possible solutions – Vytautas Pranskunas Jul 30 '20 at 21:51
  • The documentation for flutter doesn't say it works like this. The behavior you're talking about is again very old, predating flutter. https://pub.dev/documentation/firebase_auth/latest/firebase_auth/FirebaseAuth/onAuthStateChanged.html – Doug Stevenson Jul 30 '20 at 21:53
  • Yes i see it now. However it does not help me with main concern ;) – Vytautas Pranskunas Jul 30 '20 at 21:59
  • Right, that's why my answer suggests that you make a call to get the token at the time you need to use it. – Doug Stevenson Jul 30 '20 at 22:01
  • I do not think that is good idea because if i do normal API communication so with each call there will be additional call to retrieve token. Thats a bit verhead i believe because token is valid for 30 minutes. D – Vytautas Pranskunas Jul 31 '20 at 08:31
  • I have tuned my main question with additional data - maybe you have thoughts? – Vytautas Pranskunas Jul 31 '20 at 14:45
-1

so after research i ended up solving it with 2 steps.

  1. Initializing periodic token refresh with this code:

    void _handleAuthStateChanged(FirebaseUser e) async {
         if (e == null) {
         _authentication = null;
    } else {
     /// Get token
     final firebaseToken = await e.getIdToken(refresh: true);
    
     /// Setup token refresh
     firebaseToken.expirationTime.asFuture(() async {
         var user = await FirebaseAuth.instance.currentUser();
         _handleAuthStateChanged(user);
     });
    
     extension DateTimeFutureX on DateTime {
         Future<T> asFuture<T>([FutureOr<T> Function() computation]) async {
         final difference = this.difference(DateTime.now());
         return Future.delayed(difference, computation);
     }
    }
    
  1. and refetching token when app is resumed in main.dart

    @override
    Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
         super.didChangeAppLifecycleState(state);
         if (state == AppLifecycleState.resumed) {
             await _authService.refreshToken();
         }
     }