diff --git a/packages/authentication_repository/lib/src/authentication_repository.dart b/packages/authentication_repository/lib/src/authentication_repository.dart index d37d3c234..353250f4d 100644 --- a/packages/authentication_repository/lib/src/authentication_repository.dart +++ b/packages/authentication_repository/lib/src/authentication_repository.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:authentication_repository/authentication_repository.dart'; import 'package:firebase_auth/firebase_auth.dart' as fb; +import 'package:flutter/foundation.dart'; +import 'package:rxdart/rxdart.dart'; /// {@template authentication_repository} /// Repository to manage authentication. @@ -11,10 +13,13 @@ class AuthenticationRepository { AuthenticationRepository({ fb.FirebaseAuth? firebaseAuth, }) : _firebaseAuth = firebaseAuth ?? fb.FirebaseAuth.instance, - _userController = StreamController.broadcast(); + userController = BehaviorSubject(); final fb.FirebaseAuth _firebaseAuth; - final StreamController _userController; + + /// [BehaviorSubject] with the [User]. + @visibleForTesting + final BehaviorSubject userController; StreamSubscription? _firebaseUserSubscription; /// Stream of [User] which will emit the current user when @@ -24,12 +29,12 @@ class AuthenticationRepository { Stream get user { _firebaseUserSubscription ??= _firebaseAuth.authStateChanges().listen((firebaseUser) { - _userController.add( + userController.add( firebaseUser?.toUser ?? User.unauthenticated, ); }); - return _userController.stream; + return userController.stream; } /// Stream of id tokens that can be used to authenticate with Firebase. @@ -51,16 +56,25 @@ class AuthenticationRepository { Future signInAnonymously() async { try { final userCredential = await _firebaseAuth.signInAnonymously(); - _userController.add(userCredential.toUser); + userController.add(userCredential.toUser); } on Exception catch (error, stackTrace) { throw AuthenticationException(error, stackTrace); } } + /// Sign in the user anonymously. + /// + /// If the sign in fails, an [AuthenticationException] is thrown. + Future signOut() async { + await _firebaseAuth.signOut(); + + userController.add(User.unauthenticated); + } + /// Disposes any internal resources. void dispose() { _firebaseUserSubscription?.cancel(); - _userController.close(); + userController.close(); } } diff --git a/packages/authentication_repository/pubspec.yaml b/packages/authentication_repository/pubspec.yaml index 369fdb43e..ca8a483cc 100644 --- a/packages/authentication_repository/pubspec.yaml +++ b/packages/authentication_repository/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.8 + rxdart: ^0.27.7 dev_dependencies: flutter_test: diff --git a/packages/authentication_repository/test/src/authentication_repository_test.dart b/packages/authentication_repository/test/src/authentication_repository_test.dart index 7e642b40c..7a168ac43 100644 --- a/packages/authentication_repository/test/src/authentication_repository_test.dart +++ b/packages/authentication_repository/test/src/authentication_repository_test.dart @@ -139,6 +139,30 @@ void main() { }); }); + group('signOut', () { + test('calls signOut on FirebaseAuth', () async { + when(() => firebaseAuth.signOut()).thenAnswer((_) async {}); + + await authenticationRepository.signOut(); + verify(() => firebaseAuth.signOut()).called(1); + }); + + test('updates user with unauthenticated', () async { + when(() => firebaseAuth.signOut()).thenAnswer((_) async {}); + + await authenticationRepository.signOut(); + + await expectLater( + authenticationRepository.userController.stream, + emitsInOrder( + [ + User.unauthenticated, + ], + ), + ); + }); + }); + group('dispose', () { test('cancels internal subscriptions', () async { final controller = StreamController();