Front-End Web & Mobile

Federating Users using Sign in with Apple and AWS Amplify for Swift

In many mobile apps, users are offered different ways to authenticate with the app like providing a username and password as well as offering options to sign in with a social provider like Amazon, Facebook, Google, and Apple. When authenticating with a social provider, the user goes through the provider’s auth flow and, in return, the provider gives the client app an identity token as proof that the user has successfully authenticated.

This article will show you how to use Sign in with Apple (SIWA) to retrieve an identity token and federate the user in an Amazon Cognito identity pool using the AWS Amplify Libraries for Swift. Federating a user in an identity pool provides them with credentials that allows them to access other services like Amazon S3 and Amazon DynamoDB.

Setup Sign in with Apple

In Xcode, navigate to the Signing & Capabilities tab and add Sign in with Apple:

Sign in with Apple capability in Xcode

In ContentView.swift add the following import statement to the top:

import AuthenticationServices

The Sign in with Apple button requires two callbacks: one that configures the request and another that handles the response. Add the following functions to the ContentView:

func configureRequest(_ request: ASAuthorizationAppleIDRequest) {
    request.requestedScopes = [.email]
}

func handleResult(_ result: Result<ASAuthorization, Error>) {
    switch result {
    case .success(let authorization):
        guard
            let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
            let identityToken = credential.identityToken
        else { return }
    case .failure(let error):
        print(error)
    }
}

Next, in the body of the ContentView, add the Sign in with Apple button:

SignInWithAppleButton(
    onRequest: configureRequest,
    onCompletion: handleResult
)
.frame(maxWidth: 300, maxHeight: 45)

If you were to build and run the app on a device now, you would be able to go through the Sign in with Apple flow. However, Amplify needs to be setup before being able to take the identity token from the SIWA process to federate the user.

Setup Amplify Project

I will be using the Amplify CLI to create the Amplify project in the root directory of my iOS project, but you can use Amplify Studio if you’re more comfortable with that.

In the root directory of your Xcode project, run the following command in the terminal:

amplify init

I will use the default configuration for an iOS project. You can see the values I selected in the following snippet:

? Enter a name for the project FederateUserSIWA
The following configuration will be applied:

Project information
| Name: FederateUserSIWA
| Environment: dev
| Default editor: Visual Studio Code
| App type: ios

? Initialize the project with the above configuration? Yes
Using default provider  awscloudformation
? Select the authentication method you want to use: AWS profile
? Please choose the profile you want to use default
✔ Help improve Amplify CLI by sharing non sensitive configurations on failures (y/N) · no

Once your Amplify project in successfully created, you must use the Amplify CLI to configure the Auth category since federating users with a social provider requires a manual configuration.

Enter the following command in the terminal:

amplify add auth

Answer the prompts with the following values:

? Do you want to use the default authentication and security configuration? 
  Manual configuration
? Select the authentication/authorization services that you want to use: 
  User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)
? Provide a friendly name for your resource that will be used to label this category in the project: 
  federateusersiwa6c7eb14d6c7eb14d
? Enter a name for your identity pool. 
  federateusersiwa6c7eb14d_identitypool_6c7eb14d
? Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) 
  No
? Do you want to enable 3rd party authentication providers in your identity pool? 
  Yes
? Select the third party identity providers you want to configure for your identity pool: 
  Apple
? Enter your Bundle Identifier for your identity pool:  
  com.kiloloco.Federate-User-SIWA
? Provide a name for your user pool: 
  federateusersiwa6c7eb14d_userpool_6c7eb14d
? How do you want users to be able to sign in? 
  Username
? Do you want to add User Pool Groups? 
  No
? Do you want to add an admin queries API? 
  No
? Multifactor authentication (MFA) user login options: 
  OFF
? Email based user registration/forgot password: 
  Enabled (Requires per-user email entry at registration)
? Specify an email verification subject: 
  Your verification code
? Specify an email verification message: 
  Your verification code is {####}
? Do you want to override the default password policy for this User Pool? 
  No
? What attributes are required for signing up? 
  Email
? Specify the app's refresh token expiration period (in days): 
  30
? Do you want to specify the user attributes this app can read and write? 
  No
? Do you want to enable any of the following capabilities? 
  No
? Do you want to use an OAuth flow? 
  No
? Do you want to configure Lambda Triggers for Cognito? 
  No

With the Auth category configured, it’s time to push your configuration to the backend. Run the following command:

amplify push

The Amplify CLI will show which resources have been created. It should look similar to the snippet below:

┌──────────┬──────────────────────────────────┬───────────┬───────────────────┐
│ Category │ Resource name                    │ Operation │ Provider plugin   │
├──────────┼──────────────────────────────────┼───────────┼───────────────────┤
│ Auth     │ federateusersiwa6c7eb14d6c7eb14d │ Create    │ awscloudformation │
└──────────┴──────────────────────────────────┴───────────┴───────────────────┘

If everything looks correct, hit Enter to continue. Once the configuration has been successfully deployed, you will see the following output in the terminal:

Deployment completed.

Add Amplify as a Dependency

Now it’s time to open Xcode and add Amplify as a dependency for the iOS project. Open the Search Packages screen (File > Add Packages…):

Add packages to project in Xcode

Enter the Amplify Libraries for Swift repo URL (https://github.com/aws-amplify/amplify-swift/) in the search bar at the top right. Select Up to Next Major Version for Dependency Rule and set the value to 2.0.0, then click Add Package.

Add Amplify as a dependency in Xcode

Select Amplify and AWSCognitoAuthPlugin as the package products and click Add Package.

You will now see Amplify and its dependencies added to the navigation bar in Xcode.

Configure Amplify

If the two configuration files have not been added to your Xcode project, open Finder, navigate to the root directory of your project, then drag and drop both amplifyconfiguration.json and awsconfiguration.json into the Xcode Navigation pane.

Open the <YOUR_PROJECT>App.swift file and add the following import statements to the top:

import Amplify
import AWSCognitoAuthPlugin

Inside the <YOUR_PROJECT>App object, add the following function to configure Amplify:

func configureAmplify() {
    do {
        try Amplify.add(plugin: AWSCognitoAuthPlugin())
        try Amplify.configure()
        print("Successfully configured Amplify")
    } catch {
        print("Failed to initialize Amplify:", error)
    }
}

Add the following init method to ensure that Amplify is configured as soon as the app is initialized:

init() {
    configureAmplify()
}

Build and run. You will see the following output in the Xcode logs:

Successfully configured Amplify

Federating a User

Federating a user with Amplify requires using an escape hatch that gives access to the underlying AWS Swift SDK.

Navigate to the ContentView.swift and add the following import statements at the top:

import Amplify
import AWSCognitoAuthPlugin

Next, add the function that will handle federating the user once an identity token is provided:

func federateToIdentityPools(with token: Data) {
    guard
        let tokenString = String(data: token, encoding: .utf8),
        let plugin = try? Amplify.Auth.getPlugin(for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin
    else { return }
    
    Task {
        do {
            let result = try await plugin.federateToIdentityPool(
                withProviderToken: tokenString,
                for: .apple
            )
            print("Successfully federated user to identity pool with result:", result)
        } catch {
            print("Failed to federate to identity pool with error:", error)
        }
    }
}

Once the identity token has been converted to a String, it is passed to the plugin to federate the user with the specified auth provider.

Lastly, add the following line of code to the success case of handleResult:

self.federateToIdentityPools(with: identityToken)

Build and run the app on a device. You will now be able to authenticate the user using Sign in with Apple and federate into the identity pool.

Once you’ve completed the Sign in with Apple process, you will see a similar output in the Xcode console:

Successfully federated user to identity pool with result: FederateToIdentityPoolResult(credentials: ["secretAccessKey": "NY*****5x", "sessionToken": "IQ*****==", "expiration": 2023-02-15 18:35:01 +0000, "accessKey": "AS*****FZ"], identityId: "us-east-1:0***9")

To verify that the user has been federated, you can run the following command in the terminal and select Identity Pool:

amplify console auth

Your browser will open to the Amazon Cognito Federated Identities screen and you will be able to see one user that has authenticated using Sign in with Apple.

Amazon Cognito Federated Identites screen showing one user authenticated using Sign in with Apple

Conclusion

You have successfully setup an authentication flow for federating users via Sign in with Apple. If you don’t plan on maintaining this code, it is recommended that you run amplify delete to remove all the resources that were generated for you during this tutorial.

As you use Amplify to build your next project, be sure to reach out on the GitHub repository, or through the Amplify Discord server under the #swift-help channel to help us prioritize features and enhancements.