Sandboxed Electron macOS app can't access Photos library

I have an Electron app built for macOS, and it was distributed via 'Developer ID' for years, it worked well and I was able to access the photos in the system Photos library. Surely I already have the 'NSPhotoLibraryUsageDescription' key in Info.plist.

Recently we are trying to publish this app to Mac App Store, so I have to turn on the sandbox, after that the app starts giving XPC errors while accessing the Photos library. The errors look like:

PHAuthorizationStatus: Authorized
CoreData: XPC: sendMessage: failed #0
CoreData: XPC: Unable to sendMessage: to server
...
CoreData: XPC: sendMessage: failed #7
CoreData: XPC: Unable to connect to server with options {
    NSPersistentHistoryTrackingKey = 1;
    NSXPCStoreServerEndpointFactory = "<PLXPCPhotoLibraryStoreEndpointFactory: 0x7fc67e8af370>";
    skipModelCheck = 1;
}
CoreData: XPC: Unable to load metadata: Error Domain=NSCocoaErrorDomain Code=134060 "A Core Data error occurred." UserInfo={Problem=Unable to send to server; failed after 8 attempts.}
CoreData: fault: Unable to create token NSXPCConnection.  NSXPCStoreServerEndpointFactory 0x7fc67e8af370 -newEndpoint returned nil
CoreData: error: Failed to create NSXPCConnection

It seems the app could detect the current PHAuthorizationStatus which is Authorized, but it can't fetch the photos from the Photos library (using PhotoKit).

I learned from here that I could look for errors from the sandboxd daemon, so I did that, here is what I saw:

Sandbox: Picture Keeper(32625) deny(1) mach-lookup com.apple.photos.service
Violation:       deny(1) mach-lookup com.apple.photos.service
Process:         Picture Keeper [32625]
Path:            /Applications/Picture Keeper.app/Contents/MacOS/Picture Keeper
Load Address:    0x103bd3000
Identifier:      com.simplifieditproducts.picturekeepermas
Version:         4575 (4.5.75)
Code Type:       x86_64 (Native)
Parent Process:  Picture Keeper [1]
Responsible:    /Applications/Picture Keeper.app/Contents/MacOS/Picture Keeper
User ID:         501

Date/Time:       2024-08-26 16:16:14.645 EDT
OS Version:      macOS 14.5 (23F79)
Release Type:    User
Report Version:  8

MetaData: {"process_path":["Users","Kevin","Projects","Electron","picturekeeper-electron","dist","picturekeeper","mas-dev","Picture Keeper.app","Contents","MacOS","Picture Keeper"],"apple-internal":false,"primary-filter":"global-name","policy-description":"Sandbox","flags":5,"platform-policy":false,"build":"macOS 14.5 (23F79)","process-path":"\/Applications\/Picture Keeper.app\/Contents\/MacOS\/Picture Keeper","responsible-process-path":"\/Applications\/Picture Keeper.app\/Contents\/MacOS\/Picture Keeper","primary-filter-value":"com.apple.photos.service","platform_binary":"no","responsible-process-signing-id":"com.simplifieditproducts.picturekeepermas","hardware":"Mac","target":"com.apple.photos.service","action":"deny","mach_namespace":1,"checker-pid":1,"container":"\/Users\/Kevin\/Library\/Containers\/com.simplifieditproducts.picturekeepermas\/Data","binary-in-trust-cache":false,"team-id":"LU744924UY","process":"Picture Keeper","global-name":"com.apple.photos.service","platform-binary":false,"pid":32625,"summary":"deny(1) mach-lookup com.apple.photos.service","checker":"launchd","responsible-process-team-id":"xxxxx","operation":"mach-lookup","normalized_target":["com.apple.photos.service"],"errno":1,"uid":501,"profile-flags":0,"profile-in-collection":false,"sandbox_checker":"launchd","signing-id":"com.simplifieditproducts.picturekeepermas","release-type":"User"}

I believe I already have the necessary entitlements for the Photos library, see:

codesign -d --entitlements - /Applications/Picture\ Keeper.app/Contents/MacOS/Picture\ Keeper

[Dict]
	[Key] com.apple.application-identifier
	[Value]
		[String] xxxx.com.simplifieditproducts.picturekeepermas
	[Key] com.apple.developer.team-identifier
	[Value]
		[String] xxxx
	[Key] com.apple.security.app-sandbox
	[Value]
		[Bool] true
	[Key] com.apple.security.application-groups
	[Value]
		[Array]
			[String] xxxx.com.simplifieditproducts.picturekeepermas
	[Key] com.apple.security.assets.movies.read-only
	[Value]
		[Bool] true
	[Key] com.apple.security.assets.music.read-only
	[Value]
		[Bool] true
	[Key] com.apple.security.assets.pictures.read-write
	[Value]
		[Bool] true
	[Key] com.apple.security.cs.allow-dyld-environment-variables
	[Value]
		[Bool] true
	[Key] com.apple.security.cs.allow-jit
	[Value]
		[Bool] true
	[Key] com.apple.security.cs.allow-unsigned-executable-memory
	[Value]
		[Bool] true
	[Key] com.apple.security.cs.disable-executable-page-protection
	[Value]
		[Bool] true
	[Key] com.apple.security.cs.disable-library-validation
	[Value]
		[Bool] true
	[Key] com.apple.security.device.usb
	[Value]
		[Bool] true
	[Key] com.apple.security.files.bookmarks.app-scope
	[Value]
		[Bool] true
	[Key] com.apple.security.files.bookmarks.document-scope
	[Value]
		[Bool] true
	[Key] com.apple.security.files.downloads.read-only
	[Value]
		[Bool] true
	[Key] com.apple.security.files.user-selected.read-write
	[Value]
		[Bool] true
	[Key] com.apple.security.network.client
	[Value]
		[Bool] true
	[Key] com.apple.security.network.server
	[Value]
		[Bool] true
	[Key] com.apple.security.personal-information.location
	[Value]
		[Bool] true
	[Key] com.apple.security.personal-information.photos-library
	[Value]
		[Bool] true

By the way, the Photos library related code was built into a .node file (which is a dylib), and it will be loaded by the main executable during runtime.

Anything I missed? Thank you!

Answered by haoxi911 in 801965022

Hi Quinn, even though I still don't understand the root cause, I was able to tell that the problem seems to be related to the Electron framework itself. Would you mind update the title of this post by adding "Electron" to it?

For anyone who are building Mac App Store apps with Electron and met similar XPC errors, it seems the issue has been addressed since Electron v26.

Electron 25.9.8 with Node 18.15.0: Doesn't work

Electron 26.0.0 with Node 18.16.1: Works fine

It’s hard to say what’s going on here without more context. However, I used Xcode to create a simple test app that requests photos library access and it worked for me. Here’s what I did:

  1. Using Xcode 16.0b6 on macOS 14.5, I created a new project from the macOS > App template. This template always enables the App Sandbox.

  2. In Signing & Capabilities > Hardened Runtime, I checked Photos Library. This added com.apple.security.personal-information.photos-library to the entitlements file.

  3. In the Info tab, I added a NSPhotoLibraryUsageDescription property with my usage description string.

  4. I added two buttons to the content view:

    Button("Request") {
        PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
            print(status.rawValue)
        }
    }
    Button("List") {
        let result = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
        result.enumerateObjects { collection, i, _ in
            print(i, collection.localizedTitle ?? "-")
        }
    }
    
  5. I ran the app.

  6. I clicked the Request button.

  7. The system presented the authorisation UI. I allowed the access.

  8. The app printed 3, that is, .authorized.

  9. I clicked the List button. The app printed a long list of my album names.

I recommend that you repeat this test yourself, just to set a baseline. Presumably it’ll work; if it doesn’t, let me know. And once you get that working, you can then compare the setup of your working test app with your failing main app.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn, Thank you so much for your reply! I created the demo app as you suggested and I did notice an issue. In my failing main app, I didn't enable Hardened Runtime. By enabling the Hardened Runtime, the main app simply crashes after launch for some reason.

I am wondering if it is mandatory to have Hardened Runtime in order to access the Photos library in a Sandboxed app? In Xcode, the UI seems already tell me that it is required, but I am wondering if you could confirm it.

Oh, interesting. I’m not aware of specific dependency on the hardened runtime here. Weird.

Regardless, the path forward is clear: Enable the hardened runtime. That’s because:

  • If you plan to distribute directly, the hardened runtime is enforced by the notary service.

  • If you plan to distribute via the Mac App Store, that doesn’t require the hardened runtime but it does support it. So, given that the hardened runtime offers useful security benefits, you might as well enable it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I started over by creating a minimal Electron app to better narrow down the issue. It seems Hardened Runtime is not the root cause. As long as the 'com.apple.security.app-sandbox' entitlement is enabled, my app cannot communicate with the Photos library, regardless of whether Hardened Runtime is on or off. However, it starts working again once I disable the sandbox.

In my use case, the main process loads a dylib (Node.js native addon) during runtime, and I believe it uses dlopen to load the library. I can confirm that the library is successfully loaded, as that's where the PHAuthorizationStatus: Authorized log comes from, but for some reason, it can't communicate with photolibraryd and print the XPC errors instead.

I am wondering if there is anything else I can check to help reveal the root cause. Thanks for your help @DTS Engineer !

Hi Quinn, even though I still don't understand the root cause, I was able to tell that the problem seems to be related to the Electron framework itself. Would you mind update the title of this post by adding "Electron" to it?

For anyone who are building Mac App Store apps with Electron and met similar XPC errors, it seems the issue has been addressed since Electron v26.

Electron 25.9.8 with Node 18.15.0: Doesn't work

Electron 26.0.0 with Node 18.16.1: Works fine

Sandboxed Electron macOS app can't access Photos library
 
 
Q