Open patrick-motard opened 4 years ago
I agree. I still need to finish up documentation for some endpoints and especially authorization workflows. I've been a bit burnt out as of late, but it's definitely something I want to get done.
@adamgrieger I completely understand. You've done so much great work here. It's important to take breaks! I would be more than happy to add examples. I'll admit I haven't taken the time to read through the code and see if there are methods to an auth url similar to the createAuthorizeURL
method in spotify-web-api-node. I will get a PR cooked up here in this next week or so for this. Thank you for your prompt reply!
That'd be great! All of the authorization stuff is located here if you're curious.
Still trying to get authentication working. I swapped out the spotify-web-api-node client with this one, and replaced all the different function calls that were different between the two. I can interact with spotify just fine when I don't change the scopes. It doesn't ask me to authorize the client. I think that's because it's re-using an existing auth/refresh token. However, when I add an additional scope that I haven't authorized yet, it loads the page that asks for my permission to grant the app access to spotify with the additional scope. I click accept, which results in a webpage that says "Error
Oops! Something went wrong, please try again or check out our help area." which i believe corresponds to the GET https://accounts.spotify.com/authorize/accept 405
I'm seeing in the console.
Out of curiousity, i tried navigating to the authorization url in my browser, which brings up the same permissions prompt. When I click on the accept button it tries to redirect me to my app, which fails since the app isn't running. However... when i open the app, with the same additional scope that I just approved through the browser, it authenticates right away and works with the new scope. No prompt for granting permissions to the app. Just goes straight to the app.
I don't want to run out of scopes to test this bug with, so I'm not going to try the browser steps again. I'm really baffled by this. It's not redirecting to my app when i click accept in my electron app. Worked perfectly fine when i used spotify-web-api-node, so i must be missing something. I've just been smashing my head against this problem on and off for weeks and am really starting to lose steam. I don't know what to try next.
I think the way that I implemented authorization isn't 100% identical to spotify-web-api-node
. I would have to look at it again since it's been a while (assuming Spotify didn't change their authorization API). I can try to get some docs written for the authorization techniques that I've implemented so far, because I know that's probably the most confusing part of the library. Out of curiosity, which authorization workflow are you using?
I believe Authorization Code flow. Hopefully that answers your question? I'm using GetRefreshableAuthorizationUrl
https://github.com/adamgrieger/spotify-web-api-ts/blob/aaa78bc5340f4094c806846d04b36e9cf8322fab/src/index.ts#L108.
I construct that url, and render it in electron. That loads the page from spotify that asks for granting permission to the app. When I click accept, I expect it to redirect to the redirect uri in the auth url. The redirect uri is a localhost express app running in my electron app that renders my electron app when it's hit. However, the redirect never happens. The 'will-navigate' event is fired instead of 'will-redirect', and the page that shows after 'accept' is clicked is a spotify error page, with the 405 error mentioned above.
Another interesting difference is that in spotify-web-api-node
it would first have me log in to spotify. This library never has me log in to spotify. Somehow it just knows it's my account that's being accessed. Maybe spotify-web-api-node
uses Implicit Grant and/or Client Credentials flows? I really need to read up on these to understand the difference.
Interesting. I'll try to take a look this weekend. I can prioritize writing an example for the Authorization Code flow.
I just tried a minimal example locally using the Authorization Code workflow and it works for me still. I can add a fleshed-out examples
directory and update the README.md
. I'm using express
too but not within an Electron app. I haven't used Electron so I can't comment on whether there might be an issue there. I can leave some example server code here in the meantime. On the client-side, I used localStorage
to store the access_token
in the redirect URI and then passed it into a SpotifyWebApi
instance after redirecting.
import express from "express";
import { SpotifyWebApi } from "spotify-web-api-ts";
const app = express();
const port = 3000;
const spotify = new SpotifyWebApi({
clientId: "<CLIENT_ID>",
clientSecret: "<CLIENT_SECRET>",
redirectUri: "http://localhost:3000/callback.html",
});
app.use(express.static("public"));
app.get("/tempAuthUrl", (req, res) =>
res.send(
spotify.getTemporaryAuthorizationUrl({ scope: ["user-library-read"] })
)
);
app.listen(port, () =>
console.log(`Example app listening at http://localhost:${port}`)
);
@adamgrieger are you able to able to test this with a scope that you haven't already granted previously? This auth flow works fine for me if i use scopes I have already approved.
With either scopes you have already approved, or havent approved, do you get a webpage that presents you with spotify permissions you have to approve? If so, what happens when you click accept?
I'm also not able to use http://localhost:8888/callback.html
as a redirect url, i get the error: INVALID_CLIENT: Invalid redirect URI
. http://localhost:8888/callback
works fine though (with scopes i have already authorized)
Also, getTemporaryAuthorizationUrl does not work for me at all. Only getRefreshableAuthorizationUrl works for me.
The answer to my woes, I believe, is in the status code. GET https://accounts.spotify.com/authorize/accept 405
is the issue. When clicking the accept button in the browser, the browser sends a POST to https://accounts.spotify.com/authorize/accept. No 405. 405 is the wrong method status code. Makes sense. For some reason my electron app is sending a get request to Spotify instead of a post. I'm looking into why this is happening.
Alright. I've solved it. Since this took me so damn long I want to leave a trail of crumbs for any other poor sap like me that faces this issue. It was indeed because I was sending a GET request when it should have been a POST. As to why, it was completely by accident. In electron, you can intercept browser events server side. I was intercepting requests to localhost:8888/callback
. When my app is redirected to that url, I want two things to happen:
I was doing this both in the will-redirect
and will-navigate
event handlers. Here is what the will-navigate
handler looked like, that was causing trouble:
win.webContents.on('will-navigate', (event, url) => {
if (!url.includes('http://localhost')) {
win.loadURL(url);
}
});
Turns out that this if condition was true for the url https://accounts.spotify.com/authorize/accept
. The thinking when I wrote this was "anything that isn't localhost should be handled via win.loadURL
. If the browser wants to make a request, make a request. But for localhost, do nothing, so that the re-direct event is triggered". I'm not actually sure if the 'will-navigate' event is triggered prior to a 'will-redirect' event, but I do know (now), that loadURL
defaults to sending a GET
request, unless a post payload is passed in as an additional parameter.
A simple conditional solved the issue.
win.webContents.on('will-navigate', (event, url) => {
if (url.includes('https://accounts.spotify.com/authorize/accept')) {
return;
}
if (!url.includes('http://localhost')) {
win.loadURL(url);
}
});
With this, I am exiting the handler for the url that is meant to be sent as a POST so that Electron (or the renderer?) doesn't get it's verb overwritten by my dumbass self. In the near future I will address whether or not the localhost conditional is needed at all. For now I just want to enjoy that this finally works again. And also kick myself.
I really appreciate your help @adamgrieger , especially considering your code wasn't the issue.
The README's example says:
It would be useful to know how to generate that access token.