felangel / fresh

🍋 A token refresh library for Dart.
https://github.com/felangel/fresh
359 stars 56 forks source link

Possible race condition when using custom tokenStorage #100

Closed LennardDeurman closed 3 months ago

LennardDeurman commented 5 months ago

Hi! First of all thanks for creating this package, this has been pretty helpful in my projects.

I've implemented the Fresh interceptor with a custom TokenStorage based on FlutterSecureStorage. After a successful login I call the write method on the TokenStorage. When restarting the app, I would expect the token to be retrieved from the store before being returned, but the token is being returned while the Fresh is still in the idle status. This returns in an empty token, which will only later be updated after the authenticationStatus changes.


  final StreamController<AuthenticationStatus> _controller =
      StreamController<AuthenticationStatus>.broadcast()
        ..add(AuthenticationStatus.initial);

  /// Setter for the [TokenStorage] instance.
  set tokenStorage(TokenStorage<T> tokenStorage) {
    _tokenStorage = tokenStorage..read().then(_updateStatus);
  }

  /// Returns the current token.
  Future<T?> get token async {
    if (_authenticationStatus != AuthenticationStatus.initial) return _token;
    await authenticationStatus.first;
    return _token;
  }

The await authenticationStatus.first; now actually awaits the AuthenticationStatus.initial status while I would expect any status that is not an AuthenticationStatus.initial.

In my project I fixed this by adding the following:

 /// Ensures that fresh is initialized and not longer loading the token.
  Future<void> ensureReady() async {
    await fresh.authenticationStatus.firstWhere(
      (authenticationStatus) => authenticationStatus != AuthenticationStatus.initial,
    );
  }

I think it would be nice if the token getter awaits the first authenticationStatus that is not AuthenticationStatus.initial

  /// Returns the current token.
  Future<T?> get token async {
    if (_authenticationStatus != AuthenticationStatus.initial) return _token;
   await fresh.authenticationStatus.firstWhere(
      (authenticationStatus) => authenticationStatus != AuthenticationStatus.initial,
    );
    return _token;
  }
vasilich6107 commented 3 months ago

hey @LennardDeurman probably this will help you https://github.com/felangel/fresh/pull/96

felangel commented 3 months ago

Closed by #96