2

I have a React app in Electron, and I'm trying to access the spotify API using the spotify-web-api-node library. However, I'm not sure exactly how the oauth flow is meant to work inside of an Electron app... Firstly, for the redirect URL, I used this question and added a registerFileProtocol call to my file. Then I added a specific ipcMain.on handler for receiving the spotify login call from a page, which I've confirmed works with console logs. However, when I get to actually calling the authorizeURL, nothing happens?

This is part of my main.js:

app.whenReady().then(() => {
...
  protocol.registerFileProtocol(
    "oauthdesktop",
    (request, callback) => {
      console.log("oauthdesktop stuff: ", request, callback);
      //parse authorization code from request
    },
    (error) => {
      if (error) console.error("Failed to register protocol");
    }
  );
});

ipcMain.on("spotify-login", (e, arg) => {
  const credentials = {
    clientId: arg.spotifyClientId,
    clientSecret: arg.spotifySecret,
    redirectUri: "oauthdesktop://test",
  };

  const spotifyApi = new SpotifyWebApi(credentials);
  console.log("spapi: ", spotifyApi);

  const authorizeURL = spotifyApi.createAuthorizeURL(
    ["user-read-recently-played", "playlist-modify-private"],
    "waffles"
  );
  console.log("spurl: ", authorizeURL);

  axios.get(authorizeURL);
}

I'd expect the typical spotify login page popup to show up, but that doesn't happen. I'd also expect (possibly) the registerFileProtocol callback to log something, but it doesn't. What am I meant to be doing here? The authorization guide specifically mentions doing a GET request on the auth url, which is what I'm doing here...

IronWaffleMan
  • 2,513
  • 5
  • 30
  • 59

1 Answers1

3

In a desktop app it is recommended to open the system browser, and the Spotify login page will render there, as part of creating a promise. The opener library can be used to invoke the browser.

When the user has finished logging in, the technique is to receive the response via a Private URI Scheme / File Protocol, then to resolve the promise, get an authorization code, then swap it for tokens. It is tricky though.

RESOURCES OF MINE

I have some blog posts on this, which you may be able to borrow some ideas from, and a couple of code samples you can run on your PC:

The second of these is a React app and uses a Private URI scheme, so is fairly similar to yours. I use the AppAuth-JS library and not Spotify though.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • I couldn't find similar code in your final desktop sample... I'm having the issue that the handler I've defined in `registerFileProtocol` is apparently not getting called from the spotify API's callback. I put loggers in there (and tested they work in other parts of the app), but they don't log anything. What does happen when spotify calls the callback URL is that my app 're-opens', which is pretty useless. Do you know how to properly handle the response to `oauthdesktop://test` and to get the tokens from that? – IronWaffleMan Dec 13 '20 at 05:52
  • 1
    Look in my [main source file](https://github.com/gary-archer/authguidance.desktopsample.final/blob/master/src/main.ts) and see the calls to _onOpenUrl and _onSecondInstance, which is where login responses are received. You will need to store a promise globally in the app when redirecting, then resolve the promise once you've processed the login response in the above methods. – Gary Archer Dec 13 '20 at 10:10
  • Looks like the trick for me was not using `registerFileProtocol` but instead using `setAsDefaultProtocolClient`. I then copied your code requesting a single instance lock and put the handler for the callback in like this: `app.on("second-instance", handleCallback);` (and the second arg to the callback included the token that I needed). Regarding access tokens and refreshing them, do you have any advice for the best way to handle that in an electron app? Ideally I wouldn't have to manually get new tokens every time I start the electron app. – IronWaffleMan Dec 13 '20 at 20:40
  • 1
    Good to hear you've solved your main problem - have a look at the keytar component from the Final Desktop sample blog post above - and this [wrapper class](https://github.com/gary-archer/authguidance.desktopsample.final/blob/master/src/plumbing/oauth/tokenStorage.ts) – Gary Archer Dec 13 '20 at 20:49
  • I will, thanks! Just one last question... the spotify tokens that I get expire in an hour, which sucks if I try to use them when my app has been open for longer than that. Is there a recommended way of refreshing them, or should I simply refresh the tokens every time I make an api call? – IronWaffleMan Dec 15 '20 at 01:59
  • You should receive a refresh token that can be used to get new access tokens. You may need to include an offline_access scope when redirecting though ... – Gary Archer Dec 15 '20 at 09:03
  • @GaryArcher, does this work in development? I have been following the docs for registerHttpProtocol (and also tried registerFileProtocol and registerStringProtocol)--trying to intercept a redirect url with an api key. But I have not gotten it to work. Further detail here: https://stackoverflow.com/questions/68838012/does-electrons-registerhttpprotocol-work-in-development – SeanRtS Aug 19 '21 at 12:16
  • Yes it does - I answered your other question - hope it helps – Gary Archer Aug 19 '21 at 13:20