LCOV - code coverage report
Current view: top level - infrastructure - firebase_auth_facade.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 13 130 10.0 %
Date: 2021-04-25 20:40:09 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : import 'dart:convert';
       3             : 
       4             : import 'package:dartz/dartz.dart';
       5             : import 'package:firebase_auth/firebase_auth.dart';
       6             : import 'package:flutter/foundation.dart';
       7             : import 'package:flutter_firebase_auth_facade/flutter_firebase_auth_facade.dart';
       8             : import 'package:flutter_firebase_auth_facade/src/utils/constants.dart';
       9             : import 'package:google_sign_in/google_sign_in.dart';
      10             : import 'package:http/http.dart' as http;
      11             : import 'package:sign_in_with_apple/sign_in_with_apple.dart' as apple;
      12             : import 'package:url_launcher/url_launcher.dart';
      13             : import 'package:uni_links2/uni_links.dart';
      14             : 
      15             : class FirebaseAuthFacade implements IAuthFacade {
      16           1 :   FirebaseAuthFacade(
      17             :     this._firebaseAuth,
      18             :     this._googleSignIn,
      19             :   );
      20             : 
      21             :   /// This is the clientID in GitHub OAuth app
      22             :   late String githubClientId;
      23             : 
      24             :   /// The secret generated in the github OAuth apps
      25             :   late String githubSecret;
      26             : 
      27             :   ///can be found in signin method of firebase,
      28             :   ///it's required because it's needed for appleSignin
      29             :   late String callbackUrl;
      30             : 
      31             :   ///Only for ios auth on Android
      32             :   late String appleClientId;
      33             : 
      34             :   /// DI of the firebaseAuth plugin
      35             :   final FirebaseAuth _firebaseAuth;
      36             : 
      37             :   /// DI of the GoogleSignIn plugin
      38             : 
      39             :   final GoogleSignIn _googleSignIn;
      40             : 
      41           0 :   void call({
      42             :     required callbackUrl,
      43             :     githubClientId = '',
      44             :     githubSecret = '',
      45             :     appleClientId = '',
      46             :   }) {
      47           0 :     this.githubSecret = githubSecret;
      48           0 :     this.githubClientId = githubClientId;
      49           0 :     this.appleClientId = appleClientId;
      50           0 :     this.callbackUrl = callbackUrl;
      51             :   }
      52             : 
      53             :   /// Deep link stream subscription
      54             :   StreamSubscription? _streamSubscription;
      55             : 
      56             :   StreamController<Either<AuthFailure, Unit>>? githubloginStreamController;
      57             : 
      58             :   /// get the current signed in user,
      59             :   /// return null if unauthenticated
      60           1 :   @override
      61           2 :   User? getSignedInUser() => _firebaseAuth.currentUser;
      62             : 
      63             :   /// Checking if user is anonym or not
      64           1 :   @override
      65           3 :   bool isAnonymous() => _firebaseAuth.currentUser!.isAnonymous;
      66             : 
      67             :   ///Register with Email and password
      68             :   /// return [Future<Either<AuthFailure, Unit>>]
      69             :   @override
      70           1 :   Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword(
      71             :       {required String email, required String password}) async {
      72             :     try {
      73           1 :       final previousUser = getSignedInUser();
      74             :       if (previousUser != null) {
      75             :         final authCreds =
      76           0 :             EmailAuthProvider.credential(email: email, password: password);
      77           0 :         await previousUser.linkWithCredential(authCreds);
      78             :       } else {
      79           3 :         await _firebaseAuth.createUserWithEmailAndPassword(
      80             :           email: email,
      81             :           password: password,
      82             :         );
      83             :       }
      84             :       return const Right(unit);
      85           1 :     } on FirebaseAuthException catch (e) {
      86           2 :       if (e.code == kFirebaseCodeEmailAlreadyInUse) {
      87             :         return const Left(AuthFailure.emailAlreadyInUse());
      88           2 :       } else if (e.code == kFirebaseCodeInvalidEmail) {
      89             :         return const Left(AuthFailure.invalidEmail());
      90           2 :       } else if (e.code == kFirebaseCodeWeakPassword) {
      91             :         return const Left(AuthFailure.weakPassword());
      92           2 :       } else if (e.code == kFirebaseCodeOperationNotAllowed) {
      93             :         return const Left(AuthFailure.operationNotAllowed());
      94           0 :       } else if (e.code == kFirebasecodeInvalidCredentials) {
      95             :         return const Left(AuthFailure.invalidCredentials());
      96             :       } else {
      97             :         return const Left(AuthFailure.serverError());
      98             :       }
      99             :     }
     100             :   }
     101             : 
     102             :   /// Send an email to reset the password
     103           0 :   @override
     104             :   Future<void> resetPassword({required String email}) =>
     105           0 :       _firebaseAuth.sendPasswordResetEmail(email: email);
     106             : 
     107             :   /// Login out
     108           0 :   @override
     109             :   Future<void> signedOut() =>
     110           0 :       Future.wait([_googleSignIn.signOut(), _firebaseAuth.signOut()]);
     111             : 
     112             :   /// Sign in as anonymous
     113             :   /// return [Future<Either<AuthFailure, Unit>>]
     114             :   @override
     115           0 :   Future<Either<AuthFailure, Unit>> signInWithAnon() async {
     116             :     try {
     117           0 :       await _firebaseAuth.signInAnonymously();
     118           0 :       return right(unit);
     119           0 :     } on FirebaseAuthException catch (e) {
     120           0 :       if (e.code == kFirebaseCodeOperationNotAllowed) {
     121             :         return const Left(AuthFailure.operationNotAllowed());
     122             :       }
     123             :       return const Left(AuthFailure.serverError());
     124             :     }
     125             :   }
     126             : 
     127             :   /// Sign in with Apple
     128             :   /// return [Future<Either<AuthFailure, Unit>>]
     129             :   @override
     130           0 :   Future<Either<AuthFailure, Unit>> signInWithApple() async {
     131             :     try {
     132           0 :       final oAuthProvider = OAuthProvider(appleProvider);
     133             :       if (kIsWeb) {
     134           0 :         final result = await _firebaseAuth.signInWithPopup(oAuthProvider);
     135           0 :         var credential = result.credential;
     136           0 :         await _firebaseAuth.signInWithCredential(credential!);
     137           0 :         return right(unit);
     138             :       } else {
     139           0 :         final credential = await apple.SignInWithApple.getAppleIDCredential(
     140           0 :           scopes: [
     141             :             apple.AppleIDAuthorizationScopes.email,
     142             :             apple.AppleIDAuthorizationScopes.fullName,
     143             :           ],
     144           0 :           webAuthenticationOptions: apple.WebAuthenticationOptions(
     145           0 :             clientId: appleClientId,
     146           0 :             redirectUri: Uri.parse(callbackUrl),
     147             :           ),
     148             :         );
     149           0 :         final newCredentials = oAuthProvider.credential(
     150           0 :           idToken: credential.identityToken,
     151           0 :           accessToken: credential.authorizationCode,
     152             :         );
     153           0 :         final previousUser = getSignedInUser();
     154             :         if (previousUser != null) {
     155           0 :           await previousUser.linkWithCredential(newCredentials);
     156             :         } else {
     157           0 :           await _firebaseAuth.signInWithCredential(newCredentials);
     158             :         }
     159           0 :         return right(unit);
     160             :       }
     161           0 :     } on apple.SignInWithAppleException catch (e) {
     162           0 :       print(e);
     163           0 :       if (e is apple.SignInWithAppleNotSupportedException) {
     164             :         return const Left(AuthFailure.wrongIosVersion());
     165             :       }
     166             :       return const Left(AuthFailure.serverError());
     167           0 :     } on FirebaseAuthException catch (e) {
     168           0 :       if (e.code.contains('credential-already-in-use')) {
     169             :         return const Left(AuthFailure.emailAlreadyInUse());
     170             :       }
     171             :       return const Left(AuthFailure.serverError());
     172             :     }
     173             :   }
     174             : 
     175             :   /// Sign in with user and password
     176             :   /// return [Future<Either<AuthFailure, Unit>>]
     177             :   @override
     178           0 :   Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword(
     179             :       {required String email, required String password}) async {
     180             :     try {
     181           0 :       await _firebaseAuth.signInWithEmailAndPassword(
     182             :         email: email,
     183             :         password: password,
     184             :       );
     185             :       return const Right(unit);
     186           0 :     } on FirebaseAuthException catch (e) {
     187           0 :       if (e.code == 'user-not-found' || e.code == 'wrong-password') {
     188             :         return const Left(AuthFailure.invalidEmailAndPasswordCombination());
     189             :       } else {
     190             :         return const Left(AuthFailure.serverError());
     191             :       }
     192             :     }
     193             :   }
     194             : 
     195             :   /// Sign in with GitHub
     196             :   /// return [Future<Either<AuthFailure, Unit>>]
     197             :   @override
     198           0 :   Future<Either<AuthFailure, Unit>> signInWithGitHub() async {
     199             :     try {
     200           0 :       githubloginStreamController =
     201           0 :           StreamController<Either<AuthFailure, Unit>>();
     202           0 :       var provider = GithubAuthProvider()
     203           0 :         ..addScope('repo')
     204           0 :         ..setCustomParameters({'allow_signup': false});
     205             : 
     206             :       if (kIsWeb) {
     207           0 :         final result = await _firebaseAuth.signInWithPopup(provider);
     208           0 :         var credential = result.credential;
     209           0 :         await _firebaseAuth.signInWithCredential(credential!);
     210           0 :         return right(unit);
     211             :       } else {
     212           0 :         final url = '$githubAuthUrl'
     213           0 :             '$githubClientId&$githubScope';
     214           0 :         await _streamSubscription?.cancel();
     215           0 :         _streamSubscription = linkStream.listen((deeplink) async {
     216           0 :           final code = _getCodeFromGitHubLink(deeplink!);
     217           0 :           await _loginWithGitHub(code);
     218             :         });
     219           0 :         if (await canLaunch(url)) {
     220           0 :           print('Launchunbg url');
     221           0 :           await launch(url);
     222             :         } else {
     223           0 :           return left(const AuthFailure.serverError());
     224             :         }
     225             :         final failureOrSuccess =
     226           0 :             await githubloginStreamController!.stream.first;
     227           0 :         await githubloginStreamController!.close();
     228           0 :         print(failureOrSuccess);
     229             :         return failureOrSuccess;
     230             :       }
     231             :     } catch (e) {
     232           0 :       print(e);
     233           0 :       return left(const AuthFailure.serverError());
     234             :     }
     235             :   }
     236             : 
     237             :   /// Sign in with google
     238             :   /// return [Future<Either<AuthFailure, Unit>>]
     239             :   @override
     240           0 :   Future<Either<AuthFailure, Unit>> signInWithGoogle() async {
     241             :     try {
     242           0 :       final googleUser = await _googleSignIn.signIn();
     243             :       if (googleUser == null) {
     244             :         return const Left(AuthFailure.cancelledByUser());
     245             :       }
     246           0 :       final googleAuthentication = await googleUser.authentication;
     247           0 :       final authCredential = GoogleAuthProvider.credential(
     248           0 :           idToken: googleAuthentication.idToken,
     249           0 :           accessToken: googleAuthentication.accessToken);
     250           0 :       final previousUser = getSignedInUser();
     251             :       if (previousUser != null) {
     252           0 :         await previousUser.linkWithCredential(authCredential);
     253             :       } else {
     254           0 :         await _firebaseAuth.signInWithCredential(authCredential);
     255             :       }
     256           0 :       return right(unit);
     257           0 :     } on FirebaseAuthException catch (e) {
     258           0 :       print(e);
     259           0 :       if (e.code.contains('credential-already-in-use')) {
     260             :         return const Left(AuthFailure.emailAlreadyInUse());
     261             :       }
     262             :       return const Left(AuthFailure.serverError());
     263             :     }
     264             :   }
     265             : 
     266             :   //// Stream of the user current state
     267             :   @override
     268           0 :   Stream<User?> userState() async* {
     269           0 :     yield* _firebaseAuth.authStateChanges();
     270             :   }
     271             : 
     272           0 :   String _getCodeFromGitHubLink(String link) =>
     273           0 :       link.substring(link.indexOf(RegExp('code=')) + 5);
     274             : 
     275           0 :   Future<void> _loginWithGitHub(String code) async {
     276             :     try {
     277           0 :       final response = await http.post(
     278           0 :           Uri.parse('https://github.com/login/oauth/access_token'),
     279           0 :           headers: {
     280             :             'Content-Type': 'application/json',
     281             :             'Accept': 'application/json'
     282             :           },
     283           0 :           body: '{"client_id":"$githubClientId",'
     284           0 :               '"client_secret":"$githubSecret","code":"$code"}');
     285           0 :       final body = response.body;
     286           0 :       final Map<String, dynamic> bodymap = json.decode(body);
     287           0 :       final token = bodymap['access_token'];
     288           0 :       final AuthCredential credential = GithubAuthProvider.credential(
     289             :         token,
     290             :       );
     291           0 :       await _firebaseAuth.signInWithCredential(credential);
     292           0 :       print(getSignedInUser());
     293           0 :       githubloginStreamController!.add(right(unit));
     294           0 :       await _streamSubscription?.cancel();
     295           0 :     } on FirebaseAuthException catch (e) {
     296           0 :       if (e.code == kFirebaseCodeEmailAlreadyInUse) {
     297           0 :         githubloginStreamController!
     298           0 :             .add(const Left(AuthFailure.emailAlreadyInUse()));
     299           0 :       } else if (e.code == kFirebaseCodeInvalidEmail) {
     300           0 :         githubloginStreamController!
     301           0 :             .add(const Left(AuthFailure.invalidEmail()));
     302           0 :       } else if (e.code == kFirebaseCodeWeakPassword) {
     303           0 :         githubloginStreamController!
     304           0 :             .add(const Left(AuthFailure.weakPassword()));
     305           0 :       } else if (e.code == kFirebaseCodeOperationNotAllowed) {
     306           0 :         githubloginStreamController!
     307           0 :             .add(const Left(AuthFailure.operationNotAllowed()));
     308           0 :       } else if (e.code == kFirebasecodeInvalidCredentials) {
     309           0 :         githubloginStreamController!
     310           0 :             .add(const Left(AuthFailure.invalidCredentials()));
     311             :       } else {
     312           0 :         githubloginStreamController!.add(const Left(AuthFailure.serverError()));
     313             :       }
     314             :     }
     315             :   }
     316             : }

Generated by: LCOV version 1.15