2025, Sep 24 05:16

Как исправить 403 в Google Calendar API: правильные OAuth-области для events.list

Разбираем причину 403 в Google Calendar API при вызове events.list через gcloud ADC: несоответствие OAuth-областей. Пошаговое решение и корректные scopes.

Когда вы авторизуетесь через gcloud для Application Default Credentials и пытаетесь вызвать метод events.list в Google Calendar API, очень легко получить 403 — даже если вход прошёл успешно и файл с учётными данными присутствует. Причина не в файле токена и не в значении calendarId, а в несоответствии между вызываемым методом API и OAuth-областью (scope), выданной при входе.

Контекст проблемы

Авторизация выглядит так: вы передаёте cloud-platform и одну из областей Calendar:

gcloud auth application-default login --client-id-file google_oauth_client_id.json --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/calendar.calendars.readonly"

Затем вы создаёте клиент Calendar и вызываете events.list, но получаете 403. При проверке объекта credentials кажется, что в нём нет заполненных областей, из‑за чего ошибка выглядит как проблема с загрузкой токена. На деле же корень проблемы в том, что выданные области доступа не покрывают конкретный метод, который вы вызываете.

Пример, который воспроизводит ошибку

Следующий фрагмент на Python демонстрирует сценарий, приводящий к 403, если выдана только calendar.calendars.readonly:

from google.auth import default as adc_default
from google.auth.transport.requests import Request as HttpRequest
from googleapiclient.discovery import build as api_build

PERMS = [
    "https://www.googleapis.com/auth/calendar.calendars.readonly"
]

creds_obj, proj_id = adc_default(scopes=PERMS, quota_project_id="my-project-id")
creds_obj.refresh(HttpRequest())
user_token = creds_obj.token
cal_api = api_build("calendar", "v3", credentials=creds_obj)
items = cal_api.events().list(
    calendarId="My Calendar Id",
    maxResults=10,
    singleEvents=True,
    orderBy="startTime"
).execute()

print((creds_obj.scopes, creds_obj.default_scopes, creds_obj.granted_scopes))

Что происходит на самом деле

Каждый метод Calendar API определяет, какие области доступа он принимает. Для events.list среди допустимых областей должна быть calendar.events.readonly. Если же вы аутентифицировались с calendar.calendars.readonly, у токена просто нет прав на чтение событий, и сервер возвращает 403. Это никак не связано с тем, упомянуты ли области в файле токена; важно лишь, был ли ваш токен выпущен с областью, соответствующей методу. Проверить, какие области включены в токен, можно через endpoint tokeninfo:

https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={ACCESS_TOKEN}

Как исправить

Запрашивайте корректную область при входе. В данном случае добавьте calendar.events.readonly. Например:

gcloud auth application-default login \
--scopes=\
https://www.googleapis.com/auth/cloud-platform,\
https://www.googleapis.com/auth/calendar.events.readonly

Если хотите также явно задать нужную область в коде, укажите её при создании учётных данных:

from google.auth import default as adc_default
from google.auth.transport.requests import Request as HttpRequest
from googleapiclient.discovery import build as api_build

PERMS = [
    "https://www.googleapis.com/auth/calendar.events.readonly"
]

creds_obj, proj_id = adc_default(scopes=PERMS, quota_project_id="my-project-id")
creds_obj.refresh(HttpRequest())
user_token = creds_obj.token
cal_api = api_build("calendar", "v3", credentials=creds_obj)
items = cal_api.events().list(
    calendarId="My Calendar Id",
    maxResults=10,
    singleEvents=True,
    orderBy="startTime"
).execute()

Почему это важно

Google API проверяют авторизацию на уровне отдельных методов. Путаница между похожими областями — например, calendars.readonly и events.readonly — приводит к непонятным ошибкам 403, которые не очевидны из локального состояния клиента. Сопоставляйте область доступа с конкретным методом API и при необходимости проверяйте активный токен через tokeninfo — это экономит время и делает аутентификацию менее хрупкой.

Выводы

Всегда согласовывайте OAuth-области с методами API, которые вызываете. Для events.list в Calendar API это — calendar.events.readonly. Если вы авторизовались без неё, повторно выполните gcloud auth application-default login с дополнительной областью и, при необходимости, укажите эту область при сборке клиента. Если сомневаетесь, проверьте области токена через endpoint tokeninfo и сверяйтесь с разделом Authorization в официальной документации по методу.

Статья основана на вопросе на StackOverflow от Ian Burnette и ответе от DazWilkin.