Skip to content

Switch to PKCE auth #363

@adamcik

Description

@adamcik

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 :-)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-webapiArea: Spotify Web API

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions