The project automates the iOS app distribution. It automatically processes access requests and cuts wait times caused by time zone differences from 24 hours to under 20 minutes.
Apple restricts devices from installing and using apps outside the Apple App Store without establishing a form of trust between the device and the app’s developer through the Apple Developer Program. Firebase simplifies this process by providing tools for app distribution.
However, the process required manual work from the engineer to register devices and manage access. When there's a timezone difference between the engineer and new users, it could take a day for the engineer to see a new access request.
This project automates the work required on the developer's side to grant access to the app after a user registers their device with Firebase. Users now only need to wait approximately 15 minutes after registering their device with Firebase, after which they will receive an email from Firebase that informs the build is ready to be downloaded and installed.
Follow the step-by-step guide here to request access and register your device.
There’s no question whether automating the process of building and distribution is worth it because doing it manually would cost a substantial amount of time to make small releases that are necessary for identifying issues early.
Automating the registration of device UDIDs and the management of the email list required by the Firebase CLI may seem unnecessary, but it provides several benefits. It mitigates the risk of human errors like entering the wrong UDID or email address. It also protects engineers from frequent context switching as they would otherwise need to switch to these tasks every time someone new needs access. Lastly, it eliminates the need for coordination between testers and engineers just for app access.
The project consists of three main components:
Firebase (App Distribution) and Apple Developer Account (via Apple Connect API) are external services that do not require any coding.
iOS Build GitHub Workflow is in iOSAppAccessAutomation repo. Normally, it would be in the KMP project's repo but because the KMP project's repo is submitted as part of a university assignment, it's locked until I get my final result. Once unlocked, iOS Build GitHub Workflow can be moved into the KMP project's repo. However, this is low priority and not planned.
Please note that the Node.js service in iOSAppAccessAutomation isn't necessary if the emails are managed as tester groups. As the Firebase CLI allows distribution by a group name, a group name can be set in the GitHub workflow only once and re-used for all builds. However, during the assignment, the automation aspect wasn't factored into implementation decisions. This led to the use of a Firebase invite link that doesn't add the user's email to a group. As a result, this automation project needs a service to manage an email list.
This is used in iOSAppAccessAutomation.
Dependency Chain Abuse is listed in the OWASP Top 10 CI/CD Security Risks. Therefore, any third-party GitHub action that requires sensitive credentials, such as API keys or access tokens, as arguments is not used in this project.
This server is deployed on Heroku. To enable HTTPS, Heroku uses Server Name Indication (SNI), an extension of the widely supported TLS protocol (Heroku, 2024). This ensures encrypted communication over the network to prevent eavesdropping and man-in-the-middle attacks.
To ensure only iOSAppInviteAlert can save device information into iOSAppAccessAutomation, an asymmetric encryption called RSA is used.
iOSAppInviteAlert has a private key to generate JWTs. This private key is stored in Firebase Cloud Secret Manager as iOSAppInviteAlert is a Firebase Cloud Function.
iOSAppAccessAutomation has a public key to verify the JWTs from iOSAppInviteAlert. iOSAppAccessAutomation will reject a request to save device information if the JWT isn’t signed with the matching private key stored in Firebase Cloud Secret Manager. The public key is stored in Heroku configuration variables as iOSAppAccessAutomation is deployed on Heroku.
For an organization, Heroku configuration variables may not be a viable option because Heroku doesn’t provide a way to pick and choose what features of Heroku a team member can have access to. This makes the secret keys visible to anyone who has access to Heroku dashboard. However, for this project, because I’m the sole engineer, this is not an issue.
To ensure only the GitHub workflow in this project can download the email list from iOSAppAccessAutomation, the same security design is used.
The GitHub workflow uses a private key stored in GitHub Secret to generate JWTs to identify itself when communicating with iOSAppAccessAutomation. iOSAppAccessAutomation verifies these JWTs using a public key stored in Heroku configuration variables.
Please, note that the RSA key pair used here is not the same the key pair used for communications between iOSAppInviteAlert and iOSAppAccessAutomation. This enables separate access management.
Rotating the RSA keys periodically would minimize the impact of key compromise. This will be done as part of learning secure infrastructure management though the exact date for this isn't set yet. Priorities are reviewed every 4 months.
Logging and monitoring will also be done as part of learning secure infrastructure management.
If a user invites themselves using a new email but with a device that's already registered, Apple Connect API will return an error and iOSAppInviteAlert won't call the GitHub workflow to create a new build.
This is not a problem because the latest build will have already been built with a provisioning profile that's configured with the device's UDID since it's already registered.
However, the new email will replace the existing email in the device table in iOSAppAccessAutomation so that the new email can receive notifications for new builds. It's designed this way because the history of device ownership is not important in this project.
As for the old email in user table, since it won't be cleared, it will continue to receive updates for new builds. This means the user can use either email to access the latest build.
No. | Task | Component | Hours | Status |
---|---|---|---|---|
1 | Design Doc
|
8 | Done | |
2 | Distribution Certificate in GitHub Actions
|
iOS build GitHub workflow | 4 | Done |
3 | Provisioning Profile in GitHub Actions
|
iOS build GitHub workflow | 4 | Done but discarded |
4 | Build the iOS app using GitHub Actions
|
iOS build GitHub workflow | 8 | Done |
5 | Listen to new device registration in Firebase using Firebase Cloud Functions
|
iOSAppInviteAlert | 4 | Done |
6 | Register new Apple device UDIDs using Apple Connect API through Firebase Cloud Functions | iOSAppInviteAlert | 4 | Done |
7 | Update the Provisioning Profile using Apple Connect API through Firebase Cloud Functions
|
iOSAppInviteAlert | 1 | Canceled |
8 | Project Revision
|
2 | Done | |
9 | Design secure communication between iOSAppInviteAlert and iOSAppAccessAutomation backend app
|
iOSAppInviteAlert | 2 | Done |
10 | Write an API for iOSAppInviteAlert to send emails and UDIDs
|
iOSAppAccessAutomation | 4 | Done |
11 | Send the device model, email and the UDID of the new device to iOSAppAccessAutomation backend app through Firebase Cloud Functions | iOSAppInviteAlert | 4 | Done |
12 | Download the device list from Apple in GitHub Actions
|
iOS build GitHub workflow | 4 | Done |
13 | Create a Provisioning Profile from GitHub Actions
|
iOS build GitHub workflow | 4 | Done |
14 | Trigger iOS build process in GitHub Actions through Firebase Cloud Functions
|
iOSAppInviteAlert | 2 | Done |
15 | Design secure communication between the GitHub workflow and iOSAppAccessAutomation backend app | iOS build GitHub workflow | 1 | Done |
16 | Write an API for the GitHub workflow to download the list of emails
|
iOSAppAccessAutomation | 2 | Done |
17 | Download the list of emails from iOSAppAccessAutomation
|
iOS build GitHub workflow | 2 | Done |
18 | Distribute the app through Firebase App Distribution
|
iOS build GitHub workflow | 4 | Done |
19 | Download provisioning profile list
|
iOS build GitHub workflow | 2 | Done |
20 | Delete all old provisioning profiles
|
iOS build GitHub workflow | 2 | Done |
21 | Bump iOS build version for every release
|
iOS build GitHub workflow | 2 | Done |
22 | Final end-to-end test | 2 | Done | |
Total | 72 |
Initially, it was designed to update the provisioning profile from the Firebase Cloud Function but later, it was found that Apple Connect API does not support updating provisioning profile. It only provides APIs for creating and deleting.
Because of it, it's decided to create a new provisioning profile for every build using GitHub Workflow instead.
Creating a provisioning profile returns the created provisioning profile as part of the response. Therefore, the task number 3 in the Task Breakdown was discarded.
The task number 7 is also canceled but because the time spent on this task revealed the missing information that led to this revision, it's still marked as 1 hour spent on this task.
This revision increased the project timeline by 13 hours.
The discarded initial design can be found here.
Estimating something unknown has always been problematic for me. Sometimes, I estimate 16 hours and it's done in 4 hours. Sometimes, it's the other way around. This makes stakeholders unhappy because it's difficult for them to manage project timelines.
This project was carried out with a focus on figuring out what can be done to address this weakness of mine and applying the findings along the way.