Getting an Electron app ready for macOS distribution

Praveen Jayakody
Ascentic Technology
5 min readOct 13, 2022

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:

You only get two options and one of them is delete the app!

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):

A better prompt

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!

--

--