FirebaseAuthState constructor

FirebaseAuthState(
  1. FirebaseAuthAppModule authModule,
  2. User? _currentFbUser,
  3. UserDetails? currentProfile, {
  4. required Stream<ErrorStack> appErrors,
})

Implementation

FirebaseAuthState(
    this.authModule, this._currentFbUser, UserDetails? currentProfile,
    {required Stream<ErrorStack> appErrors})
    : _loginProviders = authModule.loginProviders,
      _currentUserProfile = AuthUserProfile(
        _currentFbUser,
        currentProfile,
        AuthEventSource.initial,
      ) {
  /// This block receives all events from the auth state change clearing  house,
  /// loads the user profile if necessary, and pipes the results to the [onUserChange]
  /// stream
  authStateChange.asyncMap<AuthUserProfile>((authEvent) async {
    final fbUser = authEvent.fbUser;
    log.warning(
        "User changed: ${fbUser?.displayName ?? fbUser?.email ?? 'None'}");
    if (fbUser == null) {
      _currentUserProfile = AuthUserProfile.empty(authEvent.source);
      return _currentUserProfile!;
    } else {
      // Fetch the profile first thing.
      try {
        final profile = await authModule.loadProfile(fbUser);
        final aup = AuthUserProfile(fbUser, profile, authEvent.source);
        _currentUserProfile = aup;
        log.warning("User loaded from server: ${profile.name}");
        log.info("Profile ${profile.name} loaded");

        final token = await fbUser.getIdToken();
        final meta = fbUser.metadata;
        log.info("Last sign in: ${meta.lastSignInTime}");
        log.info("Token provider: ${fbUser.providerData}");
        log.info("Updated auth token: $token");
        return aup;
      } on ApiResponseException catch (e) {
        if (e.statusCode == kPreconditionFailed ||
            e.statusCode == kPreconditionRequired) {
          log.info(
              "Authenticated, but no account exists yet.  When the account is finished "
              "being created, you will have to manually push that userProfile through");
          return AuthUserProfile(fbUser, null, authEvent.source,
              status: AuthStatus.partial);
        } else if (e.statusCode == 401) {
          log.warning(
              "Got unauthorized getting profile. ${e.message} ? '(No message)' Logging you out!");
        }
        return AuthUserProfile.empty(authEvent.source);
      } catch (e, stack) {
        log.severe("Problems logging in: $e", e, stack);
        return AuthUserProfile.empty(AuthEventSource.failed);
      }
    }
  }).listen((mapped) {
    _currentUserProfile = mapped;
    userStateChangedStream.current = mapped;
  }, cancelOnError: false).watch(this);

  /// Reads from firebase auth change events, and pushes to clearing house
  fb.FirebaseAuth.instance.idTokenChanges().listen((event) async {
    log.info("Firebase sent auth state change: $event");
    if (_currentFbUser?.uid != event?.uid) {
      _currentFbUser = event;
      FirebaseApiAuth.user = event;
      _authStateClearinghouse
          .add(AuthEvent(event, source: AuthEventSource.framework));
    } else {
      log.info("Ignoring duplicate state change for ${event?.uid}");
    }
  }).watch(this);

  /// Listen for authentication failures and remove logged in users
  appErrors
      .where((_) {
        return _.exception is ApiResponseException;
      })
      .where((_) => (_.exception as ApiResponseException).statusCode == 401)
      .listen((_) {
        log.warning("Got unauthenticated error from API.");
        _authStateClearinghouse.add(AuthEvent.error(_));
        _unauthenticatedErrors.add(true);
      })
      .watch(this);

  Future.microtask(() {
    initializeState(fbUser: _currentFbUser, profile: _currentUserProfile);
  });
}