From df99eab61cd54183e0d6399a64590f8b889cd3cd Mon Sep 17 00:00:00 2001 From: IdmFoundInHim Date: Sun, 30 Aug 2020 06:01:05 -0400 Subject: [PATCH] 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 --- CHANGELOG.md | 10 +++++++++- spotipy/oauth2.py | 45 ++++++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb7b374..b4540d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 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 diff --git a/spotipy/oauth2.py b/spotipy/oauth2.py index 03f6b99..f3fc7b1 100644 --- a/spotipy/oauth2.py +++ b/spotipy/oauth2.py @@ -650,6 +650,8 @@ class SpotifyPKCE(SpotifyAuthBase): def get_authorize_url(self, state=None): """ Gets the URL to use to authorize this app """ + if not self.code_challenge: + self.get_pkce_handshake_parameters() payload = { "client_id": self.client_id, "response_type": "code", @@ -726,8 +728,8 @@ class SpotifyPKCE(SpotifyAuthBase): "Go to the following URL: {}\n" "Enter the URL you were redirected to: ".format(url) ) - response = SpotifyOAuth._get_user_input(prompt) - state, code = SpotifyOAuth.parse_auth_response_url(response) + response = self._get_user_input(prompt) + state, code = self.parse_auth_response_url(response) if self.state is not None and self.state != state: raise SpotifyStateError(self.state, state) return code @@ -904,12 +906,16 @@ class SpotifyPKCE(SpotifyAuthBase): Parameters: - url - the response url """ - _, code = SpotifyOAuth.parse_auth_response_url(url) + _, code = self.parse_auth_response_url(url) if code is None: return url else: return code + @staticmethod + def parse_auth_response_url(url): + return SpotifyOAuth.parse_auth_response_url(url) + class SpotifyImplicitGrant(SpotifyAuthBase): """ Implements Implicit Grant Flow for client apps @@ -920,16 +926,29 @@ class SpotifyImplicitGrant(SpotifyAuthBase): Security Advisory ----------------- - The Implicit Grant Flow is part of the - [OAuth 2.0 standard](https://oauth.net/2/grant-types/implicit/). - It is intended for client-side (running in browser or a native app) - interactions where the client secret would have to be hard-coded and - exposed. OAuth no longer recommends its use because sensitive - info (the auth token) can be yanked from the browser address bar or - history, instead recommending the Auth Code flow with PKCE. However, - 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), - so Implicit Grant is the only viable options for client-side Spotify - API requests. + The OAuth standard no longer recommends the Implicit Grant Flow for + client-side code. Spotify has implemented the OAuth-suggested PKCE + extension that removes the need for a client secret in the + Authentication Code flow. Use the SpotifyPKCE auth manager instead + of SpotifyImplicitGrant. + + SpotifyPKCE contains all of the functionality of + SpotifyImplicitGrant, plus automatic response retrieval and + refreshable tokens. Only a few replacements need to be made: + + * 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"