Refinement of PKCE (#566)

* Improve SpotifyPKCE.get_authorization_url

Ensure that a code challenge has been generated for the auth url

* Clean up namespace in SpotifyPKCE._get_auth_response_interactive

* Duplicate parse_auth_response_url into SpotifyPKCE

* Update SpotifyPKCE security advisory

* Update changelog for PKCE refinements
This commit is contained in:
IdmFoundInHim 2020-08-30 06:01:05 -04:00 committed by GitHub
parent c927f02c1c
commit df99eab61c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 14 deletions

View File

@ -7,7 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
// Add your changes here and then delete this line ### Added
- `SpotifyPKCE.parse_auth_response_url`, mirroring that method in
`SpotifyOAuth`
### Fixed
- Using `SpotifyPKCE.get_authorization_url` will now generate a code
challenge if needed
## [2.14.0] - 2020-08-29 ## [2.14.0] - 2020-08-29

View File

@ -650,6 +650,8 @@ class SpotifyPKCE(SpotifyAuthBase):
def get_authorize_url(self, state=None): def get_authorize_url(self, state=None):
""" Gets the URL to use to authorize this app """ """ Gets the URL to use to authorize this app """
if not self.code_challenge:
self.get_pkce_handshake_parameters()
payload = { payload = {
"client_id": self.client_id, "client_id": self.client_id,
"response_type": "code", "response_type": "code",
@ -726,8 +728,8 @@ class SpotifyPKCE(SpotifyAuthBase):
"Go to the following URL: {}\n" "Go to the following URL: {}\n"
"Enter the URL you were redirected to: ".format(url) "Enter the URL you were redirected to: ".format(url)
) )
response = SpotifyOAuth._get_user_input(prompt) response = self._get_user_input(prompt)
state, code = SpotifyOAuth.parse_auth_response_url(response) state, code = self.parse_auth_response_url(response)
if self.state is not None and self.state != state: if self.state is not None and self.state != state:
raise SpotifyStateError(self.state, state) raise SpotifyStateError(self.state, state)
return code return code
@ -904,12 +906,16 @@ class SpotifyPKCE(SpotifyAuthBase):
Parameters: Parameters:
- url - the response url - url - the response url
""" """
_, code = SpotifyOAuth.parse_auth_response_url(url) _, code = self.parse_auth_response_url(url)
if code is None: if code is None:
return url return url
else: else:
return code return code
@staticmethod
def parse_auth_response_url(url):
return SpotifyOAuth.parse_auth_response_url(url)
class SpotifyImplicitGrant(SpotifyAuthBase): class SpotifyImplicitGrant(SpotifyAuthBase):
""" Implements Implicit Grant Flow for client apps """ Implements Implicit Grant Flow for client apps
@ -920,16 +926,29 @@ class SpotifyImplicitGrant(SpotifyAuthBase):
Security Advisory Security Advisory
----------------- -----------------
The Implicit Grant Flow is part of the The OAuth standard no longer recommends the Implicit Grant Flow for
[OAuth 2.0 standard](https://oauth.net/2/grant-types/implicit/). client-side code. Spotify has implemented the OAuth-suggested PKCE
It is intended for client-side (running in browser or a native app) extension that removes the need for a client secret in the
interactions where the client secret would have to be hard-coded and Authentication Code flow. Use the SpotifyPKCE auth manager instead
exposed. OAuth no longer recommends its use because sensitive of SpotifyImplicitGrant.
info (the auth token) can be yanked from the browser address bar or
history, instead recommending the Auth Code flow with PKCE. However, SpotifyPKCE contains all of the functionality of
Spotify [does not support PKCE](https://community.spotify.com/t5/Spotify-for-Developers/Authentication-API-failing-in-production-right-now/m-p/4960693/highlight/true#M234), <!---# noqa: E501--> SpotifyImplicitGrant, plus automatic response retrieval and
so Implicit Grant is the only viable options for client-side Spotify refreshable tokens. Only a few replacements need to be made:
API requests.
* get_auth_response()['access_token'] ->
get_access_token(get_authorization_code())
* get_auth_response() ->
get_access_token(get_authorization_code()); get_cached_token()
* parse_response_token(url)['access_token'] ->
get_access_token(parse_response_code(url))
* parse_response_token(url) ->
get_access_token(parse_response_code(url)); get_cached_token()
The security concern in the Implict Grant flow is that the token is
returned in the URL and can be intercepted through the browser. A
request with an authorization code and proof of origin could not be
easily intercepted without a compromised network.
""" """
OAUTH_AUTHORIZE_URL = "https://accounts.spotify.com/authorize" OAUTH_AUTHORIZE_URL = "https://accounts.spotify.com/authorize"