There're three types of google api client_id:
1. Web Application
2. Service Account
3. Installed application
I have used 3. Installed application successfullly base on oauth2client, but failed on 2. Service Account. I wanna access my own gmail inbox with oauth2.0 Credentials.
import imaplib
import json
import urllib2
from oauth2client.client import SignedJwtAssertionCredentials
from httplib2 import Http
from apiclient.discovery import build
import os
reldir = os.path.dirname(os.path.relpath(__file__))
CLIENT_SECRET_FILE = os.path.join(reldir, 'gmail_service.json')
OAUTH_SCOPE = "https://mail.google.com"
GMAIL_ADDRESS = 'my_gmail_address#gmail.com'
def jwt_oauth2():
'''
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
'''
with open(CLIENT_SECRET_FILE) as f:
data = json.loads(f.read())
private_key = data['private_key']
client_email = data['client_email']
credentials = SignedJwtAssertionCredentials(
client_email, private_key, scope=OAUTH_SCOPE)
http_auth = credentials.authorize(Http())
try:
gmail_service = build('gmail', 'v1', http=http_auth)
threads = gmail_service.users().messages().list(userId='me').execute()
except Exception as e:
return e
I got an exception same as the question. I encounter another exception while trying to add sub=GMAIL_ADDRESS into credentials:
AccessTokenRefreshError: unauthorized_client: Unauthorized client or scope in request.
I'm trying to figure out the problem, with credentials without sub:
>>> credentials = SignedJwtAssertionCredentials(
client_email, private_key, scope=OAUTH_SCOPE)
>>> http = credentials.authorize(Http())
>>> credentials.access_token
>>> credentials.refresh(http)
>>> credentials.access_token
u'ya29.pAGJjddCXjwsiHFN6hKU1yAkdWN7xMJbks5O76Pmrpe1hW1BbgwfZifjp81aDE55ALMVgjv-yBYiyQ'
>>> gmail_service = build('gmail', 'v1', http=http)
>>> request = gmail_service.users().messages().list(userId='me')
>>> response = request.execute()
{
"error": {
"errors": [
{
"domain": "global",
"reason": "failedPrecondition",
"message": "Bad Request"
}
],
"code": 400,
"message": "Bad Request"
}
}
Try to use credentials with sub:
>>> credentials = SignedJwtAssertionCredentials(
client_email, private_key, scope=OAUTH_SCOPE, sub=GMAIL_ADDRESS)
>>> http = credentials.authorize(Http())
>>> credentials.access_token
>>> credentials.refresh(http)
AccessTokenRefreshError: unauthorized_client: Unauthorized client or scope in request.
I found a similar question Google OAuth2 Service Account HTTP/REST Authentication, but I don't know much about node.js. Any help would be appreciated.
You should use the sub field where you specify the account which you want to impersonate.
Say, using your service Account, you want to get details of a user user#domain.com, the sub field should be populated as:
sub: user#domain.com
You should also make sure that you have given the service Account access to users in the domain.
Refer - https://developers.google.com/drive/web/delegation
Related
when I want to delete an event with a "XXXXXXX#group.calendar.google.com" with this command (below), it's work with my primary email but not with this "XXXXXXX#group.calendar.google.com":
from __future__ import print_function import httplib2 import os
from apiclient import discovery from oauth2client import file import
oauth2client from oauth2client import client from oauth2client import
tools
import datetime import requests
SCOPES = 'https://www.googleapis.com/auth/calendar' CLIENT_SECRET_FILE
= 'credentials.json' APPLICATION_NAME = 'Google Calendar API Python Quickstart' calendar_use = 'XXXXXXX#group.calendar.google.com'
def get_credentials():
"""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,'calendar-python-quickstart.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatibility with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
def deleteEvent():
get_credentials()
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('calendar', 'v3', http=http)
x = requests.delete('https://www.googleapis.com/calendar/v3/calendars/xxxxxxxxxxxxx#group.calendar.google.com/events/xxxxxxxxxxxxxxxxx')
print(x.text)
deleteEvent()
But i get this error message, and i can't find the answer.
{
"error": {
"code": 401,
"message": "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.",
"errors": [
{
"message": "Login Required.",
"domain": "global",
"reason": "required",
"location": "Authorization",
"locationType": "header"
}
],
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"service": "calendar-json.googleapis.com",
"method": "calendar.v3.Events.Delete"
When I open the link, which is write in the error code, I don't find the anwser
However, I can already create event, but i can't delete. Thanks for all
I guess you don't need to use request.delete (Because the request doesn't get auth credentials) but something like
service.events().delete(calendarId=calendarId, eventId=eventId).execute()
you just need to set up proper calendarId, eventId
I would like to get push notifications anytime a file is added to our shared drive by any user.
I have a service account authenticated with the Google Drive API calling the changes.watch() method on the shared drive, and I successfully get notifications if the shared drive name or icon changes, a file is created by the service account, or if files are modified by any user so long as the service account created the file. However, I do not get any push notifications for files created on this shared drive by other users - even though the page token increases and calling changes.list() will show me the changes that all users made on the shared drive.
I have the shared drive shared with the service account, and the service account also has domain-wide delegation for the scope https://www.googleapis.com/auth/drive and uses that scope in the API call.
Any help would be greatly appreciated as I cannot figure out what I'm lacking to get push notifications for all changes on this shared drive. Below is my code related to authenticating and subscribing to change notifications.
import urllib
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google.oauth2 import service_account
import uuid
from datetime import datetime
def get_service(api_name, api_version, scopes, key_file_location):
credentials = service_account.Credentials.from_service_account_file(
key_file_location)
scoped_credentials = credentials.with_scopes(scopes)
service = build(api_name, api_version, credentials=scoped_credentials)
return service
def get_start_page_token(service):
response = service.changes().getStartPageToken(
driveId = "REDACTED",
supportsAllDrives = True,
supportsTeamDrives = True
).execute()
startPageToken = response.get("startPageToken")
print(F'Start token: {startPageToken}')
return startPageToken
def subscribe_changes(service):
channel_id = str(uuid.uuid4())
body = {
"id": channel_id,
"type": "web_hook",
"address": "https://REDACTED"
}
response = service.changes().watch(
body=body,
pageToken = get_start_page_token(service),
includeItemsFromAllDrives = True,
supportsAllDrives = True,
supportsTeamDrives = True,
driveId = "REDACTED"
).execute()
ts = int(response['expiration'])
print("Expiration in UTC: " + datetime.utcfromtimestamp(ts / 1000).strftime('%Y-%m-%d %H:%M:%S'))
print(response)
return channel_id
# Define the auth scopes to request.
scope = 'https://www.googleapis.com/auth/drive'
key_file_location = 'credentials.json'
try:
# Authenticate and construct service.
service = get_service(
api_name='drive',
api_version='v3',
scopes=[scope],
key_file_location=key_file_location)
except HttpError as error:
print(f'An error occurred: {error}')
# subscribe to changes
channel_id = subscribe_changes(service)
print(f"Channel Id: {channel_id}")
I try my google API Project
from __future__ import print_function
import httplib2
from oauth2client import tools
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from apiclient import discovery
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# Set up a Flow object to be used if we need to authenticate. This
# sample uses OAuth 2.0, and we set up the OAuth2WebServerFlow with
# the information it needs to authenticate. Note that it is called
# the Web Server Flow, but it can also handle the flow for
# installed applications.
#
# Go to the Google API Console, open your application's
# credentials page, and copy the client ID and client secret.
# Then paste them into the following code.
FLOW = OAuth2WebServerFlow(
client_id='592262365202-h6qu4mrf2b043e2gv2qf5grjg0j53ádfasdfsf2s5.apps.googleusercontent.com',
client_secret='VVxrBm9SEádfsf1ONzyX1oIEwSfB2',
scope='https://www.googleapis.com/auth/contacts',
user_agent='contact_cms/YOUR_APPLICATION_VERSION')
# If the Credentials don't exist or are invalid, run through the
# installed application flow. The Storage object will ensure that,
# if successful, the good Credentials will get written back to a
# file.
def get_credentials(FLOW):
storage = Storage('info.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
credentials = tools.run_flow(FLOW, storage)
return credentials
# Create an httplib2.Http object to handle our HTTP requests and
# authorize it with our good Credentials.
def get_http(credentials):
http = httplib2.Http()
http = credentials.authorize(http)
return http
# Build a service object for interacting with the API. To get an API key for
# your application, visit the Google API Console
# and look at your application's credentials page.
def print_list_google_contact_with_number_of_contacts(creds, numberofcontact):
# Call the People API
service = build('people', 'v1', credentials=creds)
print('Ban dang in ra ', numberofcontact, ' danh ba')
results = service.people().connections().list(
resourceName='people/me',
pageSize=numberofcontact,
personFields='names,emailAddresses,phoneNumbers,emailAddresses,addresses').execute()
connections = results.get('connections', [])
# for person in connections:
# names = person.get('names', [])
# if names:
# name = names[0].get('displayName')
# print(name)
for person in connections:
names = person.get('names', [])
if names:
name = names[0].get('displayName')
else:
name = None
phoneNumbers = person.get('phoneNumbers', [])
if phoneNumbers:
phoneNumber = phoneNumbers[0].get('value')
else:
phoneNumber = None
Cities = person.get('addresses', [])
if Cities:
City = Cities[0].get('city')
else:
City = None
emails = person.get('emailAddresses', [])
if emails:
email = emails[0].get('value')
else:
email = None
print(f'{name}: {phoneNumber}: {City}: {email}')
def creat_a_google_contact(http):
service = discovery.build('people', 'v1', http=http, discoveryServiceUrl='https://people.googleapis.com/$discovery/rest')
service.people().createContact(parent='people/me', body={
"names": [
{
'givenName': "Samkafafsafsafit"
}
],
"phoneNumbers": [
{
'value': "8600086024"
}
],
"emailAddresses": [
{
'value': "samkit5495#gmail.com"
}
]
}).execute()
def main():
creds=get_credentials(FLOW)
http=get_http(creds)
printout=print_list_google_contact_with_number_of_contacts(creds,10)
creat_a_google_contact(http)
return print(printout)
if __name__ == '__main__':
main()
But when i run, have the log
Traceback (most recent call last):
File "/Users/nguyenngoclinh/cms/cmsproject/google_contacts_cms/setup_your_app.py", line 116, in <module>
main()
File "/Users/nguyenngoclinh/cms/cmsproject/google_contacts_cms/setup_your_app.py", line 113, in main
creat_a_google_contact(http)
File "/Users/nguyenngoclinh/cms/cmsproject/google_contacts_cms/setup_your_app.py", line 104, in creat_a_google_contact
'value': "samkit5495#gmail.com"
File "/Users/nguyenngoclinh/.conda/envs/1z_vietnam/lib/python3.7/site-packages/googleapiclient/discovery.py", line 840, in method
raise TypeError('Got an unexpected keyword argument "%s"' % name)
TypeError: Got an unexpected keyword argument "parent"
createContact() does not have a parent param. It has been deprecated:
https://developers.google.com/people/api/rest/v1/people/createContact
parent='people/me' is deprecated. Try without that.
So I have an object, which is credentials from an OAuth2 authorization for a web service. I want to save the users credentials so I can continue to use them in the future. I'm using Django.
The object is: <oauth2client.client.OAuth2Credentials object at 0x104b47310>
I'm not sure how I can stringify the credentials and then build the credentials object back from a string.
Sample code as requested:
#!/usr/bin/python
import httplib2
from apiclient import errors
from apiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
# Copy your credentials from the console
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
# Check https://developers.google.com/webmaster-tools/search-console-api-original/v3/ for all available scopes
OAUTH_SCOPE = 'https://www.googleapis.com/auth/webmasters.readonly'
# Redirect URI for installed apps
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
# Run through the OAuth flow and retrieve credentials
flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE, REDIRECT_URI)
authorize_url = flow.step1_get_authorize_url()
print 'Go to the following link in your browser: ' + authorize_url
code = raw_input('Enter verification code: ').strip()
credentials = flow.step2_exchange(code)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
http = credentials.authorize(http)
webmasters_service = build('webmasters', 'v3', http=http)
# Retrieve list of properties in account
site_list = webmasters_service.sites().list().execute()
# Filter for verified websites
verified_sites_urls = [s['siteUrl'] for s in site_list['siteEntry']
if s['permissionLevel'] != 'siteUnverifiedUser'
and s['siteUrl'][:4] == 'http']
# Printing the URLs of all websites you are verified for.
for site_url in verified_sites_urls:
print site_url
# Retrieve list of sitemaps submitted
sitemaps = webmasters_service.sitemaps().list(siteUrl=site_url).execute()
if 'sitemap' in sitemaps:
sitemap_urls = [s['path'] for s in sitemaps['sitemap']]
print " " + "\n ".join(sitemap_urls)
You can use pickle module to serialize and de-serialize python objects. Here is untested code:
import pickle
# Store OAuth2Credentials to a file
with open(FILENAME, 'wb') as credentials_file:
pickle.dump(credentials, credentials_file)
# Read OAuth2Credentials from file
with open(FILENAME, 'rb') as credentials_file:
credentials = pickle.load(credentials_file)
I'm trying to get a connection to my gmail inbox via the python API.
I've got a working example for connecting to the directory api to do user maintenance operations that is set up and working nearly identically to this code. So I've been through all the dev console and security setup for my service account user, who has an email box with a test email sitting in it waiting for me to read it with this code. Currently, my issue is that the line:
gmail_service = build('gmail', 'v1', http=http)
throws:
File "./gmail_test.py", line 29, in <module>
gmail_service = build('gmail', 'v1', http=http)
NameError: name 'http' is not defined
Not quite sure what I'm missing as the nearly identical tailored for admin sdk access works great.
anyway, here's the code:
import urllib2
from httplib2 import Http
from googleapiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
client_email = '<private>#developer.gserviceaccount.com'
with open("Unsubscribe Processing.p12") as f:
private_key = f.read()
creds = SignedJwtAssertionCredentials(
client_email,
private_key,
{
'https://www.googleapis.com/auth/gmail.readonly'
},
sub='serviceaccount#ourdomain.com')
auth = creds.authorize(Http())
gmail_service = build('gmail', 'v1', http=http)
results = gmail_service.users().labels().list(userId='serviceaccount#ourdomain.com').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()
You haven't defined what the variable http is.
From https://developers.google.com/gmail/api/auth/web-server, this seems to be how you create a Gmail service object:
http = httplib2.Http()
http = creds.authorize(http)
gmail_service = build('gmail', 'v1', http=http)