Skip to content

Conversation

@KennethTrinh
Copy link

No description provided.

@KennethTrinh
Copy link
Author

  • Added Client-Token to the headers since it's apparently now needed for license request
  • Changed timestamp_now = time.time() * 1000 -> timestamp_now = time.time() to prevent unnecessary calls to self._setup_authorization() in def _refresh_session_auth

Testing

python3 -m venv env
. env/bin/activate
pip install -e .
votify 'https://open.spotify.com/track/7c1NzmP0XUmo0DCb8KxkrG'

@KennethTrinh
Copy link
Author

I may well be wrong but I noticed that in _refresh_session_auth() the line timestamp_now = time.time() * 1000 was replaced with timestamp_now = time.time(). I understand this was done to prevent unnecessary calls to _setup_authorization(), but since session_auth_expire_time is still stored in milliseconds, this might create a unit mismatch. The comparison could then always evaluate true and skip refreshes entirely. Multiplying time.time() by 1000 might still be needed so both values use the same unit.

Let's go through an example. When I make a request to api/token, I got back the following timestamp in milliseconds:

'accessTokenExpirationTimestampMs': 1759617378605

Here's a quick way to see the dates in bash:

$ date -r 1759617378605 # this is in milliseconds, note the invalid date below
Fri Jan 20 16:10:05 EST 57730

$ date -r $((1759617378605/1000)) # this is the correct conversion to seconds, which is timestamp_session_expire
Sat Oct  4 18:36:18 EDT 2025

$ python3 -c 'import time as t; print(t.time())' # this is also in seconds, which is timestamp_now
1759614343.295896

I updated the code to check if timestamp_now (1759614343.295896) is less than timestamp_session_expire (1759617378605 / 1000). If this is true, then the token is still valid, and we early return to avoid making another request to authorization endpoints.

Lets ignore the decimals for now.

timestamp_now is:
1759614343
timestamp_session_expire is:
1759617378

timestamp_now < timestamp_session_expire evaluates to true, which makes sense: the token should expire in the future. Both of them are 10 digits long.

If you multiply timestamp_now by 1000 as you suggested, you get:
1759614343000
which is 13 digits long. This was the previous logic, which always called self._setup_authorization() unconditionally

A 13 digit long positive integer will always be greater than a 10 digit long positive integer. Since the condition isn't true, the code will always skip the early return and make a request to authorization endpoints every time. I believe the point of the def _refresh_session_auth is to only call self._setup_authorization() IFF the token is expired.

This didn't cause issues previously with the api/token endpoint, but now causes issues when adding the new /clienttoken endpoint to the authorization flow. And the reason I had to add it sequentially after the api/token is because we need the clientId from the api/token response. Can someone double check my logic?

@yashikada
Copy link

yashikada commented Oct 5, 2025

Can someone double check my logic?

your logic is good.
This PR is very similar to my code psted here and solve the issue of invalid authorization after the call to /clienttoken .
I have already approved because I tested while my code didn't work

@yashikada
Copy link

One change you could make to match the current web version is to remove the ts parameter from SESSION_TOKEN_URL and add totpServer with the same value of totp.
With mitmproxy I see this behaviour.
Here another my test.
I don't know if a different PR is better.

@yashikada
Copy link

yashikada commented Oct 5, 2025

Can someone double check my logic?

I add other informations for confirm your logic is good.
In these lines of code there is the inizialization of self.session_auth_expire_time which is used in _refresh_session_auth, here the value is divided by 1000 so the value is in seconds and not ms.

I don't know when the _setup_authorization_with_device_flow function is used.
In this case, the self.session_auth_expire_time value is multiplied by 1000.
The value should be standardized (ms or seconds) or a switch should be implemented, otherwise a new issue will arise.

In the _setup_authorization_with_device_flow function the call to self.set_authorization_header is not more valid because now require 2 parameters and not 1.

@KennethTrinh
Copy link
Author

Yeah good catch, but SpotifyDeviceFlow.DEVICE_TOKEN_URL doesn't return a clientId , so I kept getting 400s when hitting the CLIENT_TOKEN_URL. I tried using SpotifyDeviceFlow.DEVICE_CLIENT_ID but that returns 400 as well.

Using the _setup_authorization_with_device_flow , if you don't specify a client-token, you end up getting this when trying to download:

requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://gue1-spclient.spotify.com/widevine-license/v1/audio/license

Something for glomatico to take a look at

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants