Created
March 7, 2021 08:50
-
-
Save definitelyme/008945386999d691f725959b64b280e1 to your computer and use it in GitHub Desktop.
Firebase Authentication codes for Flutter & Dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:dartz/dartz.dart'; | |
import 'package:data_connection_checker/data_connection_checker.dart'; | |
import 'package:eazox/features/auth/domain/core/auth.dart'; | |
import 'package:eazox/features/auth/domain/entities/fields/exports.dart'; | |
import 'package:eazox/features/home/domain/entities/user/user.dart'; | |
import 'package:eazox/utils/utils.dart'; | |
import 'package:firebase_auth/firebase_auth.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:flutter_facebook_login/flutter_facebook_login.dart'; | |
import 'package:google_sign_in/google_sign_in.dart'; | |
import 'package:injectable/injectable.dart'; | |
import 'package:meta/meta.dart'; | |
import 'firebase_user_extension.dart'; | |
@LazySingleton(as: AuthFacade) | |
class FirebaseAuthImpl implements AuthFacade { | |
static const String USER_EMAIL_PERMISSION = "email"; | |
static const String USER_PROFILE_PERMISSION = "public_profile"; | |
static const String USER_BIRTHDAY_PERMISSION = "user_birthday"; | |
static const String USER_FRIENDS_PERMISSION = "user_friends"; | |
final FirebaseAuth _firebaseAuth; | |
final GoogleSignIn _googleSignIn; | |
final FacebookLogin _facebookLogin; | |
final DataConnectionChecker _connectionChecker; | |
const FirebaseAuthImpl( | |
this._firebaseAuth, | |
this._googleSignIn, | |
this._connectionChecker, | |
this._facebookLogin, | |
); | |
Future<void> _checkForStableInternet() async { | |
var hasStableInternet = await _connectionChecker.hasConnection; | |
if (!hasStableInternet) throw AuthFailure.noInternetConnection(); | |
} | |
@override | |
Future<Option<User>> get currentUser async => _firebaseAuth.currentUser().then((firebaseUser) => optionOf(firebaseUser?.toBaseUser())); | |
@override | |
Stream<Option<User>> get onAuthStateChanged => _firebaseAuth.onAuthStateChanged.map((user) => optionOf(user?.toBaseUser())); | |
@override | |
Future<Either<AuthFailure, Unit>> createAccount({@required EmailAddress emailAddress, @required Password password}) async { | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
await _firebaseAuth.createUserWithEmailAndPassword(email: emailAddress.getOrCrash, password: password.getOrCrash); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e, email: emailAddress); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> login({@required EmailAddress emailAddress, @required Password password}) async { | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
await _firebaseAuth.signInWithEmailAndPassword(email: emailAddress.getOrCrash, password: password.getOrCrash); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e, email: emailAddress); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> updateProfileInfo({@required DisplayName displayName, String photoUrl}) async { | |
final fullName = displayName.value.getOrElse(() => ""); | |
final photo = photoUrl; | |
final UserUpdateInfo updateInfo = UserUpdateInfo() | |
..displayName = fullName | |
..photoUrl = photo; | |
try { | |
final currentUser = await _firebaseAuth.currentUser(); | |
if (currentUser != null) await currentUser?.updateProfile(updateInfo); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> signInWithPhoneNumber({ | |
Phone phone, | |
Duration timeout = Helpers.autoRetrievalTimeout, | |
int forceResendingToken, | |
void Function(String verificationId, [int resendToken]) codeSent, | |
void Function(AuthCredential credential) verificationCompleted, | |
void Function(AuthException exception) verificationFailed, | |
void Function(String verificationId) codeAutoRetrievalTimeout, | |
}) async { | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
await _firebaseAuth.verifyPhoneNumber( | |
phoneNumber: "${phone.country.dialCode}${phone.getOrCrash}", | |
timeout: timeout, | |
forceResendingToken: forceResendingToken, | |
verificationCompleted: (credential) async { | |
verificationCompleted(credential); | |
// If there's no Authenticated User | |
if ((await _firebaseAuth.currentUser()) == null) firebaseSignInWithCredentials(credential); | |
// User is authenticated. | |
(await _firebaseAuth.currentUser()).updatePhoneNumberCredential(credential); | |
}, | |
codeSent: (String verificationId, [int resendToken]) => codeSent(verificationId, resendToken), | |
codeAutoRetrievalTimeout: (verificationId) => codeAutoRetrievalTimeout(verificationId), | |
verificationFailed: (AuthException ex) => verificationFailed(ex), | |
); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> withPhoneCredential(String verificationId, String code) async { | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
final credential = PhoneAuthProvider.getCredential(verificationId: verificationId, smsCode: code); | |
// If there's no Authenticated User | |
if ((await _firebaseAuth.currentUser()) == null) return firebaseSignInWithCredentials(credential); | |
// User is authenticated. | |
(await _firebaseAuth.currentUser()).updatePhoneNumberCredential(credential); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> confirmPasswordReset({String code, Password newPassword}) async { | |
try { | |
await _checkForStableInternet(); | |
await _firebaseAuth.confirmPasswordReset(code, newPassword.getOrCrash); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> sendPasswordResetEmail(EmailAddress email) async { | |
try { | |
await _checkForStableInternet(); | |
await _firebaseAuth.sendPasswordResetEmail(email: email.getOrCrash); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
if (e.code == "ERROR_INVALID_EMAIL") | |
return left(AuthFailure.invalidCredentials( | |
message: "Invalid email-address.", | |
)); | |
return left(AuthFailure.userAccountNotFound( | |
message: "Email address does not exist.", | |
)); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> googleAuthentication() async { | |
GoogleSignInAccount account; // Init Google Account | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
// Attempt authenticating user with google credentials | |
account = await _googleSignIn.signIn(); | |
// If null, => user cancelled authentication | |
if (account == null) throw AuthFailure.cancelledAction(); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} catch (e) { | |
return left(AuthFailure.unknownFailure(message: e?.message)); | |
} | |
// get authentication details [idToken], [accessToken] | |
final googleAccountAuth = await account.authentication; | |
// Get instance of AuthCredentials | |
AuthCredential authCredential = GoogleAuthProvider.getCredential( | |
idToken: googleAccountAuth.idToken, | |
accessToken: googleAccountAuth.accessToken, | |
); | |
return firebaseSignInWithCredentials(authCredential); | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> facebookAuthentication() async { | |
// TEST USER // | |
// [email protected] | |
// septembeR123 | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
final result = await _facebookLogin.logIn([USER_EMAIL_PERMISSION, USER_PROFILE_PERMISSION]); | |
switch (result.status) { | |
case FacebookLoginStatus.loggedIn: | |
AuthCredential credential = FacebookAuthProvider.getCredential(accessToken: result.accessToken.token); | |
return firebaseSignInWithCredentials(credential); | |
case FacebookLoginStatus.cancelledByUser: | |
throw AuthFailure.cancelledAction(); | |
break; | |
case FacebookLoginStatus.error: | |
throw AuthFailure.unknownFailure(code: "${result.status.index}", message: "${result.errorMessage}"); | |
break; | |
default: | |
throw AuthFailure.unknownFailure(code: "unknown_error", message: "Provider error. Please contact support."); | |
} | |
} on AuthFailure catch (e) { | |
return left(e); | |
} | |
} | |
@override | |
Future<Either<AuthFailure, Unit>> twitterAuthentication() => throw UnimplementedError(); | |
Future<Either<AuthFailure, Unit>> firebaseSignInWithCredentials(AuthCredential credential) async { | |
try { | |
// First we'll check for stable Internet connection | |
await _checkForStableInternet(); | |
// SignIn to firebase using user's google account credentials | |
await _firebaseAuth.signInWithCredential(credential); | |
return right(unit); | |
} on AuthFailure catch (e) { | |
return left(e); | |
} on PlatformException catch (e) { | |
print("Exception here ====> $e"); | |
return _handlePlatformException(e); | |
} | |
} | |
Future<Either<AuthFailure, Unit>> _handlePlatformException(PlatformException e, {EmailAddress email}) async { | |
switch (e.code) { | |
case "ERROR_INVALID_EMAIL": | |
case "ERROR_WRONG_PASSWORD": | |
return left(AuthFailure.invalidCredentials()); | |
case "ERROR_INVALID_CREDENTIAL": | |
return left(AuthFailure.invalidCredentials()); | |
case "ERROR_USER_NOT_FOUND": | |
return left(AuthFailure.userAccountNotFound()); | |
case "ERROR_USER_DISABLED": | |
return left(AuthFailure.userAccountDisabled()); | |
case "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL": | |
return _handleAccountAlreadyExists(email); | |
case "ERROR_INVALID_ACTION_CODE": | |
return left(AuthFailure.expiredOrInvalidToken()); | |
case "ERROR_EMAIL_ALREADY_IN_USE": | |
return left(AuthFailure.emailAlreadyInUse()); | |
case "ERROR_TOO_MANY_REQUESTS": | |
return left(AuthFailure.tooManyRequests()); | |
case "ERROR_OPERATION_NOT_ALLOWED": | |
return left(AuthFailure.unExpectedFailure(message: "Operation not allowed! Please contact support.")); | |
case "WEAK_PASSWORD": | |
return left(AuthFailure.weakPassword()); | |
case "EXPIRED_ACTION_CODE": | |
return left(AuthFailure.expiredOrInvalidToken(message: "Expired verification code.")); | |
case "INVALID_ACTION_CODE": | |
return left(AuthFailure.expiredOrInvalidToken(message: "Invalid verification code, please try again.")); | |
case "ERROR_INVALID_VERIFICATION_CODE": | |
return left(AuthFailure.invalidCredentials( | |
message: "Invalid verification code." | |
"\nDidn't get the code? Tap Resend.")); | |
default: | |
return left(AuthFailure.unknownFailure(code: "ERROR_UNKNOWN", message: e?.message)); | |
} | |
} | |
Future<Either<AuthFailure, Unit>> _handleAccountAlreadyExists(EmailAddress emailAddress) async { | |
// Retrieve the signin methods | |
final String email = emailAddress.getOrCrash; | |
try { | |
final methods = await _firebaseAuth.fetchSignInMethodsForEmail(email: email); | |
print("Methods ==> ${methods.toString()}"); | |
return left(AuthFailure.accountAlreadyExists(email: emailAddress, provider: AuthProvider.Google)); | |
} on PlatformException catch (e) { | |
return _handlePlatformException(e); | |
} | |
} | |
@override | |
Future<void> signOut() => Future.wait([ | |
_googleSignIn.signOut(), | |
_facebookLogin.logOut(), | |
_firebaseAuth.signOut(), | |
]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you need additional info (for imports) create an issue. Thanks