Flutter Firebase Authentication: Apple Sign In
Tags: flutter, ios, tutorial, firebase
Published: 2021-02-17 06:53:29


As per Apple guidelines, apps that are offering sign in with other social services also need to provide the option of apple sign-in. I think it is very convenient to offer (Apple Sign In) on iOS as you would offer Google Sign In on Android, as your iOS users already have an Apple ID and can use it to sign in with your App.

In this post, we are going to add Apple Sign In to our Flutter & Firebase app. We will use the [sign_in_with_apple](https://pub.dev/packages/sign_in_with_apple) Flutter package available on pub.dev.

##Prerequisites

Xcode installed
An Apple Developer Account
An <b>iOS 13.x</b> device or simulator, signed in with an Apple ID
Firebase Project for the App

##Firebase Setup

We need to add iOS App to the Firebase project. Note the bundle id; we will use it later.

![Screen Shot 2021-02-16 at 4.19.52 PM](https://i.imgur.com/D4pY35L.png)

Download the GoogleService-Info.plist file 

![Screen Shot 2021-02-16 at 4.30.08 PM](https://i.imgur.com/4zJxLYh.png)

Enable Apple authentication 

![Screen Shot 2021-02-16 at 9.09.05 PM](https://i.imgur.com/q4Eksrw.png)

##Apple Setup

Open the Identifiers page on the Apple developer website [here](https://developer.apple.com/account/resources/identifiers/list/bundleId) and click the (+) icon.

![Screen Shot 2021-02-16 at 8.40.34 PM](https://i.imgur.com/0jD8k7W.png)

Select App IDs and click Continue.

![Screen Shot 2021-02-16 at 8.43.02 PM](https://i.imgur.com/4aZHK53.png)

The type is App. click Continue.

![Screen Shot 2021-02-16 at 8.46.05 PM](https://i.imgur.com/ZRilshx.png)


Set the bundle Id similar to what we used in the firebase project. Check on the (Sign In with Apple) capability, then select Continue.

![Screen Shot 2021-02-16 at 8.52.22 PM](https://i.imgur.com/kE5u4Nl.png)

Click Register to complete Apple configuration.

![Screen Shot 2021-02-16 at 8.56.20 PM](https://i.imgur.com/aqlOlYj.png)

##Project Setup

Let's start by creating an App using the command below using Terminal.

```shell

flutter create flutter_apple_signin

```

Update pubspec.yaml as below to add the required packages

```yaml

dependencies:
  flutter:
    sdk: flutter
  firebase_core: "^0.7.0"
  firebase_auth: "^0.20.0+1"
  sign_in_with_apple: ^2.5.2
  crypto: ^2.1.5
  provider: ^4.3.3

```

Right-click on the ios folder and select Open in Xcode.

![Screen Shot 2021-02-16 at 3.38.44 PM](https://i.imgur.com/y72fFP2.png)


On the Signing & Capabilities tab, configure the signing by setting the Team as per your Apple developer account. Make sure to use the same bundle Id we used in the firebase project.

![Screen Shot 2021-02-16 at 3.47.46 PM](https://i.imgur.com/pDemj7m.png)


 Add "Sign In With Apple" as a new Capability:

![Screen Shot 2021-02-16 at 3.46.05 PM](https://i.imgur.com/N78dEp2.png)


Set the Deployment target to iOS 13

![Screen Shot 2021-02-16 at 8.07.57 PM](https://i.imgur.com/lbT5EA6.png)

Right-click on the Runner Project and select Add Files to "Runner"

![Screen Shot 2021-02-16 at 4.34.22 PM](https://i.imgur.com/XYArIk3.png)

Select the GoogleService-Info.plist filer we downloaded from Firebase and select Add.

![Screen Shot 2021-02-16 at 4.36.56 PM](https://i.imgur.com/eokruyd.png)


##App Implementation

Create the authentication_provider.dart file below in the (lib\providers) folder. We will use it to sign in with Apple.

```dart

import 'dart:convert';
import 'dart:math';

import 'package:crypto/crypto.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';

class AuthenticationProvider with ChangeNotifier {
  final FirebaseAuth _firebaseAuth;

  AuthenticationProvider(this._firebaseAuth);

  Stream<User> get authStateChanges => _firebaseAuth.authStateChanges();

  Future<void> signOut() async {
    await _firebaseAuth.signOut();
  }

  /// 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();
  }

  Future<User> signInWithApple() async {
    // To prevent replay attacks with the credential returned from Apple, we
    // include a nonce in the credential request. When signing in 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 {
      // Request credential for the currently signed in Apple account.
      final appleCredential = await SignInWithApple.getAppleIDCredential(
        scopes: [
          AppleIDAuthorizationScopes.email,
          AppleIDAuthorizationScopes.fullName,
        ],
        nonce: nonce,
      );

      print(appleCredential.authorizationCode);

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

      // Sign in the user with Firebase. If the nonce we generated earlier does
      // not match the nonce in `appleCredential.identityToken`, sign in will fail.
      final authResult =
          await _firebaseAuth.signInWithCredential(oauthCredential);

      final displayName =
          '${appleCredential.givenName} ${appleCredential.familyName}';
      final userEmail = '${appleCredential.email}';

      final firebaseUser = authResult.user;
      print(displayName);
      await firebaseUser.updateProfile(displayName: displayName);
      await firebaseUser.updateEmail(userEmail);

      return firebaseUser;
    } catch (exception) {
      print(exception);
    }
  }
}



```

Create the login_screen.dart file below in the (lib\screens) folder. This will display the Apple SignIn button, which will use the method (signInWithApple) from the AuthenticationProvider.


```dart

import 'package:flutter/material.dart';
import 'package:flutter_apple_signin/providers/authentication_provider.dart';
import 'package:provider/provider.dart';

import 'package:sign_in_with_apple/sign_in_with_apple.dart';

class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SignInWithAppleButton(
              style: SignInWithAppleButtonStyle.black,
              iconAlignment: IconAlignment.center,
              onPressed: () {
                context.read<AuthenticationProvider>().signInWithApple();
              },
            )
          ],
        ),
      ),
    );
  }
}

```

On main.dart, we will watch the Firebase User object to determine the login or logout page.

```dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_apple_signin/providers/authentication_provider.dart';
import 'package:flutter_apple_signin/screens/login_screen.dart';
import 'package:flutter_apple_signin/screens/logout_screen.dart';
import 'package:provider/provider.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (ctx) => AuthenticationProvider(FirebaseAuth.instance),
        ),
        StreamProvider(
          create: (BuildContext context) {
            return context.read<AuthenticationProvider>().authStateChanges;
          },
        )
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.purple,
          accentColor: Colors.deepOrange,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(),
        routes: {},
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final firebaseUser = context.watch<User>();
    return Scaffold(
        appBar: AppBar(
          title: Text('Apple Sign In'),
        ),
        body: firebaseUser != null ? LogoutPage() : LoginPage());
  }
}



```

The implementation is complete. Let's run the App.

{% youtube pW4jP5PPZuU %}


Check the code <a href="https://github.com/offlineprogrammer/flutter_apple_signin">here</a>

> Follow me on <a href="https://twitter.com/_Mo_Malaka_">Twitter</a> for more tips about #coding, #learning, #technology...etc.

> Check my channel on <a href="https://bit.ly/39yUtOz">YouTube</a>

> Check my Apps on <a href="https://bit.ly/3h05gQ7">Google Play</a>

> <span>Cover image <a href="https://unsplash.com/@adijoshi11?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Aditya Joshi</a> on <a href="https://unsplash.com/s/photos/apple-logo?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>