Getting an Electron app ready for macOS distribution
Easy start!
If you are from a JavaScript background, you would have found it extremely easy to develop a cross platform desktop app using Electron. After developing it on Windows, your curiosity piqued and probably tried to run the project on macOS. Much to your amazement it builds and runs perfectly fine on macOS too!
The logical next step is to give your Amazing App.exe to your friend. This would have gone well too — the process for creating a distributable using Electron Forge is quite straightforward. Much to your dismay, the perfectly functioning macOS app that ran on your Mac, faces an alarming message when your friend downloads it and tries to run it:
Neither options provided include an option to use the app. In fact, the alert suggests that you immediately move it to the bin!
App notarization on macOS
The single reason for this mishap is that the generated Amazing App.app macOS bundle has not been signed. You can read about the signing process and how it upholds app integrity at the official Apple documentation. This article looks at doing just that — so you can create an Electron app that anyone can download! After following the instructions here, you can generate a macOS app that gives the below alert (like almost all other macOS software downloaded from a web page):
Requirements
To follow through with this guide, you should have an Electron project that runs and builds fine on macOS. Most steps in notarization process require Xcode 10 or later and macOS 10.13.6 or later. You need a paid Apple developer for app notarization. We will use Electron Forge to build and package the app. Their documentation covers both scenarios where you are creating a new forge-based project as well as adding Electron Forge support to an existing one.
We are going with Electron Forge because it has built in support to handle the notarization process. We just need to configure it!
Configuring Electron Forge
Add an entry called forge
in the config
object of the package.json file. Add the string build-scripts/forge.config.js
to this key. After this, your package.json
file should look a bit like this:
This instructs forge to use the configuration that we will define at forge.config.js
in the build-scripts
folder.
This file should look a bit like this:
There probably will be other configuration settings including name
...etc. If you already had a configuration, you can copy those settings over to this file.
Electron app signing
The osxSign
object configures the app signing procedure. hardenedRuntime
option requests the signing procedure to add protections against certain exploits like code injection, hijacking...etc (more details at the official Apple documentation). The identity
option is automatically deduced if you have signed in to your Apple developer account on Xcode. You will also have to add a signing certificate (of type Developer ID Application) on Xcode. To do this open Xcode → Preferences → Accounts → Select your Apple ID → Manage Certificates... → Add button → Developer ID Application → Done. The screenshot below should be of help:
It is required to add a list of all permissions the app would access in its lifetime. This is done using the entitlements.plist
file inside our build-scripts
folder. A minimal entitlements.plist
file looks like this:
For versions older than Electron 12, the com.apple.security.cs.allow-jit
was required - it is advised to not use it anymore if it can be avoided. Add any other permissions that your Electron app might need, for example, microphone access and camera access as described here.
Notarization
The osxNotarization
object configures the notarization process. The notarization process uploads almost all constituents of the macOS app to the Apple notary service. So be ready for an upload of no less than 60 MB when it comes to an Electron app! If the notary service detects no issue, the service sends a ticket. This ticket is attached onto the app bundle. For a more insightful explanation about the process, you can look into the Apple's official documentation on app notarization. Not to worry, we just need to provide the osxNotarization
configuration. Electron Forge takes care of the rest!
Under the hood, Electron Forge uses electron-notarize
plugin for notarization. The tool
option can be either notarytool
or legacy
. We will use notarytool
as this is faster and has more authentication methods (to ensure that our app bundle is uploaded securely and privately). The most convenient method is to use the API key authentication method. We store the API keys in a separate file build-keys.json
inside the build-scripts
folder. This is so that we can commit the forge.config.js
file to version control without having to commit the API keys too. In short, the sole purpose of the API keys is to let notarytool
upload the app bundle on our behalf.
Firstly, sign in to your App Store Connect account using a web browser. Go to Users and Access. Click on the Keys tab and then on the App Store Connect API. Next, click on the add button and generate an API key with App Manager access.
Download the API key — remember that you can only do this once! So store a backup securely in a safe place. Do not commit this file. And point the apiKey
option to this file. Next, fill in the apiKeyId
and the apiIssuer
options.
Gotchas
If you have an advanced packaging configuration that copies files to within the app bundle. Make sure that Amazing App.app/Contents/MacOS
folder has only the executable. Having any other files in this directory could fail the notarization process. Any extra resources should go in the aptly names Resources
folder within app package.
Done!
That’s about it for the whole configuration process! You can generate a macOS app bundle using yarn make
or the relevant make command as defined in packageConfig.json
. To verify that the notarization process was successful, you can run
spctl -a -vvv -t install "Amazing App.app"
Finally, if all has gone well, you should see a response like the one below:
Thanks to Sachintha for the thumbnail!