-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Normal
-
Affects Version/s: 3.0.0rc1
-
Component/s: Webservice & Networking
-
None
Since the MetaBrainz authentication migration, the OAuth server rotates refresh tokens: each successful token refresh consumes the old refresh token and returns a new one in the response, revoking the previous token server-side (single-use rotation).
Picard's OAuthManager.on_refresh_access_token_finished() (picard/oauth.py) only stores the new access_token from the refresh response and ignores the returned refresh_token. As a result, after the access token expires (expires_in is 3600s / 1 hour), the next refresh sends the now-revoked refresh token, the server responds with HTTP 400 ("'refresh_token' in request was already revoked"), and Picard calls forget_refresh_token() and logs the user out.
Symptom: The user is asked to log in again on almost every launch, with the failure appearing after a delay matching the ~1-hour access token lifetime.
Log excerpt:
E: oauth.on_refresh_access_token_finished: OAuth: access_token refresh failed:
{"error": "invalid_request", "error_description": "'refresh_token' in request was already revoked."}
W: OAuth: refresh token is no longer valid, logging out
Fix: Persist the rotated refresh_token when the token endpoint returns one on refresh (preserving the existing granted scopes). A live token response confirms the server returns a refresh_token field (mebr_...) alongside access_token and expires_in: 3600.