0

To complete refreshing of a cached oAuth token (for the project's service account) before subsequent GAS code executes, I have attempted using a while-loop as suggested in an answer to this SO post. But if the webapp script has been idle for longer than the token lifespan, an error still occurs -- the while-loop has not prevented subsequent code (that requires the token) from executing before the token has finished refreshing. Here is the code, where the function getToken() is called to generate the token and cache it:

 var scrCache=CacheService.getScriptCache();
 var clientToken=scrCache.get("clientToken");
 var tokenExpire = scrCache.get("tokenExpire");
 var timeLeft = tokenExpire - ((new Date()).getTime()/1000)

    //check if token expired or expires soon
 if(!clientToken || timeLeft< 400){
   var expToken = tokenExpire;
   var updateToken=getToken();//generate the new token and save to cache
   var start = new Date();
    // run while-loop until cache is refreshed; limit loop to maximum 15000 milliseconds = 15 seconds, to prevent endless loop
 while (scrCache.get("tokenExpire") === expToken && !isTimeUp(start, 15000)){}
  }

  // function to prevent endless while-loop
 function isTimeUp(startTime, milliSeconds){
  var now = new Date();
  return now.getTime() - startTime.getTime() > milliSeconds
}
   //function to generate and cache token
function getToken(){
  var clientJSON= JSON.parse(PropertiesService.getScriptProperties().getProperty("clientKey")); 
  var key = clientJSON.private_key;
  var clientEmail = clientJSON.client_email;
  var fetchScope=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/script.external_request"];
  var serverToken = new init(key, fetchScope, clientEmail);
 serverToken.addUser(clientEmail)
    .requestToken();
 var objectToken= serverToken.getTokens()[clientEmail];
 scrCache.put("clientToken", objectToken.token, 3700); //cache token for multiple uses before it expires
 scrCache.put("tokenExpire", objectToken.expire, 3700);
 return scrCache.get("clientToken");
}

QUESTION: This isn't working when token has expired (script has been idle for more than an hour)--how should the oAuth token be generated and cached so that the first user after the script has been idle for more than an hour (so token is expired) does not get an error?

UPDATE: Pending answers to this question with better solutions, I have eliminated the while-loop and simply execute the getToken() function when the script's "if-test" finds the token expired or expiring soon. But in addition, I have set up a once/hour timed-trigger to execute the getToken() function, resetting the clientToken values. Conceivably a user might still get an error (if I understand, the trigger timing is somewhat random), but that should be very rare.

DougMS
  • 45
  • 5
  • To give this a bit of more context, are you using a service account with its JSON key or the OAuth workflow from Google/third party?? Have you looked into the ScriptApp service or [OAuth2 for Apps Script](https://github.com/googleworkspace/apps-script-oauth2) ? – Yancy Godoy Mar 22 '22 at 00:08
  • @YancyGodoy Thanks for the outreach. The project spreadsheet is shared to the Google webapp project's service account. A Google OAuth2 authentication token is generated for the service account and used in authenticating the UrlFetchApp connection with the spreadsheet. This works fine. The problem with refreshing the token, I believe, is the async nature of generating the token and lack of an async functionality in Google's V8 API. That's how I've come to use the timed trigger to generate the token. I would be happy to learn about a better method. – DougMS Mar 22 '22 at 02:18
  • What about trying the suggestion given in [this answer](https://stackoverflow.com/questions/53824779/asynchronous-execution-of-a-function-app-script) and also maybe trying the [library suggested](https://github.com/tanaikech/RunAll) ? I also created this to replicate an [async function](https://drive.google.com/file/d/1NuLuuSR1iPAcgiMPqd1ij5WrlRdxrpeE/view?usp=sharing) to test viability. Unfortunately, since the code shared lacks other information not included I am unable to replicate and test, but you can look into those resources and share the outcome. – Yancy Godoy Mar 25 '22 at 01:46
  • @YancyGodoy--Thank you very much for looking more at this. My experience with async is same as this comment in the "answer" you refer to: "You can declare them asynchronous.. but they still execute syncronously so this wont really solve the problem.. Gotta love App Scripts. See my answer in stackoverflow.com/questions/31241396/… – Steven de Salas Mar 25, 2021 at 12:34 " Using timed triggers , etc., introduces complications and using FetchAll is not applicable. My "Update" approach is simple and working well. So unless a problem, I'll just go with that. Thanks again for your time! – DougMS Mar 25 '22 at 22:09

0 Answers0