Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessibility Access prompt shows every time I use active-winon Mac OS in electron app #135

Open
braaar opened this issue Mar 1, 2022 · 17 comments

Comments

@braaar
Copy link
Contributor

braaar commented Mar 1, 2022

This is perhaps related to but not identical to #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:

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>
@JoseMoreville
Copy link

JoseMoreville commented Apr 25, 2022

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.

@svenadlung
Copy link

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

@JoseMoreville
Copy link

JoseMoreville commented Apr 25, 2022

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)
}*/

@svenadlung
Copy link

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

@JoseMoreville
Copy link

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

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

@braaar
Copy link
Contributor Author

braaar commented Apr 27, 2022

@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?

@JoseMoreville
Copy link

JoseMoreville commented Apr 27, 2022

@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();
    })

@SindiaH
Copy link

SindiaH commented Nov 16, 2022

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
Copy link

SindiaH commented Nov 17, 2022

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.

@braaar
Copy link
Contributor Author

braaar commented Nov 18, 2022

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.

@SindiaH
Copy link

SindiaH commented Nov 19, 2022

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

@itrcz
Copy link

itrcz commented Dec 4, 2022

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

@brandon-kyle-bailey
Copy link

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

@raghavnaphade
Copy link

I am getting same error in electron js

@raghavnaphade
Copy link

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

@16nsolorzano
Copy link

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?

@codebycarlos
Copy link

codebycarlos commented Feb 11, 2024

https://github.com/sindresorhus/active-win/blob/97aca0ae90dbc815c34c224e6b09fa0cb75babbe/Sources/ActiveWinCLI/main.swift#L89-L99

It would appear based on the comments, that the accessibility permissions are only needed when requesting the window title so the if statement should be updated.

e.g.: codebycarlos@051df9c

Happy to put a PR through @sindresorhus, if you agree that's the right approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants