How to log in as different user to Google API v3? - python

So I'm trying to create a new calendar, but i want to be able to specify what google account to create it in, assuming i have the credentials for said account, which i do. The code bellow creates it on the currently signed in user, or requires user interaction to allow access. Is there a way to specify an user and run the command on the background. I essentially just want to add a calendar to my account when the program runs, but i cant guarantee that my account will be logged in at the time.
I believe this was possible with the version 2 of the google api through ClientLogin, but i'm trying to use version 3.
import gflags
import httplib2
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
FLAGS = gflags.FLAGS
FLOW = OAuth2WebServerFlow(
client_id='MY_CLIENT_KEY',
client_secret='MY_CLIENT_SECRET',
scope='https://www.googleapis.com/auth/calendar',
user_agent='MyApp/v1')
storage = Storage('calendar.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
credentials = run(FLOW, storage)
http = httplib2.Http()
http = credentials.authorize(http)
service = build(serviceName='calendar', version='v3', http=http)
calendar = {
'summary': 'Test3',
'timeZone': 'America/New_York'
}
created_calendar = service.calendars().insert(body=calendar).execute()

With V3, you'll need to use a Service Account in order to act as the user. The process is described in the Google Drive documentation, you just need to use Calendar API v3 scopes and references instead of Google Drive API.

Another option would be to store the OAuth2 refresh token and use it to grab valid access tokens even if the user is not logged in. See my reply to google Calendar api v3 Auth only for first time

Related

Google Calendar API for a simple script

I'm trying to get a simple script (python) to work against the google calendar API and don't seem to be able to get the authentication to work properly. Its for a basic raspberry pi based thing I'm trying to make at home, and I need to just grab all the events from my google calendar.
I started with the code examples on the website, but because my script is totally server side, There was no way I could get the auth challenge step to work properly (it tried with lynx, but didn't work)
I then found examples with a service account - which seemed like the right thing as it didn't need the challenge flow. Furthermore, getting the creds_with_subject, seemed like the right approach to actually get a token related to the google account that owns the calendar. I think I got it all strung together, but then I don't actually get anything listed (as a start, I was using the calendar.list() endpoint to list the calendars out). The authentication step seemed to work (no errors) - but the ensuing list was empty. I'm guessing because my service account token wasn't associated/allowed to access my google account calendar. I've enabled the calendar API in the project to which the service account belongs.
Many of the guides I found seem to mention GSuite, which its not clear if I need
So summary questions:
For this type of usage - is the service account the right approach?
If so, do I need to associate this service account with my google account (that actually owns the calendar)?
It looks like the simple API key work doesn't work any more, but thought I'd check in case there is a simpler way?
do I need GSuite?
from __future__ import print_function
import datetime
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/calendar']
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
creds = service_account.Credentials.from_service_account_file("credentials.json", scopes=SCOPES);
creds_with_subjects = creds.with_subject("<my real account>#gmail.com");
print(creds);
print(creds_with_subjects);
service = build('calendar', 'v3', credentials=creds_with_subjects)
print(creds);
print("Getting Calendar list");
page_token = None
while True:
calendar_list = service.calendarList().list(pageToken=page_token).execute()
print(calendar_list);
page_token = calendar_list.get('nextPageToken')
if not page_token:
break
if __name__ == '__main__':
main()
Creating a service account is not enough. You also have to perform domain wide delegation and impersonate your user.
The main purpose of granting domain-wide authority to a service account is for these accounts to be able to access data on behalf of a user in your domain as otherwise the service account acts like just another account and it is trying to access its own data from Calendar, hence the empty response you are receiving.
To impersonate your user, you will have to follow the steps below:
Create a Credentials object from the service account's credentials and the scopes your application needs access to:
from google.oauth2 import service_account
SCOPES = ['SCOPE1', 'SCOPE2', ...]
SERVICE_ACCOUNT_FILE = '/path/to/service.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
Delegate domain-wide authority:
delegated_credentials = credentials.with_subject('YOURADDRESS#DOMAIN.COM')
However, in order to do this, you need to have a Google Workspace domain.
Reference
Using OAuth 2.0 for Server to Server Applications;
Authorizing Requests to the Google Calendar API.

Using Google Admin to view Drive files Domain-wide

I'm trying to list all Google Drive files Domain-wide, both users that still work here, and those that have moved on. With that, we can grep the output for certain terms (former customers) to delete customer-related files.
I believe I have a successful way to list all users using the Admin SDK Quickstart, since we have only about 200 total users (max is 500). I also have a way to list all files for a user using the Drive REST API's files.list() method. What I need to know is how to impersonate each user iteratively, in order to run the file listing script.
I have found the blurb .setServiceAccountUser(someone#domain.com) but I'm not really sure where to implement this, either in the service account authorization step, or in a separate middle-man script.
Have a look at https://github.com/pinoyyid/googleDriveTransferOwnership/blob/master/src/couk/cleverthinking/tof/Main.java
Specifically lines 285-299 which deal with generating a credential for an impersonated user.
GoogleCredential.Builder builder = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(serviceAccountEmailAddress)
.setServiceAccountPrivateKeyFromP12File(f)
.setServiceAccountScopes(Collections.singleton(SCOPE));
// if requested, impresonate a domain user
if (!"ServiceAccount".equals(impersonatedAccountEmailAddress)) {
builder.setServiceAccountUser(impersonatedAccountEmailAddress);
}
// build the Drive service
Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, null)
.setApplicationName("TOF")
.setHttpRequestInitializer(builder.build()).build();
This is Java, but should at least tell you what the steps are.
You need to implement the authorization flow for Service Accounts.
Once you create a service account in a GCP project (console.developers.google.com), enable DWD (domain-wide delegation), then authorize that service account in your G Suite admin console, that key can then be used to "impersonate" any account in the G Suite instance:
Create the credentials object from the json file
from oauth2client.service_account import ServiceAccountCredentials
scopes = ['https://www.googleapis.com/auth/gmail.readonly']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'/path/to/keyfile.json', scopes=scopes)
Create a credential that can impersonate user#example.org (could be any user in the domain though)
delegated_credentials = credentials.create_delegated('user#example.org')
Authorize the credential object (i.e. get an access_token)
from httplib2 import Http
http_auth = credentials.authorize(Http())
Call the Gmail API:
from apiclient import discovery
service = discovery.build('gmail', 'v1', http=http)
response = service.users().messages().list(userId='user#example.org').execute()

Google Calendar API - service account - without google apps domain

Is it possible to use Google Calendar API v3 with service accounts without having a google apps domain?
Basically what i want to do is create an event on my own calendar with a python script without having the script to prompt me for the user and password... anyone has any idea?
I used this code, but no luck:
import pprint
import pytz
import httplib2
import requests
from datetime import datetime, timedelta
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
with open('/home/xpto/service_account_certificate.pem', 'rb') as f:
key = f.read()
service_account_name = '000000000000-j...#developer.gserviceaccount.com'
credentials = SignedJwtAssertionCredentials(
service_account_name,
key,
scope=['https://www.googleapis.com/auth/calendar',
'https://www.googleapis.com/auth/calendar.readonly'],
sub='xpto#gmail.com')
http = httplib2.Http()
http = credentials.authorize(http)
service = build(serviceName='calendar', version='v3', http=http)
I get this error:
auth2client.client.AccessTokenRefreshError: unauthorized_client: Unauthorized client or scope in request
Yes - just create a service account and this will give you an email address for the account.
Then just share your calendar with that email address giving it whatever permissions it needs.
Granting access of google resources to the service account works. In my case, I was trying to use a service account to push files onto google drive. By granting access of the folder on my google drive to the service account (email), I was able to see files and create files on google drive under my account.

How to build a service for Google Calendar API using a service account?

I am using OAuth2.0 with a Google Service Account to try and manage a calendar. I have the sample code in Python provided below:
from oauth2client.client import SignedJwtAssertionCredentials
from httplib2 import Http
from apiclient.discovery import build
client_email = "<service_account_client_email>"
with open("key.p12") as f:
priv_key = f.read()
cred = SignedJwtAssertionCredentials(client_email, priv_key, "https://www.googleapis.com/auth/calendar", sub="<user_to_impersonate>")
http_auth = cred.authorize(Http())
service = build(serviceName='calendar', version='v3', http=http_auth)
So far, I get the exception "Requested client not authorized" when running the code.
I have followed the steps in Google's documentation:
Delegate domain-wide authority to your service account (I created the service account, but I am not an administrator for the domain. However, a user who is an administrator performed this step).
Verified that the scope is set to use the calendar, and the API is enabled in developer console.
Shared the calendar with the service account (though the option to manage the calendar is unavailable).
What steps am I missing? Is my scope incorrect just for building the service?

Google Contact API - Auth2.0

I'm looking for a good way to retrieve every emails address of my contacts from a google account for a "desktop" application in Python.
In a first time, I created an app via Google Code. I toggled Google Plus API, retrieving most of my user data, but not any of my contacts.
I started investigate, and I found a lot of stuff, but most of them was outdated.
I found a good way to retrieve my contacts, using gdata library but granting me a full read/write access on it, via https://www.google.com/m8/feeds with no feedback.
self.gd_client = gdata.contacts.client.ContactsClient(source='MyAppliName')
self.gd_client.ClientLogin(email, password, self.gd_client.source)
According to the official 'google contact api' google group, which migrated to stackoverflow, read only access is broken.
By the way, I'm not a huge fan of 'Trust my application, I use read only access, I swear."
I found the google api playground at https://developers.google.com/oauthplayground in which they use OAuth2.0 token with most of apis, including contact, toggling a webpage:
Google OAuth 2.0 Playground is requesting permission to:
Manage your contacts
According to this playground, it's possible to use OAuth2.0 with google contact api, but I have no idea how to add https:// www.google.com/m8/feeds to my scope, which doesn't appear on the list.
Is there an other way to do that ?
If this question is still open for you, here is some sample code how to use oauth2 and Google Contact API v3:
import gdata.contacts.client
from gdata.gauth import AuthSubToken
from oauth2client import tools
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
def oauth2_authorize_application(client_secret_file, scope, credential_cache_file='credentials_cache.json'):
"""
authorize an application to the requested scope by asking the user in a browser.
:param client_secret_file: json file containing the client secret for an offline application
:param scope: scope(s) to authorize the application for
:param credential_cache_file: if provided or not None, the credenials will be cached in a file.
The user does not need to be reauthenticated
:return OAuth2Credentials object
"""
FLOW = flow_from_clientsecrets(client_secret_file,
scope=scope)
storage = Storage(credential_cache_file)
credentials = storage.get()
if credentials is None or credentials.invalid:
# Run oauth2 flow with default arguments.
credentials = tools.run_flow(FLOW, storage, tools.argparser.parse_args([]))
return credentials
SCOPES = ['https://www.google.com/m8/feeds/', 'https://www.googleapis.com/auth/userinfo.email']
credentials = oauth2_authorize_application('client-secret.json', scope=SCOPES)
token_string = credentials.get_access_token().access_token
# deprecated!
# auth_token = AuthSubToken(token_string, SCOPES)
with open('client-secret.json') as f:
oauth2_client_secret = json.load(f)
auth_token = gdata.gauth.OAuth2Token(
client_id=oauth2_client_secret['web']['client_id'],
client_secret=oauth2_client_secret['web']['client_secret'],
scope=SCOPES,
user_agent='MyUserAgent/1.0',
access_token=credentials.get_access_token().access_token,
refresh_token=credentials.refresh_token)
client = gdata.contacts.client.ContactsClient(auth_token=auth_token)
query = gdata.contacts.client.ContactsQuery()
The request should look like:
https://accounts.google.com/o/oauth2/auth?
scope=https%3A%2F%2Fwww.google.com%2Fm8%2Ffeeds&
state=<myState>&
redirect_uri=<Redirect URI>&
response_type=code&
client_id=<my Client ID>&approval_prompt=force
This will obtain read/write access to the user's contacts.

Categories

Resources