Refactor TOTP and Spotify API auth flow

This commit is contained in:
Rafael Moraes
2025-09-09 16:04:37 -03:00
parent 3d4023d60a
commit 7d2d0b021e
2 changed files with 34 additions and 12 deletions
+5 -5
View File
@@ -53,10 +53,10 @@ class SpotifyApi:
def __init__(
self,
sp_dc: str | None = None,
use_totp: bool = False,
use_device_flow: bool = False,
) -> None:
self.sp_dc = sp_dc
self.use_totp = use_totp
self.use_device_flow = use_device_flow
self._set_session()
@classmethod
@@ -144,10 +144,10 @@ class SpotifyApi:
)
def _setup_authorization(self) -> None:
if self.use_totp:
self._setup_authorization_with_totp()
else:
if self.use_device_flow:
self._setup_authorization_with_device_flow()
else:
self._setup_authorization_with_totp()
def _setup_authorization_with_device_flow(self) -> None:
token_data = self._get_token_via_device_flow()
+29 -7
View File
@@ -1,18 +1,40 @@
from __future__ import annotations
import hashlib
import hmac
import math
import requests
# thanks to https://github.com/glomatico/votify/pull/42#issuecomment-2727036757
class TOTP:
SPOTIFY_SECRETS_JSON = "https://raw.githubusercontent.com/Thereallo1026/spotify-secrets/refs/heads/main/secrets/secretDict.json"
PERIOD = 30
DIGITS = 6
def __init__(self) -> None:
# dumped directly from the object, after all decryptions
self.secret = b"120887534115119493370126558380647147717911891615550"
self.version = 25
self.period = 30
self.digits = 6
self._setup()
def _setup(self) -> None:
version, secret_cipher_bytes = self.get_latest_secret()
self.version = version
self.secret = self.derive_secret_number(secret_cipher_bytes).encode()
def derive_secret_number(self, secret_cipher_bytes: list[int]) -> str:
transformed = [
byte ^ ((i % 33) + 9) for i, byte in enumerate(secret_cipher_bytes)
]
return "".join(str(n) for n in transformed)
def get_latest_secret(self) -> tuple[int, list[int]]:
response = requests.get(self.SPOTIFY_SECRETS_JSON)
response.raise_for_status()
secrets = response.json()
latest_version = max(int(v) for v in secrets.keys())
return latest_version, secrets[str(latest_version)]
def generate(self, timestamp: int) -> str:
counter = math.floor(timestamp / 1000 / self.period)
counter = math.floor(timestamp / 1000 / self.PERIOD)
counter_bytes = counter.to_bytes(8, byteorder="big")
h = hmac.new(self.secret, counter_bytes, hashlib.sha1)
@@ -26,4 +48,4 @@ class TOTP:
| (hmac_result[offset + 3] & 0xFF)
)
return str(binary % (10**self.digits)).zfill(self.digits)
return str(binary % (10**self.DIGITS)).zfill(self.DIGITS)