Delegation denied using Google Gmail API Python SDK - python

Intoduction
I am an administrator of Google Admin Workspace. What I want to do, is to get list of the user's Gmail labels.
Code
Here is how the code looks like:
def show_lables(email_from, service):
results = service.users().labels().list(userId=email_from).execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
return
print('Labels:')
for label in labels:
print(label['name'])
Here is the problem, I can get my own Gmail labels, but cannot get labels of other user (within one organization, same domain name).
So, I have read about Google Service Account, created and configured one. It works just find, because I tested it with other Google Services, but here is the code:
def init_services(client_secret_file, api_name, api_version, delegated_user=None):
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES[api_name])
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(client_secret_file, SCOPES[api_name])
creds = flow.run_local_server()
with open('token.json', 'w') as token:
token.write(creds.to_json())
if delegated_user is not None:
credentials = service_account.Credentials.from_service_account_file(
'service.json', scopes=SCOPES[api_name])
creds = credentials.with_subject(delegated_user)
try:
service = build(api_name, api_version, credentials=creds)
return service
except Exception as e:
print(f'An error occurred: {e}')
Issue
Basically, when I want to create service, I just call this function, and when I want to delegate credentials, I just add delegated_user email.
service = init_services(file_name, 'gmail', 'v1','admin#company.com')
show_lables('someuser#company.com', 'admin#company.com', service)
So, here is the issue - when I create service with delegated user and put my email as delegated user email - admin#company.com, and try to get labels of emails of other user (someuser#company.com) I get next error:
<HttpError 403 when requesting
https://gmail.googleapis.com/gmail/v1/users/someuser%40company.com/labels?alt=json
returned "Delegation denied for admin#company.com". Details: "[{'message':
'Delegation denied for admin#company.com', 'domain': 'global', 'reason':
'forbidden'}]">
As I said, I have configured Google Service Account, I have proper scope - https://mail.google.com/, but I still have no idea what is wrong.

Related

Gmail-API with python

i'm building python GMAIL API for checking emails for 10 email accounts
but in google documentation not very useful.
this seems only support one account
https://github.com/suleenwong/Gmail-API-Python
If we check the default sample python quickstart This sample was designed to be single user but that can be changed.
The following section creates a token.json file when the user authorizes the application. The file will contain the access token and refresh token for the user who authorized the code.
If the file does not exist then the app will prompt the user to authorize it. If it does then the app will load the credentials from that file and run the code with the authorization of that user.
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
To add more users you can simply rename that file tokenUserOne.json, tokenUserTwo.json. Then set it up so that you can supply the file name you want your script to run on. You will only need to authorize each user once. As long as you have a token.json file for each user separated, your app can then be started using which ever token file you want, to access each users data.
https://developers.google.com/gmail/api/quickstart/python -this is the proper documentation everything you need is in here
aka (you also need a client_secet.json file but you will have to get yourself for the google cloud dashbord):
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
return
print('Labels:')
for label in labels:
print(label['name'])
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()

Inserting Custom Dimensions using Google Analytics API v3

I am using Google Analytics API to insert a custom dimension. I have set up the required Oauth2 credentials and obtained an access token and a refresh token. However, when I called my function that inserts a custom dimension to a specified Account and webProperty in Google Analytics, I get an error message that reads as follows:
<HttpError 403 when requesting: URL
returned "Request had insufficient authentication scopes.". Details: "[{'message': 'Insufficient Permission', 'domain': 'global', 'reason': 'insufficientPermissions'}]">
I opened the URL and got the following error message
Error message
Using the same code, I am able to fetch all accounts and webProperties in Google Analytics but apparently it can neither insert nor update custom dimensions and I don't know what I am missing in my code.
Here is my code:
from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient.errors import HttpError
import os
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
scope = ['https://www.googleapis.com/auth/analytics']
def get_service():
"""
authorises user to access GA resources and builds a service object
"""
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', scopes=scope)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json', scopes=scope)
creds = flow.run_local_server(port=80)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
# build a service object
service = build('analytics', 'v3', credentials=creds)
return service
def insert_customDimension(service):
# Inserts a custom Dimension at a specific index.
try:
service.management().customDimensions().insert(
accountId=ACCOUNT_ID,
webPropertyId=WEB_PROPERTY_ID,
body={'name': 'free to use',
'scope': 'HIT',
'active': False,
'index': 200
}
).execute()
except HttpError as error:
print(error)
except TypeError as er:
print(er)
def main():
# Authenticate and construct service.
service = get_service()
insert_customDimension(service)
if __name__ == '__main__':
main()
I would like to understand what am I doing wrong or what do I need to change in my code? I have deleted the token.json file and ran the code but I was unable to complete the authorization flow and not token.json was automatically created. I manually obtained the refresh and access token and created the file. I have admin privileges to the Google Analytics account I am trying to insert custom dimensions into.

Gmail API: How to simply authenticate a User and get a list of their messages?

I'm trying to build a simple python script to access gmail's API and organize certain email messages inmy inbox into a csv file.
I see in the below documentation that accessing the messages is done using the user's (mine in this case) email address.
messages.list
I'm running into difficulty accessing the API. I'm getting the below error:
"Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project."
I was hoping to build a light weight script not a web application. Does anyone know if there is a way I can authenticate my own email address in a script?
PS: I suppose I could use selenium to automatically sign in but I was wondering if there was a way to do this using gmail's API.
You need to understand that the data you are trying to access is private user data. This is data owned by a user that being you, which means your application need to be "authorized" by the user "you" to access their data.
We do this with a method called Oauth2, it will allow your application to request consent for access to read the users emails in this case.
In order to use Oauth2 you must first register your applcation on Google Developer console and set up a few things, this will identify your application to Google.
All of this is explained in the Python quick-start for gmail. Once you have that working you should be able to change the code to use message.list instead of labels.list.
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail API
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
else:
print('Labels:')
for label in labels:
print(label['name'])
if __name__ == '__main__':
main()

PYTHON Login in with multiple account using Gmail api/imaplib

I'm currently working on an application requiring a gmail inbox. Currently i'm using the Gmail API with a credentials.json and a token.pickle and this is working fine although I want to login with other credentials each time the script is used. I'm quite new to this API and was wondering if it's possible to login with other credentials than the credentials used for the token.pickle.
Gmail API
To currently get a gmail inbox I use the following code:
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
global service
service = build('gmail', 'v1', credentials=creds)
Imaplib
Another way I've tried to get a certain user's inbox is with the imaplib and email library since it lets you login with a certain email address and password. But to allow this to work a user needs to allow less secure apps to their google account which is a hassle for each user to enable that. The code I used for imaplib is the following code:
import imaplib, email
username = 'USERNAME'
password = 'PASSWORD'
imap_url = 'imap.gmail.com'
con = imaplib.IMAP4_SSL(imap_url)
con.login(username, password)
con.select('Inbox')
Question
My question is: Is it possible with the gmail api from google to login with multiple/other credentials to get the corresponding gmail inbox? If this is not possible and I have to use the Imaplib way, what are the steps I need to take so it doesn't require users to enable less secure apps?
Just change the .pickle file name. This code possibilite do this just changing the user variable. There will be different tokens to each account
def api_connection(user):
scopes = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
if os.path.exists(f'{user}.pickle'):
with open(f'{user}.pickle', 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', scopes)
creds = flow.run_local_server(port=0)
with open(f'{user}.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
return service

Any way to use Google Api without every-time authentication?

I tried to use the API on python in a autorun on a PC. But I can't because every time the program starts, it asks me about the authorization code.
This is my code:
client_secret_file = "client_secret.json"
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
client_secrets_file, scopes)
credentials = flow.run_console()
youtube = googleapiclient.discovery.build(
api_service_name, api_version, credentials=credentials)
Any help? Thanks
Becouse you are using the YouTube API you can not use a service account you will need to use Oauth2.
Oauth2 can return something called a refresh token, if you store this token your code can then access the refresh token the next time it runs and use the refresh token to request a new access token this way it will not need to ask you every time it runs to access your data.
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
The only offical sample that includes refresh token that i know if is here Python quickstart you will need to alter it for YouTube but the auth code is really all you need just plug that int your code and you should be GTG
You can achieve this using Service Account.
https://googleapis.dev/python/google-api-core/latest/auth.html#service-accounts
Service account are technically pre authorized by the developer.
IF the API does not support Service account( in some cases) then you can try oauth2 where you have a consent for asking the owner of the account if you can access

Categories