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

Create credentials from access token #1098

Closed

Conversation

kingosticks
Copy link
Contributor

New API method and example relating to #1086

@roderickvd roderickvd added enhancement SpotifyAPI Interop b/w librespot and Spotify labels Jan 14, 2023
@roderickvd
Copy link
Member

Nice addition. If you could resolve that conflict?

@GiviMAD
Copy link

GiviMAD commented Jan 14, 2023

The commercial sdk seems to allow this also: https://developer.spotify.com/documentation/commercial-hardware/implementation/reference/latest/#spconnectionloginoauthtoken

@kingosticks
Copy link
Contributor Author

Will sort tonight. I can change with_password return type also, I had actually copy and pasted the signature from there to start with!

@kingosticks
Copy link
Contributor Author

The commercial sdk seems to allow this also: https://developer.spotify.com/documentation/commercial-hardware/implementation/reference/latest/#spconnectionloginoauthtoken

Cool, thanks.

I am currently setting the username and librespot always uses it when authenticating but I was wondering if that's actually necessary for this login method. This example suggests it isn't.

@kingosticks
Copy link
Contributor Author

It seems you can remove the username from the auth packet when using AUTHENTICATION_SPOTIFY_TOKEN and change the method to just with_access_token(token: string), which would be nice. And that means Credentials.username should really be an Option<String>...

But then I had some issues when trying to actually play tracks with my token based login. I am sure that was working fine yesterday so I must be doing something silly, it is late.

@gdesmott
Copy link
Contributor

Agreed, it would be best to not rely on the username name here (assuming we can't easily extract it from the token?). That would save applications to ask for this username which is redudant when doing the proper OAuth flow.

@kingosticks
Copy link
Contributor Author

I thought ouath access tokens were supposed to be opaque. I don't think we've any idea how to extract anything from them.

I just want to double check there's no difference between setting username and not setting username during authentication. I think my test last night must have been wonky. I was trying lots of things, including using a different client ID; although, I'm not sure why you'd want to given the default keymaster client IDs seem to work just fine.

@gdesmott
Copy link
Contributor

although, I'm not sure why you'd want to given the default keymaster client IDs seem to work just fine.

Isn't each application supposed to use its own client id to respect the Spotify Developer Terms ?

@GiviMAD
Copy link

GiviMAD commented Jan 15, 2023

I thought ouath access tokens were supposed to be opaque. I don't think we've any idea how to extract anything from them.

@kingosticks, just as a comment, you can extract the username from the token by relying on the Spotify API, but you'll need token to have the user-read-email+user-read-private scopes for that, so maybe not a good idea to add that part.

But then I had some issues when trying to actually play tracks with my token based login. I am sure that was working fine yesterday so I must be doing something silly, it is late.

In case you are using the Web API, maybe you are facing this issue #1099 after rebasing the code to the dev branch

@kingosticks
Copy link
Contributor Author

I assumed he meant extracting it from the token itself. Using the token to call the API and find the account info doesn't sound interesting.

Yes, it could be related to #1099. Hmm.

@GiviMAD
Copy link

GiviMAD commented Jan 15, 2023

Isn't each application supposed to use its own client id to respect the Spotify Developer Terms ?

About this, because I was already thinking on it, I think it will be nice to confirm if the version 0.4.2 witch relays always on the keymaster client ID can work after switching that by a personal application client id. As those client ids seems to be granted with all the scopes maybe it work, and I think it will be more respectful with their developer terms and probably more secure as they can rotate their client id in the future. I'll give it a try some day but in case someone has already tried it and can share his experience that will be nice.

Just to clarified it, I'm not proposing removing the current functionality of relaying on the official app client_id but to add an option to operate with a personal client_id (of course after verifying it works). WDYT?

@kingosticks
Copy link
Contributor Author

kingosticks commented Jan 16, 2023

OK, so the problem with playback when using access token authentication in the dev branch is real. Here's the log showing what went wrong:

[2023-01-15T23:56:38Z DEBUG librespot_playback::player] command=Load(SpotifyId("spotify:track:0OLBUtDuXJQq8N9T38r6W1"), true, 0)
[2023-01-15T23:56:38Z DEBUG librespot_playback::player] command=AddEventSender
[2023-01-15T23:56:38Z DEBUG librespot::component] new SpClient
[2023-01-15T23:56:38Z INFO  librespot_core::spclient] Resolved "gew1-spclient.spotify.com:443" as spclient access point
[2023-01-15T23:56:38Z DEBUG librespot::component] new TokenProvider
[2023-01-15T23:56:38Z TRACE librespot_core::token] Requested token in scopes "playlist-read" unavailable or expired, requesting new token.
[2023-01-15T23:56:38Z ERROR librespot_core::mercury] error 403 for uri hm://keymaster/token/authenticated?scope=playlist-read&client_id=65b708073fc0480ea92a077233ca87bd&device_id=5c28e50f-01cf-46cc-a435-3992a039da16
[2023-01-15T23:56:38Z DEBUG librespot_core::session] could not dispatch command: Service unavailable { error handling Mercury response: MercuryResponse { uri: "hm://keymaster/token/authenticated?scope=playlist-read&client_id=65b708073fc0480ea92a077233ca87bd&device_id=5c28e50f-01cf-46cc-a435-3992a039da16", status_code: 403, payload: [[123, 34, 99, 111, 100, 101, 34, 58, 52, 44, 34, 101, 114, 114, 111, 114, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 58, 34, 73, 110, 118, 97, 108, 105, 100, 32, 114, 101, 113, 117, 101, 115, 116, 34, 125]] } }
[2023-01-15T23:56:38Z ERROR librespot_playback::player] Unable to load audio item: Error { kind: Unavailable, error: Response(MercuryResponse { uri: "hm://keymaster/token/authenticated?scope=playlist-read&client_id=65b708073fc0480ea92a077233ca87bd&device_id=5c28e50f-01cf-46cc-a435-3992a039da16", status_code: 403, payload: [[123, 34, 99, 111, 100, 101, 34, 58, 52, 44, 34, 101, 114, 114, 111, 114, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 58, 34, 73, 110, 118, 97, 108, 105, 100, 32, 114, 101, 113, 117, 101, 115, 116, 34, 125]] }) }
[2023-01-15T23:56:38Z ERROR librespot_playback::player] Skipping to next track, unable to load track <SpotifyId("spotify:track:0OLBUtDuXJQq8N9T38r6W1")>: ()

Compared with the regular method using user/pass authentication:

[2023-01-15T23:58:19Z DEBUG librespot_playback::player] command=Load(SpotifyId("spotify:track:0OLBUtDuXJQq8N9T38r6W1"), true, 0)
[2023-01-15T23:58:19Z DEBUG librespot_playback::player] command=AddEventSender
[2023-01-15T23:58:19Z DEBUG librespot::component] new SpClient
[2023-01-15T23:58:19Z INFO  librespot_core::spclient] Resolved "gew1-spclient.spotify.com:443" as spclient access point
[2023-01-15T23:58:19Z DEBUG librespot::component] new TokenProvider
[2023-01-15T23:58:19Z TRACE librespot_core::token] Requested token in scopes "playlist-read" unavailable or expired, requesting new token.
[2023-01-15T23:58:19Z TRACE librespot_core::token] Got token: Token {
        access_token: "XXXXX",
        expires_in: 3600s,
        token_type: "Bearer",
        scopes: [
            "playlist-read",
        ],
        timestamp: Instant {
            tv_sec: 3935,
            tv_nsec: 685210861,
        },
    }
[2023-01-15T23:58:19Z DEBUG librespot_core::spclient] Client token unavailable or expired, requesting new token.
[2023-01-15T23:58:19Z DEBUG librespot_core::http_client] Requesting https://clienttoken.spotify.com/v1/clienttoken
[2023-01-15T23:58:19Z TRACE librespot_core::spclient] Got client token: token: "AADaTnaWqjHsoejONXbmqZO3ScJoLJw1AV0KcNVdfIYQcambjTHhVPmsyyyWEJB1YEpd12H6UxNCgEsU3n/pyCKUWkBTd/v37CUwNcfVrDkaLfMMSVEpAaKui34oycX+rcaJhjKE/jl5V5yo6bMMMx4somCNLJcTZB+BhFSl8BjTyHIAP534yuvhX3uh6OAWDJEIKvkShogsKaO3xoM9kLKH4QoN0lZ4XJSTqdshqLWstzW5m+1K1w2DsVMbsZHusAwGF2ZKTBovwWtWKt4EwJyPX3Fs8bnO3I1vgA==" expires_after_seconds: 1216800 refresh_after_seconds: 1209600 domains {domain: "spotify.com"}
[2023-01-15T23:58:19Z DEBUG librespot_core::http_client] Requesting https://gew1-spclient.spotify.com:443/metadata/4/track/1ae7887f5a644395a8ab88b09e6e96ad?product=0&country=GB&salt=1702194571

I guess this makes sense from an OAuth point of view: if you've provided a token with certain permissions (scopes), it shouldn't be possible for it to request another token with extra permissions set. So the session you get with token-based auth isn't exactly the same as the session you get with user/pass-based auth. I don't think we realised this and I'm still trying to see if this is reflected anywhere in the login response.

I tried again with another token which had "playlist-read" granted in an effort to stop it requesting the new token. But that failed because TokenProvider doesn't know about the pre-existing token. Maybe that's easily fixable.

@GiviMAD
Copy link

GiviMAD commented Jan 16, 2023

@kingosticks when I used Librespot 0.4.2 authenticated with the mentioned spotify-connect tool using the default-token method, which just sends an access token with just the streaming scope but inside the blob (which should made no difference at the end), I don't think I have any error at all, so I'm not sure that your analysis is correct.

@kingosticks
Copy link
Contributor Author

kingosticks commented Jan 16, 2023

I was specifically saying that token-based auth ("access or "default" is irrelevant to librespot, as you say) does not work in the dev branch.

0.4.2 and dev are very different. The initial login is the same but when it comes to playback, they work differently. 0.4.2 gets audio data the traditional way via Hermes/Mercury and there's no problem using OAuth login with that. The dev branch gets audio data from Spotify's CDN via HTTP, to do that it first needs to get an OAuth token and then use that to get another token ("clienttoken") for CDN access. There seems to be a problem getting that OAuth token if you originally logged in using an OAuth token.

However, I expect you can force the same problem on 0.4.2 if you login using an OAuth token and then try to request a new token. Which you would presumably eventually want to do, since each token has a limited lifetime.

@GiviMAD
Copy link

GiviMAD commented Jan 16, 2023

0.4.2 and dev are very different. The initial login is the same but when it comes to playback, they work differently. 0.4.2 gets audio data the traditional way from Hermes and there's no problem there. The dev branch tries to first get an access token and then uses that to get a clienttoken for to accessing audio data from Spotify's HTTP CDN. There is a problem getting that access token.

Oh, I didn't have that context and I wasn't able to understand why the previous version works perfect just with the streaming scope, but make sense, thank you so much for the explanation. Probably is also the origin of the mentioned issue.

@roderickvd
Copy link
Member

Following this -- let me know when you've got something ready to go.
In the meantime I will chime in with advice if I have any.

@kingosticks
Copy link
Contributor Author

I think I'm stuck. If we can't get a token when using this login method, how can we use the CDN? There must be a trick I'm missing. Any experience with 403 from keymaster ?

@roderickvd
Copy link
Member

The web player probably uses the playplay HTTP endpoint to get encryption keys, so might not use Mercury anymore at all.

@kingosticks
Copy link
Contributor Author

Yes. I guess if you're only using Mercury (old connect h/w sdk stuff??), it doesn't matter that you can't get a bearer token for the http api. But if you're using the http api (everything newer) and you need a bearer token, in theory you don't need Mercury at all; you've got login5 & clienttoken, the websocket, and spclient.

Assuming that's right, this stuff needs login5 to work to be useful in dev. Otherwise we can only login and not stream, which is pointless. It would be useful as is for 0.4, but of course we're done with that branch. So it makes more sense to help get login5 working. I remember there was a person doing it but I can't find their work anywhere.

In the process of trying to get this working I've spent a lot of time getting back up to speed with how things work. I've got some notes on what the current Windows app is doing, stuff many people already know but I was thinking I could write it up and update the wiki notes for those that don't.

Few things I don't understand:

  1. the app still opens a TCP socket to ap-gewX.spotify.com:4070, presumably for Hermes. What does it still need that for? How are people intercepting that alongside mitmproxy for http? I thought the old Hermes dumping method was broken.
  2. Why do they need a websocket and spclient? Wouldn't the websocket alone be enough? Just Spotify's technical debt?

@roderickvd
Copy link
Member

Assuming that's right, this stuff needs login5 to work to be useful in dev. Otherwise we can only login and not stream, which is pointless. It would be useful as is for 0.4, but of course we're done with that branch. So it makes more sense to help get login5 working. I remember there was a person doing it but I can't find their work anywhere.

I think someone on Gitter was talking about it, but did not latch onto my proposal to make it a PR.

@gdesmott
Copy link
Contributor

@GiviMAD @kingosticks : hey! Did you make any progress on this by any chance? :)
I'm still blocked by the lack of proper oauth support as well.

@kingosticks
Copy link
Contributor Author

For dev branch (audio data from the CDN), no. It's blocked on our missing login5 implementation, at least. I'd like to work on resolving that but it's low on my list right now and I'm not currently working on it.

@gdesmott
Copy link
Contributor

What's login5 exactly and how is it needed here?

As I said here I managed to get streaming working so it's not clear to me what's missing exactly.

@kingosticks
Copy link
Contributor Author

Login5 is a newer version of Spotify's auth. It's what their official clients currently use.

Correct me if I am wrong, but you found it working in the 0.4x branch, not the dev branch. I've edited what I originally said at #1098 (comment) to hopefully be clearer.

@gdesmott
Copy link
Contributor

Correct me if I am wrong, but you found it working in the 0.4x branch, not the dev branch. I've edited what I originally said at #1098 (comment) to hopefully be clearer.

It's been a while but I'm pretty sure I was testing with dev as all my stuffs depend on it (for lyrics).

@kingosticks
Copy link
Contributor Author

I assumed you were using v0.4 since your comment referred to testing with the GStreamer element. Was that assumption wrong? It would be great if my unsuccessful experiments here using dev were incorrect. If you want to share your working patch I'll happily give it a try.

@gdesmott
Copy link
Contributor

I just re-tested and you're right indeed. I can re-use the token when using librespost 0.4 but it does not work with dev unfortunately. :(

@roderickvd
Copy link
Member

What do we want to do with this one? Do we believe we can get this working or shall we unfortunately close?

@kingosticks
Copy link
Contributor Author

Sadly have to close for how I think. And then try again when we have login5 support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement SpotifyAPI Interop b/w librespot and Spotify
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants