WWDC 2021 has ended. Now it’s time to look at all those developer tools Apple delivered and see how it can improve our work as software engineers.
One notable change for macOS developers is a new notarization tool called
notarytool. For one, the new tool promises faster uploads thanks to S3 transport optimizations. To clarify, it would upload your binary to one of Amazon Web Services’ many servers scattered throughout the globe, which would have a better chance of being nearer to you.
Changes in Notarization Workflow
Though more importantly
notarytool has the ability to wait for notarization to complete. To put it differently, the feature removes the need in your custom notarization workflow have a polling loop which repeatedly checks for notarization status. Therefore reducing the amount of automation code that you need to write or maintain.
Furthermore there is a dedicated command to fetch notarization logs. To emphasize, previously you would need two steps to obtain those logs. Firstly to get notarization information which contains the log URL. Secondly another tool needs to fetch that URL.
Equally important is a set of commands for managing keychain entries for use with notarization. Both API key and app-specific password authentication methods can use the keychain. A point often overlooked is that you can swap authentication methods without any code change done to the notarization workflow. In essence, updating the keychain entry to the new authentication method would be sufficient.
By comparison, some workflow elements are unchanged. Chiefly steps surrounding stapling stays the same. Accordingly the following activity diagram shows the updated generic notarization workflow as per Xcode 13 Beta 3. To emphasize, there is only one polling loop—waiting for the notarization to be available for stapling. Given that
notarytool would handle the task to poll the notarization status by itself.
You initiate the workflow by submitting a notarizable artifact. As of Xcode 13, you would use
notarytool submit to upload the package and optionally make it wait for the Notarization Service to finish processing the package. As a result you would get a UUID identifying your upload.
For example, the following snippet begins the notarization workflow by uploading
MyGreatApp.zip using an API Key file and let the tool wait for the process to complete.
xcrun notarytool submit MyGreatApp.zip \ --wait \ --key ~/.appstoreconnect/private_keys/AuthKey_123456789A.p8 \ --key-id 123456789A \ --issuer 00000000-0000-0000-0000-000000000000
Should notarization shows a successful result, you should staple the notarization ticket back to the original artifact. Presently stapling hasn’t change in Xcode 13. Thus it uses the same command as with previous Xcode versions and uses the same arguments.
Refer to the following snippet for an example on how to staple notarization ticket into an app bundle.
xcrun stapler staple MyGreatApp.app
To emphasize, stapling does not require authentication. A notarization ticket is public information; the same information as what Gatekeeper would try to obtain when the ticket is not embedded in the bundle.
Should the notarization process completes with a failure result, you would want to know why. Correspondingly the notarization log would provide this information.
notarytool log to obtain the log information about a notarization request. Provide the command with the UUID of the notarization request along with the standard authentication information needed by
xcrun notarytool log a5e801fe-96ae-4fe7-bbaa-aff532326471 \ --key ~/.appstoreconnect/private_keys/AuthKey_123456789A.p8 \ --key-id 123456789A \ --issuer 00000000-0000-0000-0000-000000000000
In addition, notarization logs would always be available upon completion of the notarization process. In other words, you can obtain these logs even when notarization completes successfully. Then again, Apple have not provide any data retention guarantees for these logs; thus you should download and archive this as soon as possible.
Authentication and Authorization
Similar to Xcode 12, there are two ways to authenticate to the Notary Service—via app-specific password or an API key file. But this time you can place the API key file anywhere within the local file system. To clarify, you are no longer restricted to maintain API key files in certain pre-defined directories as what
notarytool is able to manage keychain entries for you. It can save either an API key file or an app-specific password into the keychain. Subsequent invocations of
notarytool would refer to this credential name. This would be useful since the keychain is additionally encrypted with the user’s login password, making it less prone to leaks since even administrators won’t be able to access the keychain without knowledge of the logged-in password.
In contrast, the authorization flows remains the same. You would use the App Store Connect web application to assign roles to pre-existing Apple ID e-mail addresses or generate an API key in the same manner as before.
Authentication on the Command Line
The following is an example on how to get notarization history via an API key file. Markedly this file is now specified by a path. Nevertheless the example shows retrieval of the API key file in one of the pre-defined locations expected by
xcrun notarytool history \ --key ~/.appstoreconnect/private_keys/AuthKey_123456789A.p8 \ --key-id 123456789A \ --issuer 00000000-0000-0000-0000-000000000000
Whereas the following is an example on the same command but using an app-specific password to authenticate with the Notary Service.
xcrun notarytool history \ --apple-id 'email@example.com' \ --password '99-apples-on-the-wall' \ --team-id 123456789A
Authentication using the Keychain
store-credentials sub-command of
notarytool would store either an API key or an app-specific password into the Keychain. You would give this command a name for the keychain entry. Accordingly, use this name in subsequent invocations to
notarytool as its authentication information.
The following snippet shows an example on how to store an API key into the Login Keychain and name the entry as “My-Notarization-Key”
xcrun notarytool store-credentials My-Notarization-Key \ --key ~/.appstoreconnect/private_keys/AuthKey_123456789A.p8 \ --key-id 123456789A \ --issuer 00000000-0000-0000-0000-000000000000 --sync
In similar fashion the following snippet stores an app-specific password into the Keychain as “My-Notarization-Key”.
xcrun notarytool store-credentials My-Notarization-Key \ --apple-id 'firstname.lastname@example.org' \ --password '99-apples-on-the-wall' \ --team-id 123456789A
Given the above examples, storing a notarization credential would overwrite any pre-existing credential—should one with the same name is present in the Keychain. In effect, the last credential stored would prevail.
notarytool would access a notarization credential through the
--keychain-profile command-line argument. When this argument is present, you would not need to specify parameters pertaining to an API key file nor an app-specific password.
For example, the following snippet accesses notarization history through a credential stored in the Keychain.
xcrun notarytool history \ --keychain-profile My-Notarization-Key
You should try out
notarytool and see how it can simplify your notarization script. Despite that Xcode 13 is in beta, it can be considered ready for production use as far as notarization goes.
Unquestionably you would need to download Xcode beta and configure the command line tools to use it. To that end, use the
xcode-select command to select the preferred copy of Xcode for use in the command line, as per the following example.
xcode-select -s /Applications/Xcode-beta.app
altool (the old notarization tool) is present and usable in Xcode 13 beta. Though when Xcode 13 comes out of beta,
altool will be officially deprecated. To put it differently, you should update your custom notarization workflow scripts to use the new tool before Xcode 14 is released, otherwise risk breaking your delivery pipelines.