最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Google Calendar OAuth Bad Request - Refresh Token not working? - Stack Overflow

programmeradmin3浏览0评论

I am trying to implement a Google Cloud Function in which I can fetch user's google calendars (read only). I have a next.js app, in which I request their consent with the scopes, and save the access token + refresh token in Firebase. I can then, using Python, pull their calendar events using those tokens & client secrets.

However, after a couple of hours, when I'm guessing the access token expires, it is not able to generate a new one with the refresh token (even though it should be able to?). I keep getting "Invalid Grant" & "Bad Request". Since this will be running as a cloud function, I cannot have users granting access every time.

These are the full logs:

Expiry: None
Valid: True
file_cache is only supported with oauth2client<4.0.0
Getting upcoming events from 2025-03-31T07:00:00Z to 2025-04-01T07:00:00Z (adjusted for timezone: America/Los_Angeles)...
Refreshing credentials due to a 401 response. Attempt 1/2.
      ^^^^^^^^^
  File "/home/codespace/.python/current/lib/python3.12/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/codespace/.python/current/lib/python3.12/site-packages/googleapiclient/http.py", line 923, in execute
    resp, content = _retry_request(
                    ^^^^^^^^^^^^^^^
  File "/home/codespace/.python/current/lib/python3.12/site-packages/googleapiclient/http.py", line 191, in _retry_request
    resp, content = http.request(uri, method, *args, **kwargs)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/codespace/.python/current/lib/python3.12/site-packages/google_auth_httplib2.py", line 245, in request
    self.credentials.refresh(self._request)
  File "/home/codespace/.python/current/lib/python3.12/site-packages/google/oauth2/credentials.py", line 409, in refresh
    ) = reauth.refresh_grant(
        ^^^^^^^^^^^^^^^^^^^^^
  File "/home/codespace/.python/current/lib/python3.12/site-packages/google/oauth2/reauth.py", line 366, in refresh_grant
    _client._handle_error_response(response_data, retryable_error)
  File "/home/codespace/.python/current/lib/python3.12/site-packages/google/oauth2/_client.py", line 69, in _handle_error_response
    raise exceptions.RefreshError(
google.auth.exceptions.RefreshError: ('invalid_grant: Bad Request', {'error': 'invalid_grant', 'error_description': 'Bad Request'})

Here is the code I am using to fetch calendar events:

creds = Credentials(
    token=access_token,
    refresh_token=refresh_token,
    token_uri=token_uri,
    client_id=google_client_id,
    client_secret=google_client_secret
)
if self.creds.expired and self.creds.refresh_token:
   logger.info("getting new credentials")
   self.creds.refresh(GoogleRequest())

print(self.creds.expiry)
print(self.creds.valid)

calendar_service = build("calendar", "v3", credentials=self.creds)
timezone="America/Los_Angeles"
local_tz = pytz.timezone(timezone)

# Get today's midnight in the local timezone
now_local = datetime.datetime.now(local_tz).replace(hour=0, minute=0, second=0, microsecond=0)

# Convert to UTC
now_utc = now_local.astimezone(pytz.UTC)
now_format = now_utc.strftime('%Y-%m-%dT%H:%M:%SZ')
future_utc = (now_local + datetime.timedelta(days=1)).astimezone(pytz.UTC)
future_format = future_utc.strftime('%Y-%m-%dT%H:%M:%SZ')

logger.info(f'Getting upcoming events from {now_format} to {future_format} (adjusted for timezone: {timezone})...')

events_result = calendar_service.events().list(
     calendarId='primary',
     timeMin=now_format,
     timeMax=future_format,
     maxResults=10,
     singleEvents=True,
     orderBy='startTime'
).execute()
events = events_result.get("items", [])

And here is the code to grant permissions via my next.js app:

const handleGrantCalendarAccess = async () => {
        const provider = new GoogleAuthProvider();
        provider.addScope('.readonly');
        provider.setCustomParameters({
            access_type: 'offline',
            include_granted_scopes:'true',
            prompt: 'consent'
        });

        try {
            setLoading(true);
            const user = auth.currentUser;
            let result;

            if (user) {
                // Reauthenticate the user if already signed in
                result = await reauthenticateWithPopup(user, provider);
            } else {
                // Sign in the user if not already signed in
                result = await signInWithPopup(auth, provider);
            }


            const credential = GoogleAuthProvider.credentialFromResult(result);
            const userEmail = user?.email;
            const userName = user?.displayName;
            const accessToken = credential ? credential.accessToken : null;
            const refreshToken = (result.user as any).stsTokenManager.refreshToken;
            const idToken = await user?.getIdToken();
            //upload to Firebase

I have been stuck on this for quite a while, so help is greatly appreciated!

发布评论

评论列表(0)

  1. 暂无评论