1

I'm following these guides: https://developers.google.com/google-apps/calendar/quickstart/python, https://developers.google.com/gmail/api/quickstart/python.

I've completed all the setup steps for both, and the calendar API code works fine, while the Gmail API code fails with a 403 error. What's causing this? What's different between these two APIs?

I've combined the code from these two different examples, so they are sharing as much of the API setup code as possible, to rule out a mistake there.

I've disabled and re-enabled Gmail API in Google Dev Console.

As mentioned here, I've enabled the Contacts and Google+ APIs.

As mentioned here, I've tried all three combinations for Gmail scopes - just the scope from the example, just 'https://mail.google.com/', or both.

This answer, as well as several other sources, suggest 'setting the "Referers" to "Any referer allowed"', but there is no page in Dev Console I can find that contains this option. I can add a domain through the "domain verification" tab, but this does not seem to affect anything.

Code:

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()

Current error ("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">

Error from a previous version of the code ("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.">
Community
  • 1
  • 1
monguin
  • 374
  • 4
  • 13
  • Just shooting in the dark here :) I would guess the stored ´refresh_token´ in your file is not invalid yet, so it will not get a new set of tokens that actually have the `https://mail.google.com/`-scope. – Tholle Jan 07 '16 at 23:03
  • Also just a shot, but have you enabled "old outdated insecure" (or what it is they call standard remote mail access) to the e-mail accounts in question? – Koebmand STO Jan 08 '16 at 00:21
  • @KoebmandSTO, it looks like you're talking about this https://www.google.com/settings/security/lesssecureapps? That didn't help, unfortunately. – monguin Jan 08 '16 at 01:23
  • @Tholle, that was it, thanks! I guess credentials are specific to the API? I didn't realize that. Clearing my ~/.credentials and commenting out the CalendarApi lines made it work. – monguin Jan 08 '16 at 01:25
  • @monguin Sweet! I'll add an answer going into it a bit further :) – Tholle Jan 08 '16 at 01:28
  • @monquin yes, that was the one. Glad Tholle had hit the right and you got it working :) – Koebmand STO Jan 08 '16 at 04:20
  • Possible duplicate of [How do I get around HttpError 403 Insufficient Permission? (gmail api, python)](http://stackoverflow.com/questions/32143126/how-do-i-get-around-httperror-403-insufficient-permission-gmail-api-python) – Andrew Dec 29 '16 at 06:28

2 Answers2

3

The reason why the permission is still insufficient is because the refresh_token in your file is still valid. The access_token and refresh_token you get when authenticating your users only have the scopes you specify at that time.

Just imagine if that was not the case. You could then ask your users for readonly and later add the all encompassing https://mail.google.com/-scope and do a lot of additional stuff without the user's consent.

Tholle
  • 108,070
  • 19
  • 198
  • 189
  • 1
    Yep, that makes perfect sense. I was using Google's example get_credentials function without putting any thought into what it was doing. – monguin Jan 08 '16 at 01:44
1

This has been answered more thoroughly here:

How do I get around HttpError 403 Insufficient Permission? (gmail api, python)

Even though your question deals with the google calendar API, the 403 error is similar to many of the google API apps.

Community
  • 1
  • 1
Andrew
  • 1,423
  • 1
  • 17
  • 26