Fix issue with hanging authorization process if no port is provided, fixes #471, fixes #474 (#477)

* Fixed issue where using rediret uri 'localhost' (without specifying a port) would cause the authorization process to hang (not asking the user to paste/enter the URL redirected to)

* Updated the documentation to match the changes to the authorization process - also kind of fixing issues #474 and #471

* Fixed Flake8 over-indentation remark
This commit is contained in:
Jonas Camillus Jeppesen 2020-04-19 23:39:03 +02:00 committed by GitHub
parent 98d49fb20e
commit d3bab0f054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 31 deletions

View File

@ -84,24 +84,20 @@ Or you can get the source from github at https://github.com/plamere/spotipy
Getting Started Getting Started
=============== ===============
All methods require user authorization. You will need to All methods require user authorization. You will need to register your app at
register your app to get the credentials necessary to make authorized calls. `My Dashboard <https://developer.spotify.com/dashboard/applications>`_
to get the credentials necessary to make authorized calls
(a *client id* and *client secret*).
Even if your script does not have an accessible URL you will need to specify one *Spotipy* supports two authorization flows:
when registering your application which the Spotify authentication server will
redirect to after successful login. The URL doesn't need to be publicly
accessible, so you can specify "http://localhost/".
Register your app at
`My Applications
<https://developer.spotify.com/my-applications/#!/applications>`_ and register the
redirect URI mentioned in the above paragragh.
*spotipy* supports two authorization flows:
- The **Authorization Code flow** This method is suitable for long-running applications - The **Authorization Code flow** This method is suitable for long-running applications
which the user logs into once. It provides an access token that can be refreshed. which the user logs into once. It provides an access token that can be refreshed.
.. note:: Requires you to add a redirect URI to your application at
`My Dashboard <https://developer.spotify.com/dashboard/applications>`_.
See `Redirect URI`_ for more details.
- The **Client Credentials flow** The method makes it possible - The **Client Credentials flow** The method makes it possible
to authenticate your requests to the Spotify Web API and to obtain to authenticate your requests to the Spotify Web API and to obtain
a higher rate limit than you would with the Authorization Code flow. a higher rate limit than you would with the Authorization Code flow.
@ -118,7 +114,7 @@ browser or from a mobile app.
To support the **Authorization Code Flow** *Spotipy* provides a To support the **Authorization Code Flow** *Spotipy* provides a
utility method ``util.prompt_for_user_token`` that will attempt to authorize the utility method ``util.prompt_for_user_token`` that will attempt to authorize the
user. You can pass your app credentials directly into the method as arguments:: user. You can pass your app credentials directly into the method as arguments::
util.prompt_for_user_token(username, util.prompt_for_user_token(username,
scope, scope,
@ -127,22 +123,49 @@ user. You can pass your app credentials directly into the method as arguments::
redirect_uri='your-app-redirect-url') redirect_uri='your-app-redirect-url')
or if you are reluctant to immortalize your app credentials in your source code, or if you are reluctant to immortalize your app credentials in your source code,
you can set environment variables like so:: you can set environment variables like so (use ``SET`` instead of ``export``
on Windows)::
export SPOTIPY_CLIENT_ID='your-spotify-client-id' export SPOTIPY_CLIENT_ID='your-spotify-client-id'
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret' export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
export SPOTIPY_REDIRECT_URI='your-app-redirect-url' export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
Call ``util.prompt_for_user_token`` method with the username and the Call the ``util.prompt_for_user_token`` method with the username, the
desired scope (see `Using desired scope (see `Using
Scopes <https://developer.spotify.com/web-api/using-scopes/>`_ for information Scopes <https://developer.spotify.com/web-api/using-scopes/>`_ for information
about scopes) and credentials. After succesfully about scopes) and credentials (passed directly or via environment variables)
authenticating your app, you can simply copy the to start the authorization process.
"http://localhost/?code=..." URL from your browser and paste it to the
console where your script is running. This will coordinate the user authorization via Redirect URI
your web browser and callback to the SPOTIPY_REDIRECT_URI you were redirected to ------------
with the authorization token appended. The credentials are cached locally and For the **Authorization Code Flow** you need to add a **redirect URI**
are used to automatically re-authorized expired tokens. to your application at
`My Dashboard <https://developer.spotify.com/dashboard/applications>`_
(navigate to your application and then *[Edit Settings]*).
The ``redirect_uri`` argument or ``SPOTIPY_REDIRECT_URI`` environment variable
must match the redirect URI added to your application in your Dashboard.
The redirect URI can be any valid URI (it does not need to be accessible)
such as ``http://example.com``, ``http://localhost`` or ``http://127.0.0.1:9090``.
Calling ``util.prompt_for_user_token`` will open Spotify's
application authorization page in your browser (and require you to log in
if you are not already logged in to spotify.com), unless a locally cached
access token exist from a previous authorization/authentication.
After successful authentication your browser will be redirected to
the redirect URI of your application, e.g. ``http://localhost:9090/?code=<code>``.
If your request URI is set to ``http://127.0.0.1:<port>`` or ``http://localhost:<port>``
Spotipy will automatically complete the authorization process
(obtain the ``<code>`` from the URI).
If your request URI is set to any other URI you will need to manually copy the
URI from the browser's address bar and paste it into the terminal/console where
your Spotipy script is running (Spotipy will instruct you to do so).
Example
-------
Here's an example of getting user authorization to read a user's saved tracks:: Here's an example of getting user authorization to read a user's saved tracks::
@ -178,7 +201,7 @@ in comparison with requests to the Web API made without an access token,
is that a higher rate limit is applied. is that a higher rate limit is applied.
As opposed to the Authorization Code Flow, you will not need to set ``SPOTIPY_REDIRECT_URI``, As opposed to the Authorization Code Flow, you will not need to set ``SPOTIPY_REDIRECT_URI``,
which means you will never be redirected to the sign in page in your browser. which means you will never be redirected to the sign in page in your browser::
export SPOTIPY_CLIENT_ID='your-spotify-client-id' export SPOTIPY_CLIENT_ID='your-spotify-client-id'
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret' export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'

View File

@ -363,11 +363,20 @@ class SpotifyOAuth(SpotifyAuthBase):
redirect_host, redirect_port = get_host_port(redirect_info.netloc) redirect_host, redirect_port = get_host_port(redirect_info.netloc)
if redirect_host in ("127.0.0.1", "localhost") and redirect_info.scheme == "http": if redirect_host in ("127.0.0.1", "localhost") and redirect_info.scheme == "http":
return self._get_auth_response_local_server(redirect_port) # Only start a local http server if a port is specified
else: if redirect_port:
logger.info('Paste that url you were directed to in order to ' return self._get_auth_response_local_server(redirect_port)
'complete the authorization') else:
return self._get_auth_response_interactive() logger.warning('Using `%s` as redirect URI without a port. '
'Specify a port (e.g. `%s:8080`) to allow '
'automatic retrieval of authentication code '
'instead of having to copy and paste '
'the URL your browser is redirected to.',
redirect_host, redirect_host)
logger.info('Paste that url you were directed to in order to '
'complete the authorization')
return self._get_auth_response_interactive()
def get_authorization_code(self, response=None): def get_authorization_code(self, response=None):
if response: if response:

View File

@ -100,12 +100,12 @@ def prompt_for_user_token(
return None return None
def get_host_port(netloc, default_port=8080): def get_host_port(netloc):
if ":" in netloc: if ":" in netloc:
host, port = netloc.split(":", 1) host, port = netloc.split(":", 1)
port = int(port) port = int(port)
else: else:
host = netloc host = netloc
port = default_port port = None
return host, port return host, port