Skip to content

fix(mac): use correct MAS options in signApp() for Mac App Store builds. Closes #9507#9508

Closed
abergs wants to merge 1 commit intoelectron-userland:masterfrom
abergs:anders/sign-bug
Closed

fix(mac): use correct MAS options in signApp() for Mac App Store builds. Closes #9507#9508
abergs wants to merge 1 commit intoelectron-userland:masterfrom
abergs:anders/sign-bug

Conversation

@abergs
Copy link
Copy Markdown

@abergs abergs commented Jan 16, 2026

This PR tries to fix #9507

Note: I've also put up a competing PR that solves the same problem differently. The competing fix also address the fact that we're signing MAS apps twice.

I've tried this code on the repro https://github.com/abergs/eb-mas-bug-repro and it worked, with output examples below.

When building for Mac App Store (MAS), the signApp() method always passed null for masOptions to sign(), causing it to use Darwin (Developer ID) settings instead of MAS settings. This resulted in:

  1. Wrong provisioning profile being used (mac.provisioningProfile instead of mas.provisioningProfile)
  2. Wrong certificate type being searched for ("Developer ID Application" instead of "3rd Party Mac Developer Application")
  3. Build failures in CI when the Darwin provisioning profile doesn't exist
  4. Incorrect platform=darwin instead of platform=mas

Root Cause

In packages/app-builder-lib/src/macPackager.ts, the signApp() method was calling:

await this.sign(path.join(sourceDirectory, file), null, null, packContext.arch)

The sign() method determines isMas based on whether masOptions is null:

const isMas = masOptions != null  // null means darwin, not MAS

So passing null always resulted in isMas = false, even when packContext.electronPlatformName === "mas".

The Fix

Modified signApp() to detect MAS builds using packContext.electronPlatformName and construct the appropriate MAS options:

const isMas = packContext.electronPlatformName === "mas"
let masOptions: MasConfiguration | null = null
if (isMas) {
  masOptions = deepAssign({}, this.platformSpecificBuildOptions, this.config.mas)
  const isMasDev = packContext.targets.some(t => t.name === "mas-dev")
  if (isMasDev) {
    deepAssign(masOptions, this.config.masDev, { type: "development" })
  }
}

Test Results

Tested with a reproduction project using different provisioning profiles for mac vs mas config:

{
  "mac": { "provisioningProfile": "developer_id.provisionprofile" },
  "mas": { "provisioningProfile": "appstore.provisionprofile" }
}

Before Fix (v26.4.0)

arm64:

• signing file=dist/mas-arm64/App.app platform=darwin type=distribution
         provisioningProfile=developer_id.provisionprofile

universal:

• signing file=dist/mas-universal/App.app platform=darwin type=distribution
         provisioningProfile=developer_id.provisionprofile

After Fix

arm64:

• signing file=dist/mas-arm64/App.app platform=mas type=distribution
         provisioningProfile=appstore.provisionprofile

universal:

• signing file=dist/mas-universal/App.app platform=mas type=distribution
         provisioningProfile=appstore.provisionprofile

Affected Builds

  • mas (single-arch and universal)
  • mas-dev (single-arch and universal)

Breaking Changes

None known. But I'm not an expert, merely a tourist.

When building for MAS, signApp() was always passing null for masOptions
to sign(), causing it to use Darwin (Developer ID) settings instead of
MAS settings. This resulted in wrong provisioning profiles and
certificate types being used.

The fix detects MAS builds via packContext.electronPlatformName and
constructs the appropriate MAS options before calling sign().

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 16, 2026

⚠️ No Changeset found

Latest commit: d5c17b0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

if (isMas) {
masOptions = deepAssign({}, this.platformSpecificBuildOptions, this.config.mas)
// Check if this is a mas-dev build
const isMasDev = packContext.targets.some(t => t.name === "mas-dev")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might backfire since we're using some.

signApp is supposed to be per-target, so an invocation of it could be from a t.name: mas, then a 2nd invocation of it could be t.name: mas-dev. With this current logic, type: development would be set on both invocations.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though I don't necessarily like this specific PR over #9509, but would it be more appropriate to use packContext.electronPlatformName? Tried to grep through and I don't think electronPlatforname necesarily support mas-dev, only mas.

Actually. looking at the code again it seems siSignAfterPack also has a platformSpecificBuildOptions argument with a .target. Perhaps we could feed that through to signApp

@mmaietta
Copy link
Copy Markdown
Collaborator

Breaking Changes
None known. But I'm not an expert, merely a tourist.

Thanks for being an excellent tourist 🙂

@mmaietta
Copy link
Copy Markdown
Collaborator

mmaietta commented Feb 6, 2026

I'm going to pick this up in the larger logic split in #9567

@mmaietta mmaietta closed this Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MAS builds/signing fail with wrong provisioning profile due to signApp() ignoring MAS options

2 participants