nexusbert commited on
Commit
22ef84d
·
1 Parent(s): ece1b22

push oauth fix

Browse files
Files changed (2) hide show
  1. app/main.py +25 -1
  2. app/spotify_oauth.py +37 -1
app/main.py CHANGED
@@ -19,9 +19,10 @@ from .spotify_oauth import (
19
  build_authorize_url,
20
  default_redirect_uri,
21
  exchange_code_for_token,
 
22
  generate_state,
23
  validate_redirect_uri,
24
- )
25
  from .spotify_playlists_api import (
26
  add_items_to_playlist,
27
  create_playlist,
@@ -66,6 +67,12 @@ class RecommendationResponse(BaseModel):
66
  playlist_external_url: str | None = None
67
 
68
 
 
 
 
 
 
 
69
  def _extract_bearer_token(authorization: str | None) -> str:
70
  if not authorization:
71
  raise HTTPException(
@@ -299,6 +306,23 @@ def spotify_me(authorization: str | None = Header(default=None)) -> dict:
299
  raise HTTPException(status_code=500, detail="Unexpected server error") from exc
300
 
301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  @app.get("/spotify/artists/id/{artist_id}")
303
  def spotify_get_artist(
304
  artist_id: str,
 
19
  build_authorize_url,
20
  default_redirect_uri,
21
  exchange_code_for_token,
22
+ exchange_code_for_token_pkce,
23
  generate_state,
24
  validate_redirect_uri,
25
+ )
26
  from .spotify_playlists_api import (
27
  add_items_to_playlist,
28
  create_playlist,
 
67
  playlist_external_url: str | None = None
68
 
69
 
70
+ class SpotifyMobileTokenRequest(BaseModel):
71
+ code: str
72
+ code_verifier: str
73
+ redirect_uri: str
74
+
75
+
76
  def _extract_bearer_token(authorization: str | None) -> str:
77
  if not authorization:
78
  raise HTTPException(
 
306
  raise HTTPException(status_code=500, detail="Unexpected server error") from exc
307
 
308
 
309
+ @app.post("/mobile/spotify/token")
310
+ def spotify_mobile_token(body: SpotifyMobileTokenRequest) -> dict:
311
+ try:
312
+ return exchange_code_for_token_pkce(
313
+ code=body.code,
314
+ redirect_uri=body.redirect_uri,
315
+ code_verifier=body.code_verifier,
316
+ )
317
+ except SpotifyAPIError as exc:
318
+ headers = {}
319
+ if exc.retry_after is not None:
320
+ headers["Retry-After"] = str(exc.retry_after)
321
+ raise HTTPException(status_code=exc.status_code, detail=exc.to_dict(), headers=headers) from exc
322
+ except Exception as exc:
323
+ raise HTTPException(status_code=500, detail="Unexpected server error") from exc
324
+
325
+
326
  @app.get("/spotify/artists/id/{artist_id}")
327
  def spotify_get_artist(
328
  artist_id: str,
app/spotify_oauth.py CHANGED
@@ -86,7 +86,6 @@ def exchange_code_for_token(
86
  code: str,
87
  redirect_uri: str,
88
  ) -> Dict[str, Any]:
89
- """Exchange authorization code for access token and refresh token."""
90
  settings = get_settings()
91
  if not settings.spotify_client_id or not settings.spotify_client_secret:
92
  raise RuntimeError("Missing SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET")
@@ -117,6 +116,43 @@ def exchange_code_for_token(
117
  return resp.json()
118
 
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  def refresh_access_token(*, refresh_token: str) -> Dict[str, Any]:
121
  """
122
  Refresh an expired access token using a refresh token (Authorization Code flow).
 
86
  code: str,
87
  redirect_uri: str,
88
  ) -> Dict[str, Any]:
 
89
  settings = get_settings()
90
  if not settings.spotify_client_id or not settings.spotify_client_secret:
91
  raise RuntimeError("Missing SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET")
 
116
  return resp.json()
117
 
118
 
119
+ def exchange_code_for_token_pkce(
120
+ *,
121
+ code: str,
122
+ redirect_uri: str,
123
+ code_verifier: str,
124
+ ) -> Dict[str, Any]:
125
+ settings = get_settings()
126
+ if not settings.spotify_client_id or not settings.spotify_client_secret:
127
+ raise RuntimeError("Missing SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET")
128
+
129
+ headers = {
130
+ "Authorization": _basic_auth_header(
131
+ settings.spotify_client_id, settings.spotify_client_secret
132
+ ),
133
+ "Content-Type": "application/x-www-form-urlencoded",
134
+ }
135
+ data = {
136
+ "grant_type": "authorization_code",
137
+ "code": code,
138
+ "redirect_uri": redirect_uri,
139
+ "code_verifier": code_verifier,
140
+ }
141
+
142
+ resp = requests.post(SPOTIFY_TOKEN_URL, headers=headers, data=data, timeout=20)
143
+ if resp.status_code >= 400:
144
+ try:
145
+ raw = resp.json()
146
+ except Exception: # noqa: BLE001
147
+ raw = {"error": resp.text}
148
+ raise SpotifyAPIError(
149
+ status_code=resp.status_code,
150
+ message=str(raw.get("error_description") or raw.get("error") or "auth_error"),
151
+ raw=raw if isinstance(raw, dict) else None,
152
+ )
153
+ return resp.json()
154
+
155
+
156
  def refresh_access_token(*, refresh_token: str) -> Dict[str, Any]:
157
  """
158
  Refresh an expired access token using a refresh token (Authorization Code flow).