Skip to content

Instantly share code, notes, and snippets.

@definitelyme
Created March 7, 2021 08:50
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save definitelyme/008945386999d691f725959b64b280e1 to your computer and use it in GitHub Desktop.
Firebase Authentication codes for Flutter & Dart
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 //
// open_meiqiqy_user@tfbnw.net
// 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(),
]);
}
@definitelyme
Copy link
Author

If you need additional info (for imports) create an issue. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment