From 8ddea8762dfecefde01963310a75cd821b92691e Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Wed, 16 Sep 2026 19:52:23 +0000 Subject: [PATCH] Fix OAuth token exchange failing on the widened scope Google echoes back a token response scope string that's a superset/ reordering of what was requested (asking for "youtube" got back "youtube.readonly youtube ..." too, since the broader scope implies the narrower one) -- oauthlib does a strict string comparison and raised "Warning: Scope has changed" on every reconnect attempt after the scope was widened for the unsubscribe feature, even though Google's consent screen had already granted access. OAUTHLIB_RELAX_TOKEN_SCOPE=1 disables that check, matching what Google's own behavior actually requires for any multi-scope request. Co-Authored-By: Claude Sonnet 5 --- backend/app/services/google_oauth.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/app/services/google_oauth.py b/backend/app/services/google_oauth.py index ab44336..d22cd05 100644 --- a/backend/app/services/google_oauth.py +++ b/backend/app/services/google_oauth.py @@ -1,6 +1,14 @@ import logging +import os from datetime import datetime, timezone +# Google frequently echoes back a scope string that's a superset/reordering +# of what we requested (e.g. we ask for "youtube", Google's token response +# also lists "youtube.readonly" since it's implied). oauthlib does an exact +# string comparison by default and raises on any mismatch -- this disables +# that overly strict check. Must be set before requests_oauthlib reads it. +os.environ.setdefault("OAUTHLIB_RELAX_TOKEN_SCOPE", "1") + import httpx from google.auth.transport.requests import Request as GoogleAuthRequest from google.oauth2.credentials import Credentials