Open mrticker opened 2 years ago
What happens in from config import APP_CLIENT_ID, APP_CLIENT_SECRET
?
Did you try setting client_id
and client_secret
directly, eg.
SpotifyClientCredentials( client_id ="abc", client_secret ="abc" )
?
config just sets these variables appropriately.
In general, it seems inconsistent, one time it works, another it doesn't. Currently it works so I'll close this issue for now.
It randomly stopped working. Here's the error:
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
C:\usr\Miniconda3\lib\site-packages\spotipy\oauth2.py in _request_access_token(self)
264 )
--> 265 response.raise_for_status()
266 token_info = response.json()
C:\usr\Miniconda3\lib\site-packages\requests\models.py in raise_for_status(self)
1020 if http_error_msg:
-> 1021 raise HTTPError(http_error_msg, response=self)
1022
HTTPError: 400 Client Error: Bad Request for url: https://accounts.spotify.com/api/token
During handling of the above exception, another exception occurred:
SpotifyOauthError Traceback (most recent call last)
d:\devel\spotify\get_playlists.py in <module>
39
40 try:
---> 41 res = sp.user_playlists(user)
42 except SpotifyException as e:
43 print(e)
C:\usr\Miniconda3\lib\site-packages\spotipy\client.py in user_playlists(self, user, limit, offset)
761 - offset - the index of the first item to return
762 """
--> 763 return self._get(
764 "users/%s/playlists" % user, limit=limit, offset=offset
765 )
C:\usr\Miniconda3\lib\site-packages\spotipy\client.py in _get(self, url, args, payload, **kwargs)
295 kwargs.update(args)
296
--> 297 return self._internal_call("GET", url, payload, kwargs)
298
299 def _post(self, url, args=None, payload=None, **kwargs):
C:\usr\Miniconda3\lib\site-packages\spotipy\client.py in _internal_call(self, method, url, payload,
params)
219 if not url.startswith("http"):
220 url = self.prefix + url
--> 221 headers = self._auth_headers()
222
223 if "content_type" in args["params"]:
C:\usr\Miniconda3\lib\site-packages\spotipy\client.py in _auth_headers(self)
210 return {}
211 try:
--> 212 token = self.auth_manager.get_access_token(as_dict=False)
213 except TypeError:
214 token = self.auth_manager.get_access_token()
C:\usr\Miniconda3\lib\site-packages\spotipy\oauth2.py in get_access_token(self, as_dict, check_cache
)
236 return token_info if as_dict else token_info["access_token"]
237
--> 238 token_info = self._request_access_token()
239 token_info = self._add_custom_values_to_token_info(token_info)
240 self.cache_handler.save_token_to_cache(token_info)
C:\usr\Miniconda3\lib\site-packages\spotipy\oauth2.py in _request_access_token(self)
267 return token_info
268 except requests.exceptions.HTTPError as http_error:
--> 269 self._handle_oauth_error(http_error)
270
271 def _add_custom_values_to_token_info(self, token_info):
C:\usr\Miniconda3\lib\site-packages\spotipy\oauth2.py in _handle_oauth_error(self, http_error)
144 error_description = None
145
--> 146 raise SpotifyOauthError(
147 'error: {0}, error_description: {1}'.format(
148 error, error_description
SpotifyOauthError: error: invalid_client, error_description: Failed to get client
Hey,
I am facing the same error as well. Has anyone figured out a workaround..?
Can you confirm you're receiving the error SpotifyOauthError: error: invalid_client, error_description: Failed to get client
?
If so, please print the client id and make sure it is what you expect.
The client id is correct but I have been just getting a 400 error for the token. I tried out the examples/app.py and was able to get it working till the sign in step and was able to get a code but i was not able to get the other way without the callback to work
On Fri, Oct 21, 2022 at 6:39 PM Peter Schorn @.***> wrote:
Can you confirm you're receiving the error SpotifyOauthError: error: invalid_client, error_description: Failed to get client ?
If so, please print the client id and make sure it is what you expect.
— Reply to this email directly, view it on GitHub https://github.com/plamere/spotipy/issues/856#issuecomment-1287502524, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACOJUGTX5CCTXTUVAWPPZFLWEMLRNANCNFSM6AAAAAAQOP76WE . You are receiving this because you commented.Message ID: @.***>
Please post the code that produces the error, as well as the full output.
Hello everyone! I have the same issue using Spotipy in a docker container:
File "/usr/src/app/module_spotipy.py", line 36, in get_playlist
playlist_id = playlist_exists(sp, playlist_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/module_spotipy.py", line 43, in playlist_exists
user_id = sp.me()['id']
^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 1219, in me
return self._get("me/")
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 321, in _get
return self._internal_call("GET", url, payload, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 245, in _internal_call
headers = self._auth_headers()
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 236, in _auth_headers
token = self.auth_manager.get_access_token(as_dict=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 525, in get_access_token
token_info = self.validate_token(self.cache_handler.get_cached_token())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 380, in validate_token
token_info = self.refresh_access_token(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 596, in refresh_access_token
self._handle_oauth_error(http_error)
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 146, in _handle_oauth_error
raise SpotifyOauthError(
spotipy.oauth2.SpotifyOauthError: error: invalid_client, error_description: Invalid client
It works fine at first but it seems there is a problem with a token refreshment after some timeout. If I connect to container with vscode and run script manually everything is working as expected.
I'm using .env to pass the credentials:
SPOTIPY_CLIENT_ID='91c927...'
SPOTIPY_CLIENT_SECRET='8c0b8...'
SPOTIPY_REDIRECT_URI='http://localhost:8083'
The ENV variables is available when I check them in a container console.
Maybe I missed something in configuration? Would be happy to hear any ideas or thoughts.
@tonkado what does os.getenv('SPOTIPY_CLIENT_ID')
return from the python code inside your docker container?
@stephanebruckert it returns an actual client id (the same that I provide via ENV)
@tonkado Are you able to get it working without docker at least?
Furthermore, you should modify your actual project code to print the client id to the console and verify that it is what you expect (not just your env_test.py
script).
Thank you for suggestion. I've add some logging. After restart it working but at some point I've got error again:
__main__ SPOTIPY_CLIENT_ID: '91c927e2xxxxxxxxxxxxxxxxxxxxxx'
Connecting to 149.154.167.51:443/TcpFull...
Connection to 149.154.167.51:443/TcpFull complete!
on_track_found SPOTIPY_CLIENT_ID: '91c927e2xxxxxxxxxxxxxxxxxxxxxx'
Unhandled exception on my_event_handler
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 588, in refresh_access_token
response.raise_for_status()
File "/usr/local/lib/python3.12/site-packages/requests/models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://accounts.spotify.com/api/token
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/telethon/client/updates.py", line 497, in _dispatch_update
await callback(event)
File "/usr/src/app/module_telegram.py", line 30, in my_event_handler
callback(event.raw_text)
File "/usr/src/app/tg_monitor.py", line 25, in on_track_found
playlist_id = get_playlist(sp)
^^^^^^^^^^^^^^^^
File "/usr/src/app/module_spotipy.py", line 36, in get_playlist
playlist_id = playlist_exists(sp, playlist_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/module_spotipy.py", line 43, in playlist_exists
user_id = sp.me()['id']
^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 1219, in me
return self._get("me/")
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 321, in _get
return self._internal_call("GET", url, payload, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 245, in _internal_call
headers = self._auth_headers()
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/client.py", line 236, in _auth_headers
token = self.auth_manager.get_access_token(as_dict=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 525, in get_access_token
token_info = self.validate_token(self.cache_handler.get_cached_token())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 380, in validate_token
token_info = self.refresh_access_token(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 596, in refresh_access_token
self._handle_oauth_error(http_error)
File "/usr/local/lib/python3.12/site-packages/spotipy/oauth2.py", line 146, in _handle_oauth_error
raise SpotifyOauthError(
spotipy.oauth2.SpotifyOauthError: error: invalid_client, error_description: Invalid client
CODE: it's a telegram client that wait for a message with spotify link and add this track to the playlist:
def on_track_found(track):
id = os.getenv('SPOTIPY_CLIENT_ID')
logging.info(f'on_track_found SPOTIPY_CLIENT_ID: {id}')
sp = authenticate_spotify(scope="playlist-modify-public")
playlist_id = get_playlist(sp)
tracks = [track]
expand_playlist_with_new_tracks(sp, playlist_id, tracks)
sp = None
if __name__ == "__main__":
id = os.getenv('SPOTIPY_CLIENT_ID')
logging.info(f'__main__ SPOTIPY_CLIENT_ID: {id}')
client = create_client()
client.start()
set_event_handler(client, on_track_found)
client.run_until_disconnected()
the authenticate_spotify() function:
def authenticate_spotify(scope: str = "user-library-read") -> Spotify:
from spotipy.oauth2 import SpotifyOAuth
try:
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
return sp
except spotipy.oauth2.SpotifyOauthError:
print("Authentication failed: SpotifyOauthError")
print(spotipy.oauth2.SpotifyOauthError)
return None
def authenticate_spotify(scope: str = "user-library-read") -> Spotify: from spotipy.oauth2 import SpotifyOAuth try: sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope)) return sp except spotipy.oauth2.SpotifyOauthError: print("Authentication failed: SpotifyOauthError") print(spotipy.oauth2.SpotifyOauthError) return None
This code does not authenticate anything. It just creates an instance of
Spotify
. Spotify automatically launches the authorization process when you try to call a method (e.g.,Spotify.playlist
).
Furthermore, and most importantly, I don't see any place in the code you provided where you actually provide the client id and client secret to the SpotifyOAuth
authorization manager class.
@Peter-Schorn Then I'm totally confused 😂 Please forgive me my lack of professionalism.
I read the docs (https://spotipy.readthedocs.io/en/latest/#authorization-code-flow) and though that you can just use a ENV variables to securely store id and secret and Spotipy will use them.
or if you are reluctant to immortalize your app credentials in your source code, you
can set environment variables like so (use $env:"credentials" instead of export on Windows):
export SPOTIPY_CLIENT_ID='your-spotify-client-id'
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
Maybe I just work with docker a lot 🤦♂️
What is the correct way to make authorization work in a container without need to pass redirect URL with fresh token to the command line?
spotipy
will retrieve the client id and secret from the environment, exactly as you indicated above.
The issue, most likely, is simply that you have not correctly passed those environment variables to your docker container. Docker containers do NOT have access to the environment variables declared in the process that launched them. You must pass them explicitly. See https://docs.docker.com/engine/reference/commandline/run/#env.
What is the correct way to make authorization work in a container without need to pass redirect URL with fresh token to the command line?
If you can't pass the redirect uri to the program over the command line, then you must setup a server that listens for when the user is redirected to your redirect uri after they authorize your app in the browser. From there, you can continue the authorization process.
@Peter-Schorn Thanks again for your help with ideas.
The issue, most likely, is simply that you have not correctly passed those environment variables to your docker container.
I'm using docker-compose and passing env variables via env_file option:
version: "3"
services:
spotify-python:
container_name: spotify-python
image: spotify-python
build: .
volumes:
- .:/usr/src/app/
env_file:
- .env
ports:
- "8083:8083"
I can confirm that during the SpotifyOAuth() execution ENV is available and correct (my message above).
As for the Client Credentials flow, it's not clear for me. I suppose that it should use the flow described at Spotify docs:
In this case it should be a request from Spotipy to 'https://accounts.spotify.com/api/token' with id and secret, and a fresh token will be returned. After token is received and cached you can use the API methods.
User authentication requires interaction with your web browser. Once you enter your credentials and give authorization, you will be redirected to a url. Paste that url you were directed to to complete the authorization.
I don't understand why it's not working like that and why there is a local URL opening/reading logic.
I can confirm that during the SpotifyOAuth() execution ENV is available and correct (https://github.com/spotipy-dev/spotipy/issues/856#issuecomment-1434506569).
Have you also confirmed that your app can also access the client secret? Furthermore, I must admit that, if you're still getting an invalid_client
error, then I'm still not confident that spotipy
can access both the client id and client secret. For the sake of simplicity if nothing else, you should initialize your instance of Spotify
as follows. (check if you have extraneous whitespace in your client_id
and client_secret
variables.)
client_id = os.getenv('SPOTIPY_CLIENT_ID')
client_secret = os.getenv('SPOTIPY_CLIENT_SECRET')
print(f"client id: '{client_id}'")
print(f"client secret: '{client_secret}'")
sp = spotipy.Spotify(
auth_manager=SpotifyOAuth(
client_id=client_id, client_secret=client_secret, scope=scope
)
)
I suppose that it should use the flow described at Spotify docs:
The Spotify web API has multiple authorization flows (see this article).The Client Credentials Flow does not support authorizing with a user, and so can not be used with endpoints that access/modify user data. It says that right at the top of the page you linked:
The Client Credentials flow is used in server-to-server authentication. Since this flow does not include authorization, only endpoints that do not access user information can be accessed.
spotipy
does support this authorization method too: https://github.com/spotipy-dev/spotipy/blob/572195617b3d63f05f4083a4067fe6eb0dfe448b/spotipy/oauth2.py#L160
If you want you authorize with a user, you must open a URL in the browser. Period. All of the authorization flows that support authorizing with a user require opening a URL in the browser. The reason for this is simple: The Spotify user must be given a chance to login to their Spotify account and grant your app permission to access/modify their data. They can't login to their account directly in your app because then you could steal their username and password, so they have to login on the browser.
@Peter-Schorn Thanks again for your patience and very detailed answers. I appreciate it a lot.
It seems that the problem was with my misunderstanding of the docs. As I write before, I was sure that you can just provide credentials via ENV variables and Spotipy will use them to refresh token. In fact you need to provide credentials as params in SpotifyOAuth. I was confused because somehow make it work without providing a client_id & client_secret as params (maybe just cache).
After I added credentials to SpotifyOAuth (as you describe above) everything is working fine inside a container and no longer throw an error.
For the history:
If you want to make Spotipy work from a Docker container
then you should obtain a Spotify token first:
1. attach to a running container
2. start authorization process with open_browser=False:
sp = spotipy.Spotify(
auth_manager=SpotifyOAuth(
client_id=client_id, client_secret=client_secret, scope=scope, open_browser=False
)
)
3. get the OAuth link to Spotify authorization page
4. open link in the browser and authorize access
5. after redirection -> copy link to clipboard
6. paste link to the console input
Example log of the authorization process:
2023-02-21 18:14:49 INFO User authentication requires interaction with your web browser. Once you enter your credentials and give authorization, you will be redirected to a url. Paste that url you were directed to to complete the authorization.
Go to the following URL: https://accounts.spotify.com/authorize?client_id=91c927e22xxxxxxxxxxxaff3b1c&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8083&scope=playlist-modify-public
Enter the URL you were redirected to: http://localhost:8083/?code=_EhBBgf3LfwIIDHa-lHBPEjrXied6Q3I4Re_-ZlGk5k9a1hF_K2Y53CfO0Ubid9vDu16gtpPyBaW8UqUl-y30Hm7-MxklRgPErmjBKYeK-
/usr/src/app #
After that .cache file will be created and will be used to refresh token when it's expired. Everything will work in a container without need for manual update.
May I kindly suggest that the documentation for this section be updated to improve clarity? I believe it would greatly benefit users of this excellent library and help avoid any potential confusion in the future. Thank you for considering this suggestion.
As I write before, I was sure that you can just provide credentials via ENV variables and Spotipy will use them to refresh token.
Yes, this is correct, and I never said it wasn't. I told you it was correct:
spotipy will retrieve the client id and secret from the environment, exactly as you indicated above. The issue, most likely, is simply that you have not correctly passed those environment variables to your docker container. Docker containers do NOT have access to the environment variables declared in the process that launched them. You must pass them explicitly. See https://docs.docker.com/engine/reference/commandline/run/#env.
In fact you need to provide credentials as params in SpotifyOAuth.
No you don't. You can provide the client id and client secret as environment variables exactly as the documentation says, or you could pass them into the authorization manager explicitly. I asked you to use the latter method because it's simpler.
I get an error when authenticating with credentials in code, as opposed to env variables. The client works when using env variables.
...