1

I'm trying to register a custom protocol with electron. I want it to be a redirect location that a website can use to provide an api key (like myprotocol://example/payload=api-key). I have been using electron's registerHttpProtocol and also tried electron's interceptHttpProtocol.

But, when the website tries to redirect to my protocol my electron app doesn't do anything. The website goes to myprotocol://example/payload=api-key, and registers a "page doesn't exist error"--while nothing happens in my app.

This is in a development environment. I've seen some discussion about custom protocols that assume a production environment.

Can you register a custom protocol with electron in development?

Why am I not able to intercept the website's going to the protocol I've set out?

Here's my code:

main.js:

app.whenReady().then(() => {
  protocol.registerHttpProtocol('examplep', (request, callback) => {
     console.log("examplep", request);
     callback('it-worked');
  }, (error) => {
        if (error) console.error('Failed to register protocol = ' + error)
  })

  protocol.interceptHttpProtocol("examplep", function (request, callback) {  //I've tried both registerHttp... and interceptHttp... methods, so including both here; though I think in practice only one should be required
    console.log('intercepted!' + request)
    callback(request);
  });
})

redirect url provided to website:

'http://examplep'

And I've whitelisted this url on the website itself.

I've also tried related methods registerStringProtocol, interceptStringProtocol, registerFileProtocol, and interceptFileProtocol, without success.


What am I missing?

SeanRtS
  • 1,005
  • 1
  • 13
  • 31

1 Answers1

3

Sounds like you need to support deep linking fora desktop app, which is done via a Custom URI Scheme and is registered with setAsDefaultProtocolClient.

When your Electron app starts up write this code to register the scheme, on the main side of your app:

const customScheme = 'x-mycompany-myapp';
app.setAsDefaultProtocolClient(customScheme);

The custom scheme can be tested from the command line like this, depending whether you are running macOS or Windows:

open  x-mycompany-myapp:/some/location
start x-mycompany-myapp:/some/location

A web client will just invoke a URL as in this Javascript code of mine;

The notification will be received within the main side of your app and on Windows will attempt to create a new instance of the app, in which case you need to detect this condition, process the notification then cancel the new app instance.

On MacOS it will be received within the open-url event, so you register it like this:

app.on('open-url', this._onOpenUrl);

Once the main side of the Electron app has the notification, it needs to get the URL information and forward it to the renderer process. You can use ipcMain events for this.

Finally the code for receiving the notification in running instances and starting the app from a deep link are different.

EXAMPLE APP

Since the code is a little tricky, here is some example code that may be useful, to give you something to compare against. If it helps you can also run the app by following the instructions in the blog post:

My use case is around receiving OAuth responses after signing in from the system browser. Hopefully you can borrow some ideas from it related to deep linking though.

INFO.PLIST

My understand is that in a development environment (on macOS) deep links work when the app is running, but if you stop the app and attempt a deep link it will not start the app.

You can only resolve this for a packaged app, which requires an info.plist. In my code sample the info.plist is generated from build protocol entries in the package.json file.

My code sample is packaged in a basic way by the Electron Packager, so when I run npm run pack, the app is built to a dist folder. I can then run the packaged version of the app and it gets registered with the system - as can be seen in the Default Apps tool. See screenshots in the blog post.

SECRETS

Secrets for a desktop app should be stored using operating system secure storage. There are screenshots of credential storage in the blog post.

On Electron, have a look at the keytar component - and this wrapper class of mine. I am storing tokens (strings) so you should be able to adapt the code for your API keys.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • Thank you for this answer. I'll check out the links you provided and see if I can get things working. I do see that the links you provided are to tutorials with a lot in them--they are not targeted directly to the code I need (the code I need might be in there, but there's a lot more there too). Would you be able to just put the code snippets that are relevant directly in your answer here? That would be most helpful to me and others, and if your reply just links to different tutorials, I don't think I'll be able to mark your reply as the accepted answer. Either way, thanks for your reply. – SeanRtS Aug 19 '21 at 13:56
  • 1
    Yep - you're right - I have listed the key points above to get you started. Hopefully this helps get you on track by getting a working custom scheme in place and receiving some events back to the app. – Gary Archer Aug 19 '21 at 16:06
  • Thanks very much. This is helpful. I will take a look. – SeanRtS Aug 19 '21 at 16:29
  • I've got it to work using your code. Thanks! Helped a lot. One thing surprised me: this works smoothly in development (I'm on mac): using the setAsDefaultProtocolClient('myProtocol') method, and then the app.on('open-url'... code, and my app registers anytime browser goes to myProtocol:/. The docs say I would have to enter the protocol in info.plist, and other stack overflow guidance made it sound like I would have to deal with packaging the app first. But I haven't done that stuff. Does it make sense that this works with just those few lines of code, without touching info.plist or packaging? – SeanRtS Aug 20 '21 at 01:38
  • To round things out: the code above helps to get the payload from the site on redirect--providing an api key for the user to access the site. How do you like to store the key so the app can access it later? Local storage, or something more complex? – SeanRtS Aug 20 '21 at 01:47
  • 1
    I added some stuff on info.plist and secrets to my previous answer. – Gary Archer Aug 20 '21 at 07:18
  • Hi Gary. I've gotten a lot of value from your answer and your posts. Thanks again. You mention packaging the app. One thing I don't see in your examples is the Mac OS codesigning details. Do you have an example of that? My main issue right now is how to pass along the appleID and app-specific-password in a way that is secure (ie, passed through environmental variables or the like). In my case, I'm using electron forge. – SeanRtS Sep 03 '21 at 13:56
  • Sorry - mine is just a demo app so I have not done macOS code signing - good luck though ... – Gary Archer Sep 03 '21 at 13:58
  • Thanks. Any experience passing environmental variables to package.json by chance? That's the key (final?) piece for me. – SeanRtS Sep 03 '21 at 14:06
  • Have a look at the [jo tool](https://github.com/jpmens/jo) which s handy for dynamically updating JSON files. – Gary Archer Sep 03 '21 at 14:18