Skip to content

Commit

Permalink
feat(apple_login): Implement firebase Apple Auth
Browse files Browse the repository at this point in the history
  • Loading branch information
juzerali committed May 30, 2021
1 parent 1a47578 commit 5ce4226
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 1 deletion.
1 change: 1 addition & 0 deletions examples/flutter_firebase_login/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Example Flutter app built with `flutter_bloc` to implement login using Firebase.
## Features

- Sign in with Google
- Sign in with Apple
- Sign up with email and password
- Sign in with email and password

Expand Down
12 changes: 12 additions & 0 deletions examples/flutter_firebase_login/lib/login/cubit/login_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,16 @@ class LoginCubit extends Cubit<LoginState> {
emit(state.copyWith(status: FormzStatus.pure));
}
}

Future<void> logInWithApple() async {
emit(state.copyWith(status: FormzStatus.submissionInProgress));
try {
await _authenticationRepository.logInWithApple();
emit(state.copyWith(status: FormzStatus.submissionSuccess));
} on Exception {
emit(state.copyWith(status: FormzStatus.submissionFailure));
} on NoSuchMethodError {
emit(state.copyWith(status: FormzStatus.pure));
}
}
}
24 changes: 24 additions & 0 deletions examples/flutter_firebase_login/lib/login/view/login_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class LoginForm extends StatelessWidget {
_LoginButton(),
const SizedBox(height: 8.0),
_GoogleLoginButton(),
const SizedBox(height: 8.0),
_AppleLoginButton(),
const SizedBox(height: 4.0),
_SignUpButton(),
],
Expand Down Expand Up @@ -139,6 +141,28 @@ class _GoogleLoginButton extends StatelessWidget {
}
}

class _AppleLoginButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return ElevatedButton.icon(
key: const Key('loginForm_appleLogin_raisedButton'),
label: const Text(
'SIGN IN WITH Apple',
style: TextStyle(color: Colors.white),
),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0.0),
),
primary: Colors.black,
),
icon: const Icon(FontAwesomeIcons.apple, color: Colors.white),
onPressed: () => context.read<LoginCubit>().logInWithApple(),
);
}
}

class _SignUpButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';

import 'package:authentication_repository/authentication_repository.dart';
import 'package:cache/cache.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:google_sign_in/google_sign_in.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:meta/meta.dart';
import 'package:crypto/crypto.dart';

/// Thrown if during the sign up process if a failure occurs.
class SignUpFailure implements Exception {}
Expand All @@ -15,6 +19,9 @@ class LogInWithEmailAndPasswordFailure implements Exception {}
/// Thrown during the sign in with google process if a failure occurs.
class LogInWithGoogleFailure implements Exception {}

/// Thrown during the sign in with apple process if a failure occurs.
class LogInWithAppleFailure implements Exception {}

/// Thrown during the logout process if a failure occurs.
class LogOutFailure implements Exception {}

Expand All @@ -27,13 +34,16 @@ class AuthenticationRepository {
CacheClient? cache,
firebase_auth.FirebaseAuth? firebaseAuth,
GoogleSignIn? googleSignIn,
SignInWithApple? appleSignIn,
}) : _cache = cache ?? CacheClient(),
_firebaseAuth = firebaseAuth ?? firebase_auth.FirebaseAuth.instance,
_googleSignIn = googleSignIn ?? GoogleSignIn.standard();
_googleSignIn = googleSignIn ?? GoogleSignIn.standard(),
_appleSignIn = appleSignIn ?? SignInWithApple();

final CacheClient _cache;
final firebase_auth.FirebaseAuth _firebaseAuth;
final GoogleSignIn _googleSignIn;
final SignInWithApple _appleSignIn;

/// User cache key.
/// Should only be used for testing purposes.
Expand Down Expand Up @@ -89,6 +99,56 @@ class AuthenticationRepository {
}
}

/// Starts the Sign In with Apple Flow.
///
/// Throws a [logInWithApple] if an exception occurs.
Future<void> logInWithApple() async {
// To prevent replay attacks with the credential returned from Apple, we
// include a nonce in the credential request. When signing in with
// Firebase, the nonce in the id token returned by Apple, is expected to
// match the sha256 hash of `rawNonce`.
final rawNonce = generateNonce();
final nonce = sha256ofString(rawNonce);

try {
final appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
nonce: nonce,
);

// Create an `OAuthCredential` from the credential returned by Apple.
final credential = firebase_auth.OAuthProvider('apple.com').credential(
idToken: appleCredential.identityToken,
rawNonce: rawNonce,
);
print(credential);

await _firebaseAuth.signInWithCredential(credential);
} on Exception {
throw LogInWithAppleFailure();
}
}

/// Generates a cryptographically secure random nonce, to be included in a
/// credential request.
String generateNonce([int length = 32]) {
final charset =
'0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
final random = Random.secure();
return List.generate(length, (_) => charset[random.nextInt(charset.length)])
.join();
}

/// Returns the sha256 hash of [input] in hex notation.
String sha256ofString(String input) {
final bytes = utf8.encode(input);
final digest = sha256.convert(bytes);
return digest.toString();
}

/// Signs in with the provided [email] and [password].
///
/// Throws a [LogInWithEmailAndPasswordFailure] if an exception occurs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies:
google_sign_in: ^5.0.0
meta: ^1.3.0
very_good_analysis: ^2.1.0
sign_in_with_apple: ^3.0.0
crypto: ^3.0.0

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit 5ce4226

Please sign in to comment.