Adding authentication to your apps using Apple OAuth 2.0

Demola Malomo - Aug 1 '23 - - Dev Community

Apple’s iOS is one of the leading mobile operating systems, with an estimated market share of about 20.5% of the global smartphone market. Beyond its usability, timely updates, security, and privacy features, an integral part of the system is the Apple ID; an identifier for accessing all Apple products and services.

In 2019, Apple announced the Sign in with Apple functionality. A fast, secure, and easy way for users to sign in to applications and websites. The functionality has helped developers seamlessly build software without the associated authentication and authorization overheads.

In this post, we will learn how to set up OAuth 2.0 social login in a Flutter application using Apple as the provider. The project repository can be found here.

Prerequisites

To fully grasp the concepts presented in this tutorial, the following are required:

  • Basic understanding of Dart and Flutter
  • Flutter SDK installed
  • Xcode with a developer account
  • Appwrite account; sign-up is completely free
  • Apple developer account

Getting started

To get started, we need to clone the project by navigating to the desired directory and running the command below:



git clone https://github.com/Mr-Malomz/flutter_auth_apple


Enter fullscreen mode Exit fullscreen mode

Running the project

First, we need to install the project dependencies by running the command below:



flutter pub get


Enter fullscreen mode Exit fullscreen mode

Then, run the project using the following command:



flutter run


Enter fullscreen mode Exit fullscreen mode

The command above will run the application on the selected device.

Login screen

Set up a project on Appwrite

To get started, we need to log into our Appwrite console, click the Create project button, input flutter_auth_apple as the name, and then Create.

Create project

Add platform support

To add support for our Flutter app, navigate to the Home menu and click the Flutter App button.

Add platform

Next, we must modify the Flutter application as detailed below:

To obtain our Bundle ID, navigate to the path below:
ios > Runner.xcodeproj > project.pbxproj

Open the project.pbxproj file and search for PRODUCT_BUNDLE_IDENTIFIER.

Select new Flutter app

Finally, open the project directory on Xcode, open the Runner.xcworkspace folder in the app's iOS folder, select the Runner project in the Xcode project navigator, select the Runner target in the primary menu sidebar, and then select iOS 11 in the deployment info’s target.

Change deployment target

Enable Apple as the preferred OAuth 2.0 provider

To do this, we must navigate to the Auth menu, click the Settings tab, and enable Apple as a provider.

Navigate to the provider
Apple OAuth

We will fill in the required details later. However, we must copy and keep the redirect URI, it will come in handy when setting up our application on the Apple developer portal.

Copy URI

Set up Apple OAuth 2.0

With our project partly configured on Appwrite, we must also set up our project on the Apple developer portal.

Create App ID using Xcode

An App ID is used to identify one or more applications in the development team. We must tie our application with an App ID on the platform. To get started, we need to first open the ios folder in our source code with Xcode.

ios folder open on Xcode

Second, we must add Sign in with Apple capability to our App ID. To do this, navigate to the Signing & Capabilities tab, click the “+ Capability” button, search for Sign in with Apple, and double-click the result to add it.

Add capability
Added sign in

Lastly, we must modify our Bundle Identifier to a unique string by prefixing it with any letters of our choice and then change the Team to our Apple developer team.

On doing the step above, the system should automatically create an App ID for us. See details below.

PS: We might get an error about registering the bundle identifier; we can resolve this by prefixing the Bundle identifier to another set of unique strings. Beyond the use of Xcode, we can also create an App ID using our Apple developer portal.

Create a Service ID

A Service ID is used to identify the applications or websites interacting with Apple web services. To do this, we need to log into the portal, navigate to the Identifiers screen, change to Services IDs, and Register a Services ID.

https://developer.apple.com/account/resources/identifiers/list/serviceId

Account
Create Service ID

Select Service IDs and Continue.

Continue

Input flutter auth apple as the Description, input another unique ID as the Identifier, and Register. We also need to keep the inputted Identifier as it will come in handy when we finish setting up our application on Appwrite.

Register

View the details of the newly created Service ID, enable the Sign in with Apple and Configure.

Click details
Enable and configure

Select the App ID we configured using Xcode earlier, input appwrite.io as the Domain, input the Redirect URI we copied from Appwrite earlier as the Redirect URLs, click Next, and Save.

Input

Create a Key ID

We need to create a key ID that our application will use to securely communicate with Apple services. To do this, navigate to the Keys tab and Create.

Input flutterAuthApple as the Key Name, enable the Sign in with Apple option, and Configure.

configure

Select the App ID we created earlier and Save.

Select and Save

Register the key, copy the Key ID, and Download the Key File.

Register the app
Copy and download

Putting it together on Appwrite

With our application configured on Apple, we need to create a link to it on Appwrite. To do this, we must update the Apple provider details on Appwrite with the copied Service ID Identifier, Key ID, Team ID, P8 File, and Update.

Add credentials

We can get our Team ID by navigating to the Membership details of the Apple Developer account section and the P8 File by opening the downloaded Key File in an editor.

Integrating Apple OAuth 2.0 with Flutter using Appwrite

With all that done, let’s build the social login into our application. First, we need to create a class for storing our Appwrite credentials. To do this, we need to create a utils.dart file in the lib folder and add the snippet below:



class AppConstant {
  final String projectId = "REPLACE WITH PROJECT ID";
  final String endpoint = "https://cloud.appwrite.io/v1";
}


Enter fullscreen mode Exit fullscreen mode

PS: We can get our Project ID from the Appwrite console.

Secondly, we need to create a service file to separate the application core logic from the UI. To do this, we need to create an auth_service.dart file in the same lib folder and add the snippet below:



import 'package:appwrite/appwrite.dart';
import 'package:flutter_auth_apple/utils.dart';

class AuthService {
  Client _client = Client();
  late Account _account;

  AuthService() {
    _init();
  }

  //initialize the application
  _init() async {
    _client
        .setEndpoint(AppConstant().endpoint)
        .setProject(AppConstant().projectId);

    _account = Account(_client);
  }

  Future loginWithApple() async {
    try {
      return await _account.createOAuth2Session(provider: 'apple');
    } catch (e) {
      throw Exception('Error login into Apple!');
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

The snippet above does the following:

  • Imports the required dependencies
  • Creates an AuthService class with _client and _account properties to connect to the Appwrite instance
  • Creates an _init method that configures Appwrite using the properties
  • Creates a loginWithApple method that uses the _account property to log in with OAuth 2.0 using apple as the provider

Lastly, we need to modify the login.dart file inside the screens folder to use the service.



import 'package:flutter/material.dart';
import 'package:flutter_auth_apple/auth_service.dart';

class Login extends StatefulWidget {
  const Login({Key? key}) : super(key: key);

  @override
  State<Login> createState() => _LoginState();
}

class _LoginState extends State<Login> {
  bool _isLoading = false;

  _loginWithApple() {
    setState(() {
      _isLoading = true;
    });
    AuthService()
        .loginWithApple()
        .then((value) => {
              setState(() {
                _isLoading = false;
              })
            })
        .catchError((e) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error login into Apple!')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Text(
              "Appwrite + Apple Auth",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 18.0,
              ),
            ),
            const SizedBox(height: 30),
            SizedBox(
              width: double.infinity,
              height: 45.0,
              child: TextButton(
                onPressed: _isLoading
                    ? null
                    : () {
                        _loginWithApple();
                      },
                style: ButtonStyle(
                  backgroundColor:
                      MaterialStateProperty.all<Color>(Colors.black),
                ),
                child: const Text(
                  'Sign in with Apple',
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                    fontSize: 14.0,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

The snippet above does the following:

  • Lines 1-2: Import the required dependencies
  • Line 12: Creates an _isLoading property to manage the application state
  • Lines 14-33: Create a _loginWithApple method to log in users using the AuthService().loginWithApple service and set state accordingly
  • Lines 57-61: Update the button to use the method created

With that done, we restart the application using the code editor or run the command below:

flutter run
Enter fullscreen mode Exit fullscreen mode

Conclusion

This post detailed a step-by-step guide on how to add Apple OAuth 2.0 social login in a Flutter application using Appwrite. Beyond what was discussed above, the SDK also supports the success, failure, and scope mechanism for managing callbacks and the amount of user information requested from Apple.

These resources may also be helpful:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .