Google Scripts OAuth2.0 library throws invalid state token error when another google user tries to use the script

I haven't been able to find any answers to this problem, and it's probably because the answer is really obvious and I just can't see it.

I wrote a Google apps script that will call an external API and use that data to update a Google Sheet. It uses the OAuth2.0 library to authenticate the call. When authorization is needed, it opens up a sidebar with an authorization link to click, which then reaches out to retrieve an access token. It works just fine when I run the function, but I'd like at least one other user to be able to do it too, just in case I'm ever out of the office.

If I log in as another Google user and click the authorization link, I get an error page saying "The state token is invalid or has expired. Please try again." The URL of that error page contains the redirect URI with the state parameter appended to it. If I log back into the sheet with my normal account and reload that URL, the script completes no problem.

I've made sure that the account I'm testing with has edit access to the Sheet. In the deployment settings for the script, I've set it to execute as the "User accessing the web app" and allowed anyone with a google account access. I can't think of anything else I can do to allow another google user to run this script. My code is below.

var userProperties = PropertiesService.getUserProperties();

var sheetID = '{{sheet ID}}';

var subDomain = userProperties.getProperty('subDomain');
var clientID = userProperties.getProperty('clientID');
var clientSecret = userProperties.getProperty('clientSecret');
var mid = userProperties.getProperty('mid');


function getSFservice() {
  var authURL = 'https://'+subDomain+'.auth.marketingcloudapis.com/v2/authorize';
  var tokenURL = 'https://'+subDomain+'.auth.marketingcloudapis.com/v2/token';
  var redirectURI = 'https%3A%2F%2Fscript.google.com%2Fmacros%2Fd%2F{{scriptID}}%2Fusercallback';
  var responseType = 'code';

  return OAuth2.createService('sfapi')
    .setAuthorizationBaseUrl(authURL)
    .setTokenUrl(tokenURL)

    .setClientId(clientID)
    .setClientSecret(clientSecret)

    .setCallbackFunction('authCallback')

    .setPropertyStore(PropertiesService.getUserProperties())

    .setParam("account_id", mid)

    .setParam('state', getStateToken('authCallback'))
}

function checkAccess() {
  var sfService = getSFservice();
  if (!sfService.hasAccess()) {
    showSidebar();
  }
}

function showSidebar() {
  var sfService = getSFservice();
  if (!sfService.hasAccess()) {
    var authorizationUrl = sfService.getAuthorizationUrl();
    var template = HtmlService.createTemplate(
        '<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
        'Reopen the sidebar when the authorization is complete.');
    template.authorizationUrl = authorizationUrl;
    var page = template.evaluate();
    SpreadsheetApp.getUi().showSidebar(page);
  } 
}

function makeRequest() {

  var sfService = getSFservice();

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
  var lastUpdated = Utilities.formatDate(sheet.getRange(sheet.getLastRow(), 1).getValue(), "GMT", "MM/dd/yyyy");
  var yesterday = new Date;
  yesterday.setTime(yesterday.getTime() - (1000 * 60 * 60 * 24));
  yesterday = Utilities.formatDate(yesterday, "GMT", "MM/dd/yyyy");

  if (lastUpdated != yesterday) {
    
    checkAccess();
      
       /*Send and parse XML Payload*/
    
    logout();
}

function authCallback(request) {
  var sfService = getSFservice();
  var isAuthorized = sfService.handleCallback(request);
  if (isAuthorized) {
    makeRequest();
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}

function getStateToken(callbackFunction){
  var stateToken = ScriptApp.newStateToken()
     .withMethod(callbackFunction)
     .withTimeout(120)
     .createToken();
 return stateToken;
}

function logout() {
  var sfService = getSFservice();
  sfService.reset();
}

EDIT: I have discovered one of the reasons I am dumb. Clicking the authorization link from the sidebar wasn't working. So I tried opening the script editor and running the script while logged into my test account. It showed me a pop-up message warning me that the script was requesting access to my Google account. Once I clicked to proceed, I was able to run the script successfully.

So now my question is, is there any way for me to present that warning and request access without the user needing to open up the script editor? Can I do it from the sidebar where I show the auth URL?



Read more here: https://stackoverflow.com/questions/67007809/google-scripts-oauth2-0-library-throws-invalid-state-token-error-when-another-go

Content Attribution

This content was originally published by K K at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: