Michael Terry
Michael Terry

Reputation: 992

Google service account can't impersonate GSuite user

from google.oauth2 import service_account
import googleapiclient.discovery

SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = 'GOOGLE_SECRET.json'

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

delegated_credentials = credentials.with_subject('[email protected]')

google_calendar = googleapiclient.discovery.build('calendar', 'v3', credentials=delegated_credentials)

events = google_calendar.events().list(
    calendarId='[email protected]',
    maxResults=10
).execute()

The result of the foregoing code is:

google.auth.exceptions.RefreshError: ('unauthorized_client: Client is unauthorized to retrieve access tokens using this method.', '{\n  "error" : "unauthorized_client",\n  "error_description" : "Client is unauthorized to retrieve access tokens using this method."\n}')

Domain-wide delegation is on. The service account client ID is authorized with the appropriate scopes in GSuite.

The service account works for normal credentials. It only doesn't work for delegated credentials.

I've tried different APIs (scopes) and different users in our domain.

I had a colleague try to code up a sample from scratch and he got the same thing.

Upvotes: 2

Views: 1535

Answers (1)

ScottMcC
ScottMcC

Reputation: 4460

I think you're problem is that your not authorising your credentials before making the call to the Calendar API but there is a couple of differences that you have that I use in my own implementation

  • Use the following import statements

    from oauth2client.service_account import ServiceAccountCredentials
    from apiclient import discovery
    
  • Use the from_json_keyfile_name method

  • Authorise the credentials and pass it as a http argument when building the service

Try this modification to your code:

from apiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials
import httplib2

SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = 'GOOGLE_SECRET.json'

credentials = ServiceAccountCredentials.from_json_keyfile_name(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

# Use the create_delegated() method and authorize the delegated credentials
delegated_credentials = credentials.create_delegated('[email protected]')  
delegated_http = delegated_credentials.authorize(httplib2.Http())
google_calendar = discovery.build('calendar', 'v3', http=delegated_http)

events = google_calendar.events().list(
    calendarId='[email protected]',
    maxResults=10
).execute()

I would also suggest setting calendarId='primary' in your API call so that you always return the primary calendar of the user who you currently have delegated credentials for.

For more information on authenticating using ServiceAccountCredentials see the following link: http://oauth2client.readthedocs.io/en/latest/source/oauth2client.service_account.html

Upvotes: 3

Related Questions