I'm developing a Google App Engine app with Python. And I'm using:
- Google Calendar API v3 (to access a calendar in my own domain. So, this is Google Apps installed in my domain)
- Google APIs client library for Python.
- OAuth2 to authenticate users of my domain (name@mydomain.com)
I thought I had to use Service Accounts, because of this:
"If your App Engine application needs to call an API to access data owned by the application's project, you can simplify OAuth 2.0 by using Service Accounts"
Taken from https://developers.google.com/api-client-library/python/platforms/google_app_engine#ServiceAccounts
But I'm not sure if I misunderstood something. Is my scenario (GAE app trying to access Google Apps in my own domain) a candidate for Service Accounts?
I've tried several ways to handle OAuth2:
- With Service Accounts, as said
- With Python decorators provided by Google APIs client library for Python (OAuth2Decorator and OAuth2DecoratorFromClientSecrets)
In both cases, I get the same errors:
- Executing in my local machine: HttpError 401 when requesting https://www.googleapis.com/calendar/v3/calendars/primary/events?alt=json returned "Invalid Credentials" (I created the event as JSON object, following this: https://developers.google.com/google-apps/calendar/v3/reference/events/insert#examples). An error stack trace: https://dl.dropbox.com/u/61566717/output/local_error
- Deploying to GAE: Error 310 (net::ERR_TOO_MANY_REDIRECTS). The string "_ah/login_required?continue=" is appended at the end of the url several times.
May it be a problem with the Client ID/client secret or the Service Account parameters generated with API console? Should I re-create them?
I'm totally lost. Any clues?
Many thanks in advance
You don't need a service account, though using one may be useful. There are some tricky issues with service accounts on App Engine detailed in a reported issue with the library. Try playing around with the Google APIs explorer a bit and see if that helps clarify how to use the API.
As long as you authorize the application with an account that has access to those calendars, you will be able to access them, irrespective of whether or not this is on Google App Engine.
Using the OAuth2Decorator
is your best bet here. If you give a specific example I'd be happy to provide some code snippets for accomplishing the task.
See a similar question asked recently: How can I log in to an arbitrary user in appengine for use with the Drive SDK? This seems to be your use case, except you want to use the Calendar API instead of the Drive API.
UPDATE:
After reading your other post (which I would consider closing, if I were you), I have pieced together a sample that may help you understand how to use the decorator.
First, to use your credentials so your app can let user's authorize it:
from apiclient.discovery import build
import json
from oauth2client.appengine import OAuth2Decorator
import webapp2
decorator = OAuth2Decorator(
client_id='your_client_id',
client_secret='your_client_secret',
scope='https://www.googleapis.com/auth/calendar')
service = build('calendar', 'v3')
Then your main page will make sure your users are signed in and the @decorator.oauth_required
decorator will save the OAuth 2.0 tokens in your datastore.
class MainPage(webapp2.RequestHandler):
@decorator.oauth_required
def get(self):
# This will force the user to go through OAuth
self.response.write(...)
# show some page to them
On the page you display to them, you would likely have a form that POST
s to /add-event
and this AddEvent
handler will be able to use the token to make the request. Instead of using oauth_required
we use @decorator.oauth_aware
to allow graceful failure. If a user is detected in the request by the App Engine cookies from their browser session (which they will be if they POST
from a form), then your app will lookup the OAuth 2.0 credentials from your datastore before making the authenticated calendar request.
class AddEvent(webapp2.RequestHandler):
@decorator.oauth_aware
def post(self):
if decorator.has_credentials():
event_name = self.request.get('event-name')
some_event = {...} # Create event here
# Documented at
# https://developers.google.com/google-apps/calendar/v3/reference/events/insert
http = decorator.http()
# Using 'primary' will insert the event for the current user
request = service.events().insert(calendarId='primary', body=some_event)
inserted = request.execute(http=http)
self.response.write(json.dumps(inserted))
else:
self.response.write(json.dumps({'error': 'No credentials'})
Finally, to make sure all those routes work, you'll need to define routes for each handler and the OAuth 2.0 handler used by the decorator:
app = webapp2.WSGIApplication([
('/', MainPage),
('/add-event', AddEvent),
(decorator.callback_path, decorator.callback_handler())
],
debug=True)
Extra Reference:
https://developers.google.com/api-client-library/python/platforms/google_app_engine
https://developers.google.com/google-apps/calendar/v3/reference/events/insert
I really struggled in a recent effort to integrate Google Calendar. I wrote up my own documentation. Maybe it will help:
http://www.tqis.com/eloquency/googlecalendar.htm