How do I auth to Google API in AWS Lambda from Python

I have some simple Python code running as a REST service I use to automate the creation of Google calendar entries. The auth part of the code looks like this:

    store = file.Storage('token.json')
    creds = store.get()
    if not creds or creds.invalid:
        flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
        creds = tools.run_flow(flow, store)
    calendar_service = build('calendar', 'v3', credentials=creds)

I’m attempting to move this to an AWS Lambda function. The same code running as a Lambda function results in the following error.

[ERROR] OSError: [Errno 30] Read-only file system: 'token.json'
Traceback (most recent call last):
  File "/function/app-google-calendar.py", line 54, in handler
    insert_result = calendar_service.events().insert(calendarId=MENU_CALENDAR_ID, body=new_event).execute()
  File "/function/googleapiclient/_helpers.py", line 131, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/function/googleapiclient/http.py", line 922, in execute
    resp, content = _retry_request(
  File "/function/googleapiclient/http.py", line 190, in _retry_request
    resp, content = http.request(uri, method, *args, **kwargs)
  File "/function/oauth2client/transport.py", line 186, in new_request
    credentials._refresh(orig_request_method)
  File "/function/oauth2client/client.py", line 761, in _refresh
    self._do_refresh_request(http)
  File "/function/oauth2client/client.py", line 802, in _do_refresh_request
    self.store.locked_put(self)
  File "/function/oauth2client/file.py", line 85, in locked_put
    f = open(self._filename, 'w')

The root cause appears to be pretty clear. To wit, Lambda file system is read only. I however have been unable to find documentation or an example for how to do this oauth dance without requiring file system write access.

Answer

In the course of debugging this I ended up with some other strange errors, and in investigating those discovered that my root cause was a mismatch in architectures between my Mac and Lambda. I added --platform=linux/amd64 to my docker build command.

This still didn’t my original file system R/W issue with the JSON token file. /tmp is writable, but it looks like it is wiped clean at image startup, so putting the credential files there when building your image doesn’t work. I ended up adding the following to handler:

def handler(event, context):

    # Note moving token.json and credentials.json from same directory as python code to /tmp
    # because python code directory is not R/W and the Oauth dance to auth to google requires
    # the token file to be rewritten. Do this only if in lambda environment. We need to do this here and
    # not when the image is built, because /tmp seems to be wiped when the function image is starting
    if len(event) > 0:
        # We know we're in the lambda environment
        copyfile('/function/token.json', '/tmp/token.json')
        store = file.Storage('/tmp/token.json')
    else:
        # running locally
        store = file.Storage('token.json')