Mithun
Mithun

Reputation: 8067

Google Apps Script Error - Authorization is required to perform that action

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

Answers (2)

sojim2
sojim2

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

Mithun
Mithun

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

Related Questions