Fastlane and App Store Connect API keys

Sponsored

Codemagic logo
Codemagic CI/CD for mobile teams

What do you get when you put love for iOS and DevOps together? Answer: Codemagic CI/CD

I have recently had to set up a Continuous Integration/Continuous Development (CI/CD) pipeline for one of my side projects to upload an app to Testflight using Fastlane. I decided to use GitHub Actions as it is the CI provider I am most familiar with.

For a robust authentication and hassle free interaction with App Store Connect (ASC), Fastlane recommends that you use an App Store API Key. In fact, using an API key solves one of the most common CI issues people tend to have when communicating with App Store Connect: 2 factor authentication (2FA) requiring user input in an automated process.

I have created an App Store Connect API key and used it in Fastlane in the past but I recently found myself having to google a great part of this process again.

For this reason, I decided to put together an all you need to know guide on authentication with App Store Connect API keys in Fastlane. This article will also show you how to safely store the key we’ll create and make it available to GitHub Actions workflows.

Creating an App Store Connect API key

Follow these steps to create an App Store Connect API key:

  1. Go to App Store Connect and sign in.
  2. Go to the ‘Users & Access’ section. The App Store Connect dashboard page with an arrow pointing to the Users and Access section.
  3. Select the Keys (1) menu and click the ’+’ button (2) to create a new key. The Users and Access section in App Store Connect with numbers focusing on the two items to click. Number 1 is the Keys menu and number two is the plus button to create a new key.
  4. Give it a name and a role and click the ‘Generate’ button. Make sure you give the key a role with just enough permissions for the tasks it is going to perform. The popup in App Store Connect where a key with a given name and role can be created.
  5. The new key will now appear in the list. Click the ‘Download API Key’ button and hold on to it until the next section. A list entry showing the new key with an arrow pointing to the 'Download API Key' button which needs to be pressed to download the key file

    It is important to be aware that an API key can only be downloaded once.

  6. Copy the key id and your account’s issuer id. Fastlane needs to know both these values to validate the App Store Connect API key. App Store Connect's keys page with arrows pointing to the two fields that need to be copied: the key's id and the account's issuer id

Storing the ASC API key

I find that the best place to store the new App Store Connect API key is in the CI provider’s secrets section.

While I’ll be using GitHub Actions in this article, the approach to save secrets will be very similar across most CI providers.

Creating an action secret

First, let’s see how we can create a secret and make it available to a GitHub Actions workflow:

  1. Open the ‘Settings’ page for your app’s repository on GitHub (1) and expand the ‘Secrets and Variables’ section from the menu on the left (2). The settings menu of a GitHub repository and where to find the Secrets and Variables section
  2. Select the ‘Actions’ row from the expanded ‘Secrets and Variables’ section. Highlight of the actions section within the Secrets and Variables section in a GitHub repository's settings menu
  3. Click on the ‘New Repository Secret’ button.
  4. You’ll be shown a page where you can create a secret and give it any name you want. The name you give it can later be used to retrieve the secret’s value from within a workflow as we’ll see in the following sections.

Storing the key

Fastlane provides multiple methods to authenticate with the App Store Connect API using an API key.

The way I prefer to use this key and pass it to Fastlane is as a string, as it enables me to store it as a secret on the CI and saves me from having to securely maintain a key file or duplicate across all different runners.

As the code below shows, the app_store_connect_api_key built-in Fastlane action takes in three parameters: a key id, an issuer id and the contents of the key file itself:

Fastfile
app_store_connect_api_key(
  key_id: 🤷‍♂️,
  issuer_id: 🤷‍♂️,
  key_content: 🤷‍♂️
)

As you will have guessed, we will need to create three GitHub Action secrets using the information we obtained when creating the App Store Connect API key:

  • Key’s content:
    • Name: APP_STORE_CONNECT_API_KEY_CONTENT.
    • Value: The content of the key file you downloaded from App Store Connect. To get the value for this secret, open the key file with a text editor and copy all of its contents to the clipboard.
  • Key’s identifier:
    • Name: APP_STORE_CONNECT_API_KEY_ID.
    • Value: The id of the key that you copied earlier from App Store Connect.
  • Issuer identifier:
    • Name: APP_STORE_CONNECT_ISSUER_ID.
    • Value: The issuer id for your App Store Connect account that you copied earlier.

Once you have created these three secrets you can delete the API key file you downloaded from App Store Connect.

Retrieving the App Store Connect API key from Fastlane

Now that the secrets are available to GitHub Actions workflows, we need to make them accessible to the lane that needs to communicate with App Store Connect.

This is a two-step process:

  1. Make the secrets available as environment variables on the step that executes our lane in the GitHub Actions workflow.
  2. Retrieve these environment variables from the lane in the Fastfile.

Environment

Action secrets for a repository can be accessed from workflows with the ${{ Secrets.<SECRET_NAME> }} syntax. The retrieved values can be made available to the step which needs them through its env parameter:

release.yml
name: Release to the app store

on:
  push:
    tags:
      - 'v*.*.*'
jobs:
  release:
    runs-on: macos-12

    steps:
      - uses: actions/checkout@v3
      - name: Upload to AppStore
        run: bundle exec fastlane build_and_upload_to_appstore
        env:
          APP_STORE_CONNECT_API_KEY_CONTENT: ${{Secrets. APP_STORE_CONNECT_API_KEY_CONTENT}}
          APP_STORE_CONNECT_ISSUER_ID: ${{Secrets. APP_STORE_CONNECT_ISSUER_ID}}
          APP_STORE_CONNECT_API_KEY_ID: ${{Secrets. APP_STORE_CONNECT_API_KEY_ID}}

The names for the environment variables don’t need to match those of the secrets. For the sake of clarity, I decided to keep the naming consistent across the board.

The app_store_connect_api action

Last but not least, the build_and_upload_to_appstore lane needs to retrieve the environment variables we set in the previous section and tell Fastlane to use the App Store Connect API key we have created.

Fastlane has a way of retrieving environment variables through an object called ENV. ENV is a dictionary which holds all available environment variables as key-value pairs. This is how you can use it to retrieve the App Store Connect API secrets:

Fastfile
lane :build_and_upload_to_appstore do
  asc_key_content = ENV["APP_STORE_CONNECT_API_KEY_CONTENT"]
  asc_issuer_id = ENV["APP_STORE_CONNECT_ISSUER_ID"]
  asc_key_id = ENV["APP_STORE_CONNECT_API_KEY_ID"]
end

Now that all secrets are available, we can pass them through to Fastlane’s app_store_connect_api_key built-in action, which will handle authentication for us:

Fastfile
lane :build_and_upload_to_appstore do
  asc_key_content = ENV["APP_STORE_CONNECT_API_KEY_CONTENT"]
  asc_issuer_id = ENV["APP_STORE_CONNECT_ISSUER_ID"]
  asc_key_id = ENV["APP_STORE_CONNECT_API_KEY_ID"]

  app_store_connect_api_key(
	  key_id: asc_key_id,
	  issuer_id: asc_issuer_id,
	  key_content: asc_key_content
	)
end

When the app_store_connect_api_key action is executed, it sets a value in the lane shared context which makes the API key available to any other actions or lanes, such as deliver.

This means that you don’t need to hold on to the key yourself, Fastlane will do the heavy lifting for you.

If you wanted to hold on to the key, the app_store_connect_api_key action also returns it.

That’s it! You have safely and successfully set up authentication with an App Store Connect API key using Fastlane! 🎉