googleworkspace / apps-script-oauth2

An OAuth2 library for Google Apps Script.
https://developers.google.com/apps-script/
Apache License 2.0
1.56k stars 429 forks source link

Error: Access not granted or expired. (line 454, file "Service", project "OAuth2") #243

Closed thatvalis closed 4 years ago

thatvalis commented 4 years ago

Hi,

I had set up OAuth2 flow according to the guides here, and it has been working flawlessly for months. Then at September 16th, all my scripts with the same OAuth2 flow (different service accounts) started failing with the error in the title: Error: Access not granted or expired. (line 454, file "Service", project "OAuth2")

Did something change in how I need to use OAuth2 in Apps Script? I have looked at the service accounts in Google Console but cannot find anything out of the ordinary. Any help would be appreciated.

Script in Google Apps Script:

var JSONcred = {
  "private_key": "-----BEGIN PRIVATE KEY-----xxx----END PRIVATE KEY-----\n",
  "client_email": "xxx@yyy.iam.gserviceaccount.com",
  "client_id": "abcdef",
};

function getOAuthService() {
    return OAuth2.createService("postsheets")
        .setTokenUrl('https://accounts.google.com/o/oauth2/token')
        .setPrivateKey(JSONcred.private_key)
        .setIssuer(JSONcred.client_email)
        .setPropertyStore(PropertiesService.getScriptProperties())
        .setParam('access_type', 'offline')
        .setScope('https://www.googleapis.com/auth/spreadsheets');
}

function reset() {
    var service = getOAuthService();
    service.reset();
}

function appendSheet() {
  var service = getOAuthService();
  var headers = {
    "Accept": "application/json",
    "Authorization": "Bearer " + service.getAccessToken(),
};

  var date = Utilities.formatDate(new Date(), "GMT+1", "YYYY-MM-dd");
  var ss = SpreadsheetApp.getActiveSpreadsheet(); 
  var sheet = ss.getSheetByName("SHEETNAME"); 
  var range = sheet.getRange("A:B").getValues();

  var A = []
for (var j in range){
  A.push(range[j][0])
}
   var B = []
for (var j in range){
  B.push(range[j][1])
}

  var range = "FRONTEND!A1";

  var payload = {
    "range": range,
    "values": [
          A,B
        ],
    "majorDimension": "COLUMNS"  
  }

  var options = {
  "method" : "PUT",
  "contentType": "application/json",
  "muteHttpExceptions": true,
  "headers" : headers,
  "payload" : JSON.stringify(payload)

};

  var url = 'https://sheets.googleapis.com/v4/spreadsheets/XXXXXX/values/' + range + '?&valueInputOption=USER_ENTERED'
  var response = UrlFetchApp.fetch(url,options);

  Logger.log(response);

}
pierresteiner commented 4 years ago

We have the exact same problem. It started at the same time !

// ---  Configures the service token  ---   //
function getService(subject) {
  return OAuth2.createService('Delegation : '+ subject)

        // Set the endpoint URLs.
        .setTokenUrl('https://accounts.google.com/o/oauth2/token')

        // Set the private key and issuer, come from the service account create in https://console.developers.google.com.
        .setPrivateKey(PRIVATE_KEY)
        .setIssuer(CLIENT_EMAIL)

        //With the old Email Setting API you have to pass an admin account to acces to emailsettings/2.0/
        //You don't have to do this with the new one
        .setSubject(subject)
        // Set the property store where authorized tokens should be persisted.
        .setPropertyStore(PropertiesService.getScriptProperties())
        .setScope('https://www.googleapis.com/auth/gmail.settings.basic https://www.googleapis.com/auth/gmail.settings.sharing');
}
erickoledadevrel commented 4 years ago

You can get more information on why the access token retrieval failed by looking at the output of Service.getLastError(). That method returns the error message that was returned when Service.hasAccess() or Service.getAccessToken() fails.

supanafuku commented 4 years ago

We have the exact same problem. I used Service.getLastError() to check the content.

It contained the following. Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.

Is there anything I can do for you?

denisakov commented 4 years ago

I have been using the QuickBooks.gs pretty much as it is for a couple of clients and exactly on Sep 16 people started to get error: "You have exceeded the property storage quota. Please remove some properties and try again. (line 113, file "Storage", project "OAuth2")". I haven't been able to identify the source, but I think it must be related to the issue above somehow.

function getService() {
  return OAuth2.createService('Quickbooks')
      .setAuthorizationBaseUrl(BASE_AUTH_URL)
      .setTokenUrl(TOKEN_URL)
      .setClientId(CLIENT_ID)
      .setClientSecret(CLIENT_SECRET)
      .setScope(API_SCOPE)
      .setCallbackFunction('authCallback')
      .setParam('response_type', RESPONSE_TYPE)
      .setParam('state', getStateToken('authCallback'))
      .setPropertyStore(PropertiesService.getScriptProperties());
}

If I delete .setPropertyStore(PropertiesService.getScriptProperties()) and just use .setCache(CacheService.getScriptCache()) it works, but it is not a solution since cache expires.

Urgent help of knowledgable people is needed. I couldn't find any other mention of the trouble anywhere.

pierresteiner commented 4 years ago

I wonder if it is not linked to that: https://support.google.com/cloud/answer/9110914?hl=en#:~:text=Like%20sensitive%20scopes%2C%20restricted%20scopes,screen%20configuration%20verified%20by%20Google

We are using now restricted scope, and the documentation was updated on the same day it started to fail. From my understanding our app should not be affected, as it is 100% internal (and using google wide domain delegation) but the coincidence is uncanny

thatvalis commented 4 years ago

Good find! This could be it. My scripts are also only internal. I have completed the "Oauth consent screen" steps from the API & Services dashboard - it's going to take a few days it says but I will update here if that fixes the issue.

erickoledadevrel commented 4 years ago

It does seem likely at this point that the errors are due to a change in policy and/or configuration, and are not related to this library (which hasn't changed in months). I'll close this issue for now, but reopen if you find that a code change is required.

I also wouldn't have expected the restricted scopes policy to affect domain-wide delegation with service accounts, but the timing is suspect. Please report back if you find that getting approval resolves the issue.

@denisakov - That sounds like a different issue completely, please open a new ticket to track that.

DanW-iX commented 4 years ago

Same issue started today for me. I do not have this issue when using a different app script environment using the same GCP project creds.

DanW-iX commented 4 years ago

@denisakov I had the same error as you. I am not sure what actually fixed it but I did the following when I was troubleshooting: 1) upgraded app scripts runtime to v8 - https://developers.google.com/apps-script/guides/v8-runtime/migration 2) Went to File -> Project Properties -> Script Properties and deleted what was in there 3) Ran service.getAuthorizationUrl() and authorised

I think there might have been a Google-side issue interacting with script properties on the old runtime?

thatvalis commented 4 years ago

Hi again,

I just tried enabling V8 Runtime and now it works.

denisakov commented 4 years ago

It seems like enabling V8 works for me as well. Someone at Google messed up the old environment indeed.

erickoledadevrel commented 4 years ago

I haven't been able to repro this problem exactly, but there does seem to be something going on. I have a different script that uses the OAuth1 library that started failing at the same time, but I haven't been able to trace down why. Given that the code works correctly in the v8 engine, I'm still working on the assumption that there isn't anything that needs to be changed in this library.

henricook commented 3 years ago

I've had this issue after changing client secret/client id - i can't login with the new values image

erickoledadevrel commented 3 years ago

@henricook - That is likely working as expected. If you change client IDs you'll need to need to re-authorize access.

henricook commented 3 years ago

Yep absolutely right, it turned out to be an error in my code, apologies. My code was assuming a valid access token where none existed. For some reason during testing I'd inverted the if (!driveService.hasAccess()) { clause from the examples in the README

itsazzad commented 1 year ago

I am getting the same error!

ranvids commented 1 year ago

me too, same error

ShahrearBinAmin commented 1 year ago

@erickoledadevrel Getting same error!