为什么 Gmail API 使用适用于 Google 日历 API 的代码失败并显示 403 错误?

Why does Gmail API fail with a 403 error, using code that works properly for Google Calendar API?

我正在遵循这些指南:https://developers.google.com/google-apps/calendar/quickstart/python, https://developers.google.com/gmail/api/quickstart/python

我已经完成了两者的所有设置步骤,日历 API 代码工作正常,而 Gmail API 代码失败并出现 403 错误。这是什么原因造成的?这两个 API 有什么不同?

我合并了这两个不同示例中的代码,因此它们共享了尽可能多的 API 设置代码,以排除那里的错误。

我在 Google 开发者控制台中禁用并重新启用了 Gmail API。

如前所述 here,我已启用通讯录和 Google+ APIs。

如前所述, I've tried all three combinations for Gmail scopes - just the scope from the example, just 'https://mail.google.com/',或两者兼而有之。

This 回答,以及其他几个来源,建议 'setting the "Referers" to "Any referer allowed"',但我在 Dev Console 中没有找到包含此选项的页面。我可以通过 "domain verification" 选项卡添加域,但这似乎没有任何影响。

代码:

import httplib2
import os
from pprint import pprint

from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools

import datetime

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None


class GoogleApi(object):
    client_secret_file = 'client_secret.json'
    application_name = 'test_app'

    def __init__(self):
        self.credentials = self.get_credentials()
        self.http = self.credentials.authorize(httplib2.Http())
        self.service = discovery.build(self.api_name, self.api_version, http=self.http)

    def get_credentials(self):
        """Gets valid user credentials from storage.

        If nothing has been stored, or if the stored credentials are invalid,
        the OAuth2 flow is completed to obtain the new credentials.

        Returns:
            Credentials, the obtained credential.
        """
        home_dir = os.path.expanduser('~')
        credential_dir = os.path.join(home_dir, '.credentials')
        if not os.path.exists(credential_dir):
            os.makedirs(credential_dir)
        credential_path = os.path.join(credential_dir,
                                       'test-app.json')

        store = oauth2client.file.Storage(credential_path)
        credentials = store.get()
        if not credentials or credentials.invalid:
            flow = client.flow_from_clientsecrets(self.client_secret_file, self.scopes)
            flow.user_agent = self.application_name
            if flags:
                credentials = tools.run_flow(flow, store, flags)
            else:  # Needed only for compatability with Python 2.6
                credentials = tools.run(flow, store)
            print 'Storing credentials to ' + credential_path
        return credentials


class CalendarApi(GoogleApi):
    scopes = 'https://www.googleapis.com/auth/calendar.readonly'
    api_name = 'calendar'
    api_version = 'v3'

    def get_calendar_ids(self):
        cl = self.service.calendarList().list().execute()
        ids = [x['id'] for x in cl['items']]
        return ids


class GmailApi(GoogleApi):

    scopes = 'https://mail.google.com/'
    #scopes = 'https://www.googleapis.com/auth/gmail.readonly'
    api_name = 'gmail'
    api_version = 'v1'

    def get_labels(self):
        ml = self.service.users().labels().list(userId='me').execute()
        labels = results.get('labels', [])
        return labels


def main():
    cc = CalendarApi()
    ids = cc.get_calendar_ids()
    print(ids)

    m = GmailApi()
    labels = m.get_labels()
    print(labels)


if __name__ == '__main__':
    main()

当前错误("Insufficient Permission"):

Traceback (most recent call last):
  File "gmail_api_test.py", line 92, in <module>
    main()
  File "gmail_api_test.py", line 87, in main
    labels = m.get_labels()
  File "gmail_api_test.py", line 76, in get_labels
    ml = self.service.users().labels().list(userId='me').execute()
  File "/usr/local/lib/python2.7/site-packages/oauth2client/util.py", line 140, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/googleapiclient/http.py", line 729, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/gmail/v1/users/me/labels?alt=json returned "Insufficient Permission">

旧版本代码错误 ("Access Not Configured. The API (Gmail API) is not enabled for your project. Please use the Google Developers Console to update your configuration."):

Traceback (most recent call last):
  File "gmail_demo.py", line 71, in <module>
    main()
  File "gmail_demo.py", line 59, in main
    results = service.users().labels().list(userId='me').execute()
  File "/usr/local/lib/python2.7/site-packages/oauth2client/util.py", line 142, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/googleapiclient/http.py", line 729, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/gmail/v1/users/me/labels?alt=json returned "Access Not Configured. The API (Gmail API) is not enabled for your project. Please use the Google Developers Console to update your configuration.">

权限仍然不足的原因是因为你文件中的refresh_token仍然有效。您在对用户进行身份验证时获得的 access_tokenrefresh_token 仅具有您当时指定的范围。

想象一下,如果情况并非如此。然后,您可以向您的用户询问 readonly,然后添加无所不包的 https://mail.google.com/-scope,并在未经用户同意的情况下做很多额外的事情。

这里已经得到更彻底的回答:

尽管您的问题涉及 google 日历 API,但 403 错误与许多 google API 应用程序类似。