Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nmfisher/da84802fbb28a99b9394515e0a7f24cf to your computer and use it in GitHub Desktop.
Save nmfisher/da84802fbb28a99b9394515e0a7f24cf to your computer and use it in GitHub Desktop.
// 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