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

Switch to PKCE auth #363

Open
adamcik opened this issue Aug 22, 2023 · 4 comments
Open

Switch to PKCE auth #363

adamcik opened this issue Aug 22, 2023 · 4 comments
Labels
A-webapi Area: Spotify Web API

Comments

@adamcik
Copy link
Member

adamcik commented Aug 22, 2023

https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow has details from the Spotify side.

Essentially we want something like the following:

In [118]: def test():
     ...:     authorize_uri = 'https://accounts.spotify.com/authorize'
     ...:     redirect_uri = 'https://auth.mopidy.com/spotify/callback'
     ...:     token_uri = 'https://accounts.spotify.com/api/token'
     ...: 
     ...:     client_id = 'f88ee52f92724d51b7579a1d1cdb3128'
     ...: 
     ...:     verifier = secrets.token_urlsafe(64)
     ...:     state = secrets.token_urlsafe()
     ...:     challenge = base64.urlsafe_b64encode(
     ...:         hashlib.sha256(verifier.encode()).digest()).decode().rstrip('=')
     ...: 
     ...:     prepared_request = requests.Request('GET', authorize_uri, params={
     ...:         'response_type': 'code',
     ...:         'client_id': client_id,
     ...:         'redirect_uri': redirect_uri,
     ...:         'code_challenge_method': 'S256',
     ...:         'code_challenge': challenge,
     ...:         'state': state,
     ...:     }).prepare()
     ...: 
     ...:     print(prepared_request.url)
     ...: 
     ...:     parsed_url = urllib.parse.urlparse(input())
     ...:     query = urllib.parse.parse_qs(parsed_url.query)
     ...: 
     ...:     assert state == query['state'][0]
     ...:     result = requests.post(token_uri, data={
     ...:         'grant_type':  'authorization_code',
     ...:         'code': query['code'][0].encode(),
     ...:         'redirect_uri': redirect_uri,
     ...:         'client_id': client_id,
     ...:         'code_verifier': verifier
     ...:     }).json()
     ...:     
     ...:     print(result)
     ...:     
     ...:     print(requests.post(token_uri, data={
     ...:         'grant_type': 'refresh_token',
     ...:         'refresh_token': result['refresh_token'],
     ...:         'client_id': client_id,
     ...:     }).json())
     ...:     

This would go in a new mopidy auth spotify command or something, which prints the page to go to, we then redirect to mopidy.com. From there we either copy the result back to the CLI which is waiting for input, or we have the auth command spin up a HTTP server that mopidy.com redirects back to via info hidden in the state we got back.

Alternatively, the command starts up a local HTTP server and sends you there first, that redirects to Spotify, which redirects to Mopidy.com, which redirects back to the local server.

The command then uses the verifier to get a refresh token that we need to store somewhere, similar to an auth blob. From there we are good to go and can just do what we've always done.

Since we already have a major change "breaking" things switching to librespot, we might as well switch to PKCE without keeping support for the oauthclientbridge setup.

I think I have a branch with some of this coded up from years back, I'll what I can find. But if someone else wants to make PKCE work have at it :-)

@adamcik adamcik added the A-webapi Area: Spotify Web API label Aug 22, 2023
@adamcik
Copy link
Member Author

adamcik commented Aug 22, 2023

I also double checked that the refresh token is indeed single use and we get a new one for each token refresh.

@adamcik
Copy link
Member Author

adamcik commented Aug 22, 2023

Found my old branch again main...adamcik:mopidy-spotify:pkce

@kingosticks
Copy link
Member

I'll integrate this after #397

@kingosticks
Copy link
Member

And we need to work out the best place to store our sensitive data. Relatedly, we currently store the spotifyaudiosrc auth blob the data directory, but that's not ideal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-webapi Area: Spotify Web API
Projects
None yet
Development

No branches or pull requests

2 participants