Last active
April 14, 2021 06:37
-
-
Save nmfisher/da84802fbb28a99b9394515e0a7f24cf to your computer and use it in GitHub Desktop.
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
// MIT licence | |
import 'dart:async'; | |
import 'dart:io'; | |
import 'package:firebase_auth/firebase_auth.dart'; | |
import 'package:firebase_core/firebase_core.dart'; | |
import 'package:fluent_academy_app/blocs/initializable_cubit.dart'; | |
import 'package:fluent_academy_app/widgets/user/auth/signin/sign_in_providers.dart'; | |
import 'package:flutter/widgets.dart'; | |
import 'package:firebase_auth/firebase_auth.dart' as auth; | |
import 'package:google_sign_in/google_sign_in.dart'; | |
import 'package:sign_in_with_apple/sign_in_with_apple.dart'; | |
import 'package:firebase_auth/firebase_auth.dart'; | |
import 'dart:math'; | |
import 'dart:convert'; | |
import 'dart:io'; | |
import 'package:crypto/crypto.dart'; | |
import 'package:sign_in_with_apple/sign_in_with_apple.dart'; | |
enum SignInProviders { Google, Email, Twitter, Apple } | |
Map<SignInProviders, String> signInLabels = { | |
SignInProviders.Apple: "Apple", | |
SignInProviders.Google: "Google", | |
SignInProviders.Twitter: "Twitter", | |
SignInProviders.Email: "Email", | |
}; | |
Map<String, SignInProviders> signInProviderIds = { | |
"password": SignInProviders.Email, | |
"twitter.com": SignInProviders.Twitter, | |
"google.com": SignInProviders.Google, | |
"apple": SignInProviders.Apple | |
}; | |
class AuthenticationBlocState with InitializableState { | |
bool isAuthenticated = false; | |
bool authenticating = false; | |
bool hasConnection = false; | |
bool checkingConnection = false; | |
String? uid; | |
@override | |
AuthenticationBlocState copy() { | |
return AuthenticationBlocState() | |
..authenticating = authenticating | |
..hasConnection = hasConnection | |
..isAuthenticated = isAuthenticated | |
..initializer = initializer; | |
} | |
} | |
class AuthenticationBloc extends InitializableCubit<AuthenticationBlocState> { | |
auth.User? get user => auth.FirebaseAuth.instance.currentUser; | |
AuthenticationBloc() | |
: super(AuthenticationBlocState()..isAuthenticated = false); | |
Future<bool> initialize(BuildContext context) async { | |
await Firebase.initializeApp(); | |
auth.FirebaseAuth.instance.authStateChanges().listen((user) { | |
emit(state.copy() | |
..uid = user?.uid | |
..isAuthenticated = user != null); | |
}); | |
if (user != null) { | |
emit(state.copy() | |
..isAuthenticated = true | |
..uid = user!.uid); | |
} else { | |
try { | |
emit(state.copy()..checkingConnection = true); | |
final result = await InternetAddress.lookup('google.com'); | |
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { | |
emit(state.copy() | |
..checkingConnection = false | |
..hasConnection = true); | |
} | |
} on SocketException catch (_) { | |
emit(state.copy() | |
..checkingConnection = false | |
..hasConnection = false); | |
} | |
} | |
return super.initialize(context); | |
} | |
String _createNonce(int length) { | |
final random = Random(); | |
final charCodes = List<int>.generate(length, (_) { | |
late int codeUnit; | |
switch (random.nextInt(3)) { | |
case 0: | |
codeUnit = random.nextInt(10) + 48; | |
break; | |
case 1: | |
codeUnit = random.nextInt(26) + 65; | |
break; | |
case 2: | |
codeUnit = random.nextInt(26) + 97; | |
break; | |
} | |
return codeUnit; | |
}); | |
return String.fromCharCodes(charCodes); | |
} | |
Future<OAuthCredential> _createAppleOAuthCred() async { | |
final nonce = _createNonce(32); | |
final nativeAppleCred = Platform.isIOS | |
? await SignInWithApple.getAppleIDCredential( | |
scopes: [ | |
AppleIDAuthorizationScopes.email, | |
AppleIDAuthorizationScopes.fullName, | |
], | |
nonce: sha256.convert(utf8.encode(nonce)).toString(), | |
) | |
: await SignInWithApple.getAppleIDCredential( | |
scopes: [ | |
AppleIDAuthorizationScopes.email, | |
AppleIDAuthorizationScopes.fullName, | |
], | |
webAuthenticationOptions: WebAuthenticationOptions( | |
redirectUri: Uri.parse( | |
WEB_HANDLER), | |
clientId: CLIENT_ID, | |
), | |
nonce: sha256.convert(utf8.encode(nonce)).toString(), | |
); | |
return new OAuthCredential( | |
providerId: "apple.com", // MUST be "apple.com" | |
signInMethod: "oauth", // MUST be "oauth" | |
accessToken: nativeAppleCred | |
.identityToken, // propagate Apple ID token to BOTH accessToken and idToken parameters | |
idToken: nativeAppleCred.identityToken, | |
rawNonce: nonce, | |
); | |
} | |
Future<bool> signInWithApple() async { | |
final oauthCred = await _createAppleOAuthCred(); | |
await FirebaseAuth.instance.signInWithCredential(oauthCred); | |
return true; | |
} | |
Future<bool> signInWithGoogle() async { | |
final GoogleSignIn _googleSignIn = GoogleSignIn(); | |
GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); | |
if(googleUser == null) | |
return false; | |
GoogleSignInAuthentication googleAuth = await googleUser!.authentication; | |
AuthCredential credential = GoogleAuthProvider.credential( | |
idToken: googleAuth.idToken, accessToken: googleAuth.accessToken); | |
await FirebaseAuth.instance.signInWithCredential(credential); | |
return true; | |
} | |
Future<bool> signInWithProvider(SignInProviders provider) async { | |
switch (provider) { | |
case SignInProviders.Apple: | |
return signInWithApple(); | |
case SignInProviders.Google: | |
return signInWithGoogle(); | |
default: | |
throw UnsupportedError("Provider $provider not yet supported"); | |
} | |
} | |
Future logout() async { | |
await auth.FirebaseAuth.instance.signOut(); | |
emit(state.copy()..isAuthenticated = false); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment