refreshSession method

Future<void> refreshSession({
  1. UserId? userId,
})

Refreshes Credentials of the account with the provided userId or of the active one, if userId is not provided.

Implementation

Future<void> refreshSession({UserId? userId}) async {
  final FutureOr<bool> futureOrBool = WebUtils.isLocked;
  final bool isLocked =
      futureOrBool is bool ? futureOrBool : await futureOrBool;

  userId ??= this.userId;
  final bool areCurrent = userId == this.userId;

  Log.debug(
    'refreshSession($userId) with `isLocked`: $isLocked',
    '$runtimeType',
  );

  try {
    // Wait for the lock to be released and check the [Credentials] again as
    // some other task may have already refreshed them.
    await WebUtils.protect(() async {
      Credentials? oldCreds;

      if (userId != null) {
        oldCreds = await _credentialsProvider.read(userId, refresh: true);
      }

      if (areCurrent) {
        oldCreds ??= credentials.value;
      }

      if (userId == null) {
        Log.debug(
          'refreshSession($userId): `userId` is `null`, unable to proceed',
          '$runtimeType',
        );

        return _refreshRetryDelay = _initialRetryDelay;
      }

      if (oldCreds != null) {
        accounts[userId]?.value = oldCreds;
      } else {
        accounts.remove(userId);
      }

      // Ensure the retrieved credentials are the current ones, or otherwise
      // authorize with those.
      if (oldCreds != null &&
          oldCreds.access.secret != credentials.value?.access.secret &&
          !_shouldRefresh(oldCreds)) {
        Log.debug(
          'refreshSession($userId): false alarm, applying the retrieved fresh credentials',
          '$runtimeType',
        );

        if (areCurrent) {
          await _authorized(oldCreds);
          status.value = RxStatus.success();
        } else {
          // [Credentials] of another account were refreshed.
          _putCredentials(oldCreds);
        }

        return _refreshRetryDelay = _initialRetryDelay;
      }

      if (isLocked) {
        Log.debug(
          'refreshSession($userId): acquired the lock, while it was locked, thus should proceed: ${_shouldRefresh(oldCreds)}',
          '$runtimeType',
        );

        if (!_shouldRefresh(oldCreds)) {
          // [Credentials] are fresh.
          return _refreshRetryDelay = _initialRetryDelay;
        }
      } else {
        Log.debug(
          'refreshSession($userId): acquired the lock, while it was unlocked',
          '$runtimeType',
        );
      }

      if (oldCreds == null) {
        // These [Credentials] were removed while we've been waiting for the
        // lock to be released.
        if (areCurrent) {
          router.go(_unauthorized());
        }

        return _refreshRetryDelay = _initialRetryDelay;
      }

      try {
        final Credentials data = await _authRepository.refreshSession(
          oldCreds.refresh.secret,
          reconnect: areCurrent,
        );

        if (areCurrent) {
          await _authorized(data);
        } else {
          // [Credentials] of not currently active account were updated,
          // just save them.
          //
          // Saving to local storage is safe here, as this callback is
          // guarded by the [WebUtils.protect] lock.
          await _credentialsProvider.upsert(data);
          _putCredentials(data);
        }

        _refreshRetryDelay = _initialRetryDelay;
        status.value = RxStatus.success();
      } on RefreshSessionException catch (_) {
        Log.debug(
          'refreshSession($userId): `RefreshSessionException` occurred, removing credentials',
          '$runtimeType',
        );

        if (areCurrent) {
          router.go(_unauthorized());
        } else {
          // Remove stale [Credentials].
          accounts.remove(oldCreds.userId);
          await _credentialsProvider.delete(oldCreds.userId);
        }

        _refreshRetryDelay = _initialRetryDelay;
        rethrow;
      }
    });
  } on RefreshSessionException catch (_) {
    _refreshRetryDelay = _initialRetryDelay;
    rethrow;
  } catch (e) {
    Log.debug(
      'refreshSession($userId): Exception occurred: $e',
      '$runtimeType',
    );

    // If any unexpected exception happens, just retry the mutation.
    await Future.delayed(_refreshRetryDelay);
    if (_refreshRetryDelay.inSeconds < 12) {
      _refreshRetryDelay = _refreshRetryDelay * 2;
    }

    await refreshSession(userId: userId);
  }
}