Migrate Fastlane to use Auth Keys

With the new requirement of having two factor authentication enabled on all apple id’s it is getting more frustrating to have CI upload apps to TestFlight as you have to manage a Fastlane/iTunes session.

This can be fixed by migration to use the new auth key provided by Appstore Connect. Fastlane already supports this so it is really easy to update any existing lanes to use the auth key.

I wrote another article on how to build and deploy your iOS app with GitHub Actions. This article uses the old method and now I want to show you how to migrate it.

Getting the Auth Key

First you need to head over to Appstore Connect and create a new auth key.

Generate a new auth key with minimum of App Manager access level
Copy the issuer id and key id and download the key

So now you have 3 pieces of information

  • The issuer ID
  • The Key ID
  • The key it self (a .p8 file)

Next step is to setup a json file that contains the information and then modify fastlane to use it.

Authkey.json

Create a file called authkey.json and paste/modify the json below.

{
"key_id": "paste key id from appstore connect",
"issuer_id": "paste issuer id from appstore connect",
"key”: "paste in p8 contents here",
"duration": 1200,
"in_house": false
}

In the key part you must replace linebreaks with \n making it one line.

Example:

{
"key_id": "xxxXXxxXxx",
"issuer_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"key”: "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----",
"duration": 1200,
"in_house": false
}

Modifying Fastlane

This is just a matter of adding the key path to the relevant commands.

Match

match(type: "appstore", readonly: is_ci, api_key_path: "authkey.json")

Upload to TestFlight

upload_to_testflight(changelog: ENV["CHANGELOG"] || "No changelog provided", api_key_path: "authkey.json")

Full Example

This example is taken from my other article about fastlane and Github Actions.

default_platform(:ios)
platform :ios do
desc "Run tests"
lane :tests do
run_tests(scheme: "iOS")
end
desc "Push a new beta build to TestFlight"
lane :beta do
setup_ci
match(type: "appstore", readonly: is_ci, api_key_path: "authkey.json")
build_app(workspace: "Timeable.xcworkspace", scheme: "iOS")
upload_to_testflight(changelog: ENV["CHANGELOG"] || "No changelog provided", api_key_path: "authkey.json")
end
desc "Sets the version of the bundle to a RELEASE_VERSION passed in as an environment variable"
lane :set_release_version do
version = ENV["RELEASE_VERSION"]
if version
UI.message("Setting version to #{version}")
increment_version_number(version_number: version)
increment_build_number(build_number: Time.now.to_i)
else
UI.user_error!("Environment variable RELEASE_VERSION not set")
end
end
end

Modifying Github Actions

We do not want to commit the raw authkey.json to git (I added the json file to my .gitignore so i wont commit it by mistake.), but rather an encrypted one. GitHub recommends that we use gpd.

$ gpg --symmetric --cipher-algo AES256 authkey.json

This gives you a new file called authkey.json.gpg.

Save the password and add it as a secret to your GitHub Actions.

Add an action secret called “APPSTORE_AUTHKEY” with the password you gave gpd when asked

Next up is to update GitHub Action to create the authkey.json file with the contents of the secret.

decrypt_secret.sh

This scripts takes the APPSTORE_AUTHKEY variable with the password and decrypts the file into a plain text file that fastlane can read.

#!/bin/shgpg --quiet --batch --yes --decrypt --passphrase="$APPSTORE_AUTHKEY" --output authkey.json authkey.json.gpg

Remember to add chmod +x on decrypt_secret.sh to make it executable.

.github/workflows/testflight.yml

Update the GitHub workflow to decrypt the json file so fastlane can pick it up.

name: Deploy to Testflight on:
release:
types: [created]jobs:
deploy:
name: Deploy
runs-on: macos-latest

steps:
- name: Checkout
uses: actions/checkout@v2 - name: Select Xcode Version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_KEY }}
known_hosts: ${{ secrets.KNOWN_HOSTS }}

- name: Setup ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.2
bundler-cache: true

- name: Install Pods
run: pod install

- name: Build & Distribute to Testflight
run: |
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
./decrypt_secret.sh
bundle exec fastlane set_release_version
bundle exec fastlane beta
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }
RELEASE_VERSION: ${{ github.event.release.tag_name }}
APPSTORE_AUTHKEY: ${{ secrets.APPSTORE_AUTHKEY }}
CHANGELOG: ${{ github.event.release.body }}

We basically just added one step, call decrypt_secret.sh. You can test it out locally by setting the APPSTORE_AUTHKEY environment variable.

./decrypt_secret.sh

Commit the updated workflow and test it out. GitHub Actions should now be able to test and deploy your iOS app.

This is a follow up article on how to build and deploy your iOS app with GitHub Actions.

--

--

--

I work as a software developer with years of experience within the field of web, apps and server architecture.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Apache Kafka — 101 & How It Comes to Picture

My dive into open source with FOSSASIA during Google Code-in

Gravitational pull=null

Crash Course for learning Web Development

Shakti-powered spreadsheet

Hedge Testnet Guide

A Simple Blueprint For Building Sustainable APIs

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Rasmus Styrk

Rasmus Styrk

I work as a software developer with years of experience within the field of web, apps and server architecture.

More from Medium

Constructing A Swift Data Model for Dummies

Parsing GPX files with Swift using declarative programming

Distributing Binary Frameworks as Swift Packages

iOS: Certificate pinning Part — 2 (GraphQL)