5

I'm executing a Google apps script with my C# app about once every 1.5 minutes. The apps script moves content between spreadsheets, and edits sheets. I also use the Drive API.

My script runs fine over long periods, except for the fact that I get an authorization errors for 5 minutes every hour.

Here is my code that handles authorization:

class Authentication
{

    public static ScriptService ScriptsAuthenticateOauth(UserCredential credential)
    {
        try
        {

            ScriptService service = new ScriptService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "MyApp",
            });

           return service;
        }
        catch (Exception ex)
        {
            Console.WriteLine(DateTime.Now.ToString("HH:mm") + ": An authentication error occurred: " + ex.InnerException);
            return null;
        }

    }


    public static UserCredential getCredential(string clientId, string clientSecret, string userName)
    {

        string[] scopes = new string[] { DriveService.Scope.Drive,  // view and manage your files and documents
                                         DriveService.Scope.DriveAppdata,  // view and manage its own configuration data
                                         DriveService.Scope.DriveAppsReadonly,   // view your drive apps
                                         DriveService.Scope.DriveFile,   // view and manage files created by this app
                                         DriveService.Scope.DriveMetadataReadonly,   // view metadata for files
                                         DriveService.Scope.DriveReadonly,   // view files and documents on your drive
                                         DriveService.Scope.DriveScripts, // modify your app scripts
                                         ScriptService.Scope.Drive,

                                         "https://www.googleapis.com/auth/spreadsheets",
                                         "https://spreadsheets.google.com/feeds",
                                         "https://docs.google.com/feeds"};  
        return GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
                                                                                         , scopes
                                                                                         , userName
                                                                                         , CancellationToken.None
                                                                                         , new FileDataStore("Google.Sheet.Sync.Auth.Store")).Result;
    }

    public static DriveService DriveAuthenticateOauth(UserCredential credential)
    {
        try
        {


            DriveService service = new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "MyApp",
            });

            // Console.WriteLine("Auth success");


            return service;
        }
        catch (Exception ex)
        {

            // Console.WriteLine(ex.InnerException);
            Console.WriteLine(DateTime.Now.ToString("HH:mm") + ": An authentication error occurred: " + ex.InnerException);
            return null;

        }

    }
}

I get my services like this:

 var credential = Authentication.getCredential(CLIENT_ID, CLIENT_SECRET, Environment.UserName);
 DriveService driveService = Authentication.DriveAuthenticateOauth(credential);
 ScriptService scriptService = Authentication.ScriptsAuthenticateOauth(credential);

But around the end of the hour, the apps script call throws the following error:

Script error message: Authorization is required to perform that action.

Just to be clear, it works at all other times, just not in those 5 minutes near the end of the hour. I did activate Google Drive API on both my developers console, and under Resources > Advanced Google services... in the apps script editor.

So what is going on? Can this be fixed?

ziganotschka
  • 25,866
  • 2
  • 16
  • 33
Coert Grobbelaar
  • 627
  • 4
  • 14
  • 1
    seems you are passing an expired access token. if so read about oauth2 and how to handle refresh and access tokens. – Zig Mandel Oct 27 '15 at 13:43

3 Answers3

2

Upon doing further research, I found that the script just needed to be run from the Script Editor to prevent this error. This is the info I found:

Authorization is required to perform that action.

This error indicates that the script is lacking the authorization needed to run. When a script is run in the Script Editor or from a custom menu item an authorization dialog is presented to the user. However, when a script is run as a service, embedded with a Google Sites page, or run from a trigger the dialog cannot be presented and this error is shown. To authorize the script, open the Script Editor and run any function. To avoid this error, remember to run the script once in the Script Editor after adding new services or capabilities to your script.

Source: https://developers.google.com/apps-script/troubleshooting#common_errors

Another related improvement I made to my code, was to fetch a fresh copy of the credentials object before creating each of the 2 services: In other words, I changed this:

var credential = Authentication.getCredential(CLIENT_ID, CLIENT_SECRET, Environment.UserName);
DriveService driveService = Authentication.DriveAuthenticateOauth(credential);
ScriptService scriptService = Authentication.ScriptsAuthenticateOauth(credential);

to this:

var credential = Authentication.getCredential(CLIENT_ID, CLIENT_SECRET, Environment.UserName);
DriveService driveService = Authentication.DriveAuthenticateOauth(credential);
credential = Authentication.getCredential(CLIENT_ID, CLIENT_SECRET, Environment.UserName);
ScriptService scriptService = Authentication.ScriptsAuthenticateOauth(credential);

This by itself did not fix my problem, but it helps with avoiding OAuth quirks, and can prevent unnecessary token refreshes.

Community
  • 1
  • 1
Coert Grobbelaar
  • 627
  • 4
  • 14
0

You likely need a refresh token. Access tokens are set to live for only 3600s (1hr) generally from Google, while the refresh token lives much longer and is designed to let you easily get a new access token when the previous one expires. This may be a good place to start: Google API .NET Client information on token responses

Also, if you don't have a refresh token, but you do have an access token, you can revoke the previous access token and then authenticate again. The new response will have a refresh token if called correctly "make sure you are getting a "Bearer" token type).

There is more information about using the longer living refresh tokens here.

Leejay Schmidt
  • 1,193
  • 1
  • 15
  • 24
0

If your application fails "just in those 5 minutes near the end of the hour", you are most probably encountering this bug with the execution API.

Apparently, requests to the execution API with a token that will expire within 6 minutes give "Authorization is required..." errors. This seems to be linked to the fact that 6 minutes is the maximum run time of a script.

A workaround is to refresh your token in advance, if you know when it will expire (which is not the case on Android). Hopefully the bug will be fixed soon.

I am not sure if you managed to really fix your problem or if you just found a good workaround, but you might want to star that Apps Script issue so it can be fixed sooner.

personne3000
  • 1,780
  • 3
  • 16
  • 27