get-windows icon indicating copy to clipboard operation
get-windows copied to clipboard

Accessibility Access prompt shows every time I use `active-win`on Mac OS in electron app

Open braaar opened this issue 2 years ago • 17 comments

This is perhaps related to but not identical to https://github.com/sindresorhus/active-win/issues/88. I don't want to opt out of access dialogs altogether, I just want it to show up once and then recognise the access.

The problem

Every time activeWindow() (or activeWindow.sync()) is called, this prompt shows up, even though I have added the permissions in system preferences:

Screenshot 2022-03-01 at 9 21 53

What I'm doing

I am calling  active-win in a setInterval in an electron app built for MacOS with electron-builder.

Here's my app:

import { app } from "electron";
import activeWindow = require("active-win");

import { LOG_INTERVAL_MILISECONDS } from "./config";

app.on("ready", async () => {
  setInterval(async () => {
    const window = await activeWindow().catch((error) => console.log(error));
    console.log(window);
  }, LOG_INTERVAL_MILISECONDS);
});

This runs fine in the terminal in VSCode (which asked for the permission once, and from there on it was fine).

Attempts to figure it out

I've also tried using electron's desktopCapturer, like so:

console.log(
    await (
      await desktopCapturer.getSources({ types: ["window"] })
    ).map((window) => window.name)
  );

When I run this, the app asks for permission (for screen recording) once and when it has been granted in the system preferences the prompt does not show up again. So it seem to me that macOS is able to identify my app and give it permissions in that case, at least.

I've tried:

  • Fiddling around with various electron-builder build parameters to make sure all the fields such as bundle ID are set up to uniquely ID the app correctly (no effect)
  • Asking for accessibility permission up front using https://github.com/karaggeorge/macos-accessibility-permissions
  • setting up entitlements.mac.plist

I have not tried:

  • Setting up code signing for my app
  • forking active-win and modifying the swift-code to bypass the prompt (as seen in #67)

Additional info

Here's my build config:

  "build": {
    "extends": null,
    "appId": "com.mycompany.myapp",
    "productName": "MyApp v0.1.1",
    "files": [
      "build/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    },
    "mac": {
      "type": "distribution",
      "category": "public.app-category.productivity",
      "target": [
        {
          "target": "dmg"
        }
      ],
      "hardenedRuntime": true,
      "gatekeeperAssess": false,
      "entitlements": "public/entitlements.mac.plist",
      "entitlementsInherit": "public/entitlements.mac.plist",
      "extendInfo": {
        "NSAppleEventsUsageDescription": "Please allow access to script browser applications to detect the current URL when triggering instant lookup."
      }
    }
  }

entitlements.mac.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.automation.apple-events</key>
    <true/>
  </dict>
</plist>

braaar avatar Mar 01 '22 07:03 braaar

I'm having the same problem (different app and goal but exact same problem) however it only happens when the app is compiled, during dev mode that doesn't happen. i'm thinking it can be the privilege the app gets when it is opened from terminal, but i'd need to do a bit of research to figure out how to bypass it or fix it at least during build time.

JoseMoreville avatar Apr 25 '22 02:04 JoseMoreville

Same here 😑 In dev it works as described. After building it keeps asking for permission. Also tried option screenRecordingPermission: false without any change.

svenadlung avatar Apr 25 '22 11:04 svenadlung

Same here 😑 In dev it works as described. After building it keeps asking for permission. Also tried option screenRecordingPermission: false without any change.

I kinda solved the problem by modifying the main.swift file and compiling it.

It will prompt a different permission but it'll only appears once, i'm not really into swift so i can know how it exactly works but i'm going to do a bit more research so i can explain it better

/*
if !AXIsProcessTrustedWithOptions(["AXTrustedCheckOptionPrompt": true] as CFDictionary) {
	print("active-win requires the accessibility permission in “System Preferences › Security & Privacy › Privacy › Accessibility”.")
	exit(1)
}

// Show screen recording permission prompt if needed. Required to get the complete window title.
if !disableScreenRecordingPermission && !hasScreenRecordingPermission() {
	print("active-win requires the screen recording permission in “System Preferences › Security & Privacy › Privacy › Screen Recording”.")
	exit(1)
}*/

JoseMoreville avatar Apr 25 '22 18:04 JoseMoreville

@JoseMoreville Thx a lot. That helped for the moment 🙏

svenadlung avatar Apr 26 '22 14:04 svenadlung

@JoseMoreville Thx a lot. That helped for the moment 🙏

No problem, it looks like a fix for the moment, but surprisingly makes everything work.

JoseMoreville avatar Apr 26 '22 15:04 JoseMoreville

@JoseMoreville so you just uncommented those lines?

Link to your forked main.swift

Perhaps something is not right in those if statements.

if !AXIsProcessTrustedWithOptions(["AXTrustedCheckOptionPrompt": true] as CFDictionary) {
	print("active-win requires the accessibility permission in “System Preferences › Security & Privacy › Privacy › Accessibility”.")
	exit(1)
}

// Show screen recording permission prompt if needed. Required to get the complete window title.
if !disableScreenRecordingPermission && !hasScreenRecordingPermission() {
	print("active-win requires the screen recording permission in “System Preferences › Security & Privacy › Privacy › Screen Recording”.")
	exit(1)

I would assume what is going on in your case is that the operating system prompts the user with some kind of prompt, since it's a built in security feature that you can't bypass. And that built in prompts seems to work fine in terms of showing up only once?

braaar avatar Apr 27 '22 04:04 braaar

@braaar Indeed, it prompts a dialog to ask for permissions but as chrome (the version electron's using) once you accept it, you'll never see it again.

This is just one example of my implementation as a experimental feature for my app, i'll make the main to send the data instead of the renderer asking for it every 1000ms (performance reasons)

         //renderer
          if(shouldVideoBeMuted.value === false){
            setInterval(()=>{
              window.ipcRenderer.invoke('active', 'active').then((active) => {
                if(active !== undefined || active?.title !== undefined){
                isBackgroundActive.value = false;
                }else{
                  isBackgroundActive.value = true;
                }
        
                if(isBackgroundActive.value){
                  shouldVideoBeMuted.value = false;
                  document.getElementsByTagName('video')[0].volume = 0.33;
                }else{
                  shouldVideoBeMuted.value = true;
                }
              });
            }, 1000);
          }
// main
    ipcMain.handle('active', async (event, arg) => {
      return await activeWindow.sync();
    })

JoseMoreville avatar Apr 27 '22 17:04 JoseMoreville

Thanks a lot, @JoseMoreville, that helped a lot! Still, it would be nice if that "fix" could be added into a release... @sindresorhus - do you think you could include that and release it, please :) ?

I don't know this is nessasary for all macos versions though... I am currently using an M1 mac with Ventura installed, if that helps.

SindiaH avatar Nov 16 '22 09:11 SindiaH

Hello again,

I could not let go on that issue, since for me even though I could run the app with active-win, I was not able to get the window titles. So, after quite some time I found out, that for whatever reason, the real problem is, that it doesent work to simply add the app to the "Privacy & Security" permissions as you usually would, by just adding your ***.app into the list. Instead you need to add the executable file WITHIN the .app, found in the folder ".app/Contents/MacOS/" . You cannot simply add it via the usual menu, I helped myself by navigating into the folder manually and saving the resulting folder in my favourites, so that I was able to access it from the Permissions-Menu. No idea if there is a better way, but it worked for me.

Btw, by using this package: https://github.com/karaggeorge/mac-screen-capture-permissions the correct executable will be added to the permissions for "Screen Recording". I haven't found anything like that for the Accessability though, but that package helped me realize that that's the problem all along :D

Once the described executable is added into the "Privacy & Security" permissions "Accessability" and "Screen Recording", "active-win" works just fine.

SindiaH avatar Nov 17 '22 16:11 SindiaH

Instead you need to add the executable file WITHIN the .app, found in the folder ".app/Contents/MacOS/" .

Interesting. Could this indicate that there is some kind of problem with the configuration of my electron app such that MacOS isn't told exactly where to find the executable file when asking for permissions?

Could the solution be some kind of configuration in entitlements.mac.plist?

This doesn't exactly explain why the problem goes away with @JoseMoreville's fix, though.

braaar avatar Nov 18 '22 11:11 braaar

Josemorevilles fix removes the check that makes sure, that you do have the permissions needed to read the title. You only need the Screen Recording permissions for recording the title of the window, which indeed doesent work without setting the permissions as I described- at least not for me, did it still work for you? As for the Accessability permissions - it seems to me like they are not needed at all, so that check is kinda unnessasary, since everything else works without any of those two permissions active.

I expected that there might be some plist setting or configuration error aswell... but I wasnt able to find anything helpful on that regard yet :/

SindiaH avatar Nov 19 '22 04:11 SindiaH

Have same issue, fight was long term... I just created basic script for darwin without native part, if some one need:

import { spawn } from 'child_process'

export default () => {
  return new Promise((resolve, reject) => {
    const stream = spawn('osascript', [
      '-e',
      `global res, resName, resTitle, resPid
      set resTitle to ""
      tell application "System Events"
        set res to first process whose frontmost is true
        set resName to short name of res
        set resPid to unix id of res
        tell process resName
          tell (1st window whose value of attribute "AXMain" is true)
              if exists value of attribute "AXTitle" then
                  set resTitle to value of attribute "AXTitle"
              end if
          end tell
        end tell
      end tell
      return resName & "|:sep:|" & resTitle & "|:sep:|" & resPid
      `,
    ])
    stream.stdout.setEncoding('utf8')
    stream.stdout.on('data', (stdout) => {
      const darwinPayload = stdout.toString().split('|:sep:|')
      resolve({
        platform: 'darwin',
        app: darwinPayload[0].trim(),
        title: darwinPayload[1].trim(),
        pid: darwinPayload[2].trim(),
      })
    })
    stream.stderr.on('data', (stderr) => {
      reject(new Error(stderr.toString()))
    })
    stream.stdin.end()
  })
}

Also permission UX with this solution looks better

Screenshot 2022-12-04 at 22 57 38

itrcz avatar Dec 04 '22 19:12 itrcz

None of these solutions appear to have worked for me. Has anyone had any luck resolving this? @itrcz @braaar @svenadlung @Akiriki @JoseMoreville

brandon-kyle-bailey avatar Jan 25 '23 19:01 brandon-kyle-bailey

I am getting same error in electron js

raghavnaphade avatar May 05 '23 09:05 raghavnaphade

Same here 😑 In dev it works as described. After building it keeps asking for permission. Also tried option screenRecordingPermission: false without any change.

I kinda solved the problem by modifying the main.swift file and compiling it.

It will prompt a different permission but it'll only appears once, i'm not really into swift so i can know how it exactly works but i'm going to do a bit more research so i can explain it better

/*
if !AXIsProcessTrustedWithOptions(["AXTrustedCheckOptionPrompt": true] as CFDictionary) {
	print("active-win requires the accessibility permission in “System Preferences › Security & Privacy › Privacy › Accessibility”.")
	exit(1)
}

// Show screen recording permission prompt if needed. Required to get the complete window title.
if !disableScreenRecordingPermission && !hasScreenRecordingPermission() {
	print("active-win requires the screen recording permission in “System Preferences › Security & Privacy › Privacy › Screen Recording”.")
	exit(1)
}*/

How you have done this? Can you please guide me

raghavnaphade avatar May 05 '23 11:05 raghavnaphade

I have a Mac M1 max with ventura 13.3.1, and i get this error when i Try to compile init(dispatchQueueDisplay:outputWidth:outputHeight:pixelFormat:properties:queue:handler:)' is only available in macOS 13.0 or newer Build cancelled, how can i solve this?

16nsolorzano avatar May 06 '23 00:05 16nsolorzano