Reputation: 8067
I have created an Apps Script that replaces token in Google Doc. This App Script has been deployed as API Executable. I am able to run a function in the apps script editor without any authorization error. But, it fails sometimes with Authorization error when invoked from my java web application. I get the following error:
{
"name": "replaceTokensInDoc",
"done": true,
"error": {
"code": 3,
"message": "ScriptError",
"details": [
{
"@type": "type.googleapis.com/google.apps.script.v1.ExecutionError",
"errorMessage": "Authorization is required to perform that action.",
"errorType": "ScriptError"
}
]
}
}
I have read in multiple places that I need to just run a function in Script Editor and provide permission to solve this issue, but it hasn't helped in my case. When I run a function in the editor, it doesn't even show the authorization dialog, which means it has all the necessary permissions. It only fails sometimes. Could somebody let me know the reason for this strange behavior?
Upvotes: 3
Views: 14858
Reputation: 1307
python solution for execute.py sample (https://developers.google.com/apps-script/api/how-tos/execute#step_4_make_the_scriptrun_request)
Basically if the expiry time is less than 361 seconds, then refresh the token
from __future__ import print_function
from googleapiclient import errors
from googleapiclient.discovery import build
#from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file as oauth_file, client, tools
import sys
import datetime
def main():
"""Runs the sample.
"""
SCRIPT_ID = 'YOUR SCRIPT ID'
# Setup the Apps Script API
SCOPES = [
'https://www.googleapis.com/auth/script.external_request'
,'https://www.googleapis.com/auth/spreadsheets'
,'https://www.googleapis.com/auth/drive'
,'https://www.googleapis.com/auth/drive.scripts'
,'https://www.googleapis.com/auth/userinfo.email'
]
store = oauth_file.Storage('token.json')
creds = store.get()
now = datetime.datetime.utcnow()
secondsDiff = (creds.token_expiry-now).total_seconds()
print(secondsDiff)
if secondsDiff < 361:
http = creds.authorize(Http())
creds.refresh(http)
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('script','v1',credentials=creds)
# Create an execution request object.
request = {"function": "doGet", "parameters": [{"sheetId" : sys.argv[1]}]}
try:
# Make the API request.
response = service.scripts().run(body=request,
scriptId=SCRIPT_ID).execute()
if 'error' in response:
# The API executed, but the script returned an error.
# Extract the first (and only) set of error details. The values of
# this object are the script's 'errorMessage' and 'errorType', and
# an list of stack trace elements.
error = response['error']['details'][0]
print("Script error message: {0}".format(error['errorMessage']))
if 'scriptStackTraceElements' in error:
# There may not be a stacktrace if the script didn't start
# executing.
print("Script error stacktrace:")
for trace in error['scriptStackTraceElements']:
print("\t{0}: {1}".format(trace['function'],
trace['lineNumber']))
#else:
# # The structure of the result depends upon what the Apps Script
# # function returns. Here, the function returns an Apps Script Object
# # with String keys and values, and so the result is treated as a
# # Python dictionary (folderSet).
# folderSet = response['response'].get('result', {})
# if not folderSet:
# print('No folders returned!')
# else:
# print('Folders under your root folder:')
# for (folderId, folder) in folderSet.iteritems():
# print("\t{0} ({1})".format(folder, folderId))
except errors.HttpError as e:
# The API encountered a problem before the script started executing.
print(e.content)
if __name__ == '__main__':
main()
Upvotes: 1
Reputation: 8067
The reason for this error is Google Apps Script return the Authorization error when the access token expires_in
time is less than 6 minutes. The maximum time allowed for Apps Script execution is 6 minutes, so it wants to make sure that the token doesn't get expired while the Apps Script is being executed. This is a major issue in production. This should have been published in bold in the documentation.
There is an issue already created in the Google Issue tracker. Please star it if you are facing the same issue.
https://issuetracker.google.com/issues/36762863
The workaround, until the issue is resolved, is to refresh the token if the token expires in less than 360 seconds.
if (credential.getExpiresInSeconds() <= 360) {
credential.refreshToken();
}
Upvotes: 7