Is it possible to access the Google Admin Reports API via server to server Service Account authorization?
I am try to make a server to server call to the Google Admin API, following the tutorial here.
When setting domain-wide delegation, I added these scopes: https://www.googleapis.com/auth/admin.reports.usage.readonly, https://www.googleapis.com/auth/admin.reports.audit.readonly, as defined here.
I try making the API call like this, using the relevant PyPI packages:
creds = service_account.Credentials.from_service_account_file('credentials.json', scopes=SCOPES)
with build('admin', 'reports_v1', credentials=creds) as service:
response = service.activities().list(userKey='all', applicationName='login', maxResults=10).execute()
Which results in the following error:
googleapiclient.errors.HttpError: <HttpError 401 when requesting https://admin.googleapis.com/admin/reports/v1/activity/users/all/applications/login?maxResults=10&alt=json returned "Access denied. You are not authorized to read activity records.". Details: "[{'message': 'Access denied. You are not authorized to read activity records.', 'domain': 'global', 'reason': 'authError', 'location': 'Authorization', 'locationType': 'header'}]">
When I make the API call using a different credentials method, such as Desktop Application, the call works as expected. However, the first time I run it, I have to interact with it via browser to approve/authenticate the call. Because this code will be running on a server without user interaction, that is not desirable behavior.
As a note, the docs for the Admin API say
Your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
Based on the documentation for sever to server calls, I believe service accounts still qualify as OAuth 2.0, but I could be wrong in that assumption.
In Google Workspace domains, the domain administrator can grant third-party applications with domain-wide access to its users' data — this is referred as domain-wide delegation of authority. Perform Google Workspace Domain-Wide Delegation of Authority
The documentation even has a python example.
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
"""Email of the Service Account"""
SERVICE_ACCOUNT_EMAIL = '<some-id>#developer.gserviceaccount.com'
"""Path to the Service Account's Private Key file"""
SERVICE_ACCOUNT_PKCS12_FILE_PATH = '/path/to/<public_key_fingerprint>-privatekey.p12'
def create_reports_service(user_email):
"""Build and returns an Admin SDK Reports service object authorized with the service accounts
that act on behalf of the given user.
Args:
user_email: The email of the user. Needs permissions to access the Admin APIs.
Returns:
Admin SDK reports service object.
"""
credentials = ServiceAccountCredentials.from_p12_keyfile(
SERVICE_ACCOUNT_EMAIL,
SERVICE_ACCOUNT_PKCS12_FILE_PATH,
'notasecret',
scopes=['https://www.googleapis.com/auth/admin.reports.audit.readonly'])
credentials = credentials.create_delegated(user_email)
return build('admin', 'reports_v1', http=http)
Notice that user_email is the user you are acting on behalf of, not the service account email.
The error message Access denied. You are not authorized to read activity records. means that you have not properly set up delegation for the user. Contact your workspace admin and have them look into it.
Related
Example code
from google.oauth2 import service_account
import pygsheets
creds = service_account.Credentials.from_service_account_file(
'my/path/to/credentials.json',
scopes=('https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive'),
subject='account#mydomain.com'
)
pg = pygsheets.authorize(custom_credentials=creds)
pg.open_by_url('https://docs.google.com/spreadsheets/d/my_spreadsheet_id/edit#gid=my_sheet_id')
Problematic behaviour
Fails on the last line with Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
Expected behaviour
The last line provides an object for Google Sheets access.
Additional info
Domain-wide delegation is enabled for the service account, subject account is on the domain
Sheet is shared with the subject account
When I don't provide the subject and share the sheet with the service account directly, it works
Environment
python==3.6.9
pygsheets==2.0.3.1
google-auth==1.6.3
To consider when using domain-wide delegation
The domain-wide delegation is not enabled by default. To allow it you need to follow the steps described in the documentation.
Step: in the GCP console:
You need to activate the checkbox Enable G Suite Domain-wide Delegation for each service account you want to use for such purpose
To you use the service account to impersonate a user, you need to give the necessary permissions in the Admin console
In the Admin console:
Any scopes that the service account needs when impersonating a user have to be authorized in the admin console
For this go to Main menu menu> Security > API controls.
Add (if not already done) the service account of interest by its Client ID, provide it all the scopes it needs and authorize
You can modify the scopes anytime at a later stage if needed
I'm following this tutorial Using OAuth 2.0 for Server to Server Applications. I am trying to connect to the Gmail API using a service account.
The code I end up with looks like this:
from oauth2client.service_account import ServiceAccountCredentials
from httplib2 import Http
from apiclient.discovery import build
import json
scopes = ['https://www.googleapis.com/auth/gmail.readonly']
credentials = ServiceAccountCredentials.from_json_keyfile_name('******.json', scopes)
http_auth = credentials.authorize(Http())
service = build('gmail', 'v1', http=http_auth)
request = service.users().messages().list(userId='me')
response = request.execute()
print json.dumps(response, sort_keys=True, indent=2)
However, when I run this code, I get the following error:
googleapiclient.errors.HttpError:https://www.googleapis.com/gmail/v1/users/me/messages?alt=json returned "Bad Request">
Can someone help me understand where this error is coming from?
Think of a service account as a dummy user. It has a Google Drive account a google calendar account. What it doesn't to my knowlage is have a Gmail account.
Normally when you request data using a service account you have to grant the service account access to that data manually. In the case of google drive you can share a folder with the service account enabling it to access google drive. (you can also upload to its drive account but that's out of scope for this question)
There is no way to grant another user access to your Gmail account so there is no way to use a service account with a normal user Gmail account.
Note: If this is Not a normal user Gmail account and is in fact one based on google domains then you can grant the service account access to all the emails of the other users on the domain via the admin section.
Other wise you need to look into using Oauth2 to access gmail.
I have struggled to make this work but did half the job.
Actually I can only read messages from Gmail API, If I try to use the gmail.modify Scope I get an error:
HttpAccessTokenRefreshError: unauthorized_client: Unauthorized client or scope in request.
Here is my code:
# init gmail api
credentials_path = os.path.join(settings.PROJECT_DIR, 'settings/gmail_credential.json')
scopes = ['https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.modify']
credentials = ServiceAccountCredentials.from_json_keyfile_name(credentials_path, scopes=scopes)
delegated_credentials = credentials.create_delegated('my_account#gmail.com')
http_auth = delegated_credentials.authorize(Http())
gmail = build('gmail', 'v1', http=http_auth)
In my service account:
I have set all possibles rôles to my service account "......iam.gserviceaccount.com"
I activated DWD: DwD: Google Apps Domain-wide Delegation is enabled.
I have read somewhere that I need a google work account to give permission to my service account to use gmail.Modify on my my_account#gmail email account. Seems very hard way to just modify a message in an email.
I don't know what to do next.
Based from this documentation, you need to use the client ID from your "Developers Console" as the Client Name in the "Manage API client access" when you're setting your API scopes. Google API does not work as expected with your personal account #gmail.com. You should have organization domain account in Google in format you#your_organisation_domain.
Check these threads:
Google API Python unauthorized_client: Unauthorized client or scope in request
Google API OAuth2, Service Account, "error" : "invalid_grant"
I am try to access dbm api , I am authenticating the url using service account please find the sample code below
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
from httplib2 import Http
scopes =['https://www.googleapis.com/auth/doubleclickbidmanager']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'path/to/key/.jsonfile', scopes=scopes)
http_auth = credentials.authorize(Http())
body={}
dbm = build('doubleclickbidmanager', 'v1', http=http_auth)
print dbm
request = dbm.lineitems().downloadlineitems(body=body).execute()
print request
If I use oauth mechanism to authenticate the url the code is running properly, since I don't want user interaction, I need server to server mechanism so I used service account
Steps which I tried:
I have created the service account and downloaded the json key file and used in the code but when I try to run my code it throws the following error:
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/doubleclickbidmanager/v1/lineitems/downloadlineitems?alt=json returned "You are not authorized to use DoubleClick Bid Manager API. Please contact dbm-support#google.com.">
Please help , thanks in advance.
As others have said here, you want to log in to the DBM site and add your service account as a user:
Then, per this documentation, you can set up service account credentials using your client secrets json file. If you want that service account to be able to access reports you've created in DBM under your user account (what you log in with) you need to delegate domain-wide authority:
delegated_credentials = credentials.create_delegated('user#example.org')
http_auth = delegated_credentials.authorize(Http())
dbm = build('doubleclickbidmanager', 'v1', http=http_auth)
queries = dbm.queries().listqueries().execute()['queries']
A service account isn't you its a dummy user it has its on Google drive account for example, and by default it doesn't have access to any DoubleClick Bid Manager APIs. Service accounts need to be pre authorized to be able to access private data. So for it to be able to access your double click data you are going to have to grant it access.
Normally with any other API I would say you take the service account email address and add it as a user. I don't have access to double click so I am not even sure if you can add other users manually. They don't have anything in the documentation about service accounts kind of makes me think its not supported. Let us know if you manage to get it to work.
I'm getting an error while calling the Reports API:
<HttpError 403 when requesting https://www.googleapis.com/admin/reports/v1/usage/users/all/dates/2013-08-01?alt=json&maxResults=1 returned "Caller does not have access to the customers reporting data.">
Have anyone seen this error before? What am I missing?
I just can't see why this is showing or what I should be checking.
Regards.
EDIT:
Auth:
credentials = SignedJwtAssertionCredentials(
service_account_name='5163XXXXX#developer.gserviceaccount.com',
private_key=oauth2_private_key,
scope='https://www.googleapis.com/auth/admin.reports.usage.readonly')
# https://developers.google.com/api-client-library/python/guide/thread_safety
http = credentials.authorize(httplib2.Http())
service = apiclient.discovery.build('admin', 'reports_v1', http=http)
The actual call:
result = service.userUsageReport().get(
userKey='all',
date='2013-08-01',
maxResults=1).execute()
Other APIs just work fine with that service account.
'https://www.googleapis.com/auth/admin.reports.usage.readonly' has been properly added to OAuth2 domain config page.
Try:
credentials = SignedJwtAssertionCredentials(
service_account_name='5163XXXXX#developer.gserviceaccount.com',
private_key=oauth2_private_key,
scope='https://www.googleapis.com/auth/admin.reports.usage.readonly',
sub='super-admin#yourdomain.com')
when using Service Accounts with Admin SDK, you still need to act on behalf of an admin within the Google Apps instance.
Also, make sure that the Service Account Client ID has been granted rights to use the Reports API scopes in the Google Apps Control Panel. The Google Drive docs describe this process well, just sub in the Reports scopes instead.