googleworkspace / apps-script-oauth2

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

Access not granted or expired. (line 352, file "Service", project "OAuth2") #76

Closed Peter-Valenta closed 7 years ago

Peter-Valenta commented 7 years ago

Sorry, I dont know if this is the correct forum for receiving support. Please let me know if this is out of line.

Im using your Oauth2 library from within google sheets to access google search console data via the webmaster v3 API. Ive implemented the code as closely as I could to your examples, but im running into the following error when making requests: Access not granted or expired. (line 352, file "Service", project "OAuth2") .

Validation with getService() seems to work fine. Ive set up my project to use OAuth 2.0 client ID credentials with: Application type: Web application Authorised JavaScript origins: https://script.google.com, https://docs.google.com & https://google.com Authorised Redirect URIs: https://script.google.com/macros/d/{SCRIPT ID}/usercallback

I hope someone here can help me out. Here is my code (very closely copied from the examples).

var restBasePath = 'https://www.googleapis.com/webmasters/v3/';

var scopeWebmasters = 'https://www.googleapis.com/auth/webmasters';
var scopeWebmastersReadOnly = 'https://www.googleapis.com/auth/webmasters.readonly';

var myProjectScopes = scopeWebmasters + ' ' + scopeWebmastersReadOnly;
var myClientId = '{CLIENT ID}';
var myClientSecret = '{CLIENT SECRET}';

/** (0.1)
 * Logs the redict URI to register.
 */
function logRedirectUri() {
  var service = getService();
  Logger.log(service.getRedirectUri());
}

/** (0.2)
 * Resetting the access token
 *
 * If you have an access token set and need to remove it from the property store you can remove it with the reset() function. 
 *   Before you can call reset you need to set the property store.
 */
function clearService(){
  OAuth2.createService('myservice')
      .setPropertyStore(PropertiesService.getUserProperties())
      .reset();
}

/** (1.)
 * Creates a OAuth2 service
 *
 * The OAuth2Service class contains the configuration information for a given OAuth2 provider, including its endpoints, 
 *   client IDs and secrets, etc. This information is not persisted to any data store, so you'll need to create this object 
 *   each time you want to use it. The example below shows how to create a service for the Google Drive API.
 */
function getService() { 
  // Create a new service with the given name. The name will be used when
  // persisting the authorized token, so ensure it is unique within the
  // scope of the property store.

  return OAuth2.createService('myservice')

      // Set the endpoint URLs, which are the same for all Google services.
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')

      // Set the client ID and secret, from the Google Developers Console.
      .setClientId(myClientId)
      .setClientSecret(myClientSecret)

      // Set the name of the callback function in the script referenced
      // above that should be invoked to complete the OAuth flow.
      .setCallbackFunction('authCallback')

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getUserProperties())

      // Set the scopes to request (space-separated for Google services).
      .setScope('https://www.googleapis.com/auth/webmasters')

      // Below are Google-specific OAuth2 parameters.

      // Sets the login hint, which will prevent the account chooser screen
      // from being shown to users logged in with multiple accounts.
      .setParam('login_hint', Session.getActiveUser().getEmail())

      // Requests offline access.
      .setParam('access_type', 'offline')

      // Forces the approval prompt every time. This is useful for testing,
      // but not desirable in a production application.
      //.setParam('approval_prompt', 'force');
}

/** (2.)
 * Direct the user to the authorization URL
 *
 * Apps Script UI's are not allowed to redirect the user's window to a new URL, so you'll need to present the authorization URL
 *   as a link for the user to click. The URL is generated by the service, using the function getAuthorizationUrl().
 */
function showSidebar() {
  var service = getService();
  if (!service.hasAccess()) {
    var authorizationUrl = service.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();
    Logger.log(page);
  } else {
    // ...
  }
}

/** (3.)
 * Handle the callback
 *
 * When the user completes the OAuth2 flow, the callback function you specified for your service will be invoked. This callback 
 *   function should pass its request object to the service's handleCallback function, and show a message to the user.
 */
function authCallback(request) {
  var service = getService(); // (1.)
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}

/** (4.)
 * Get the access token
 * 
 * Now that the service is authorized you can use its access token to make reqests to the API. The access token can be passed along 
 * with a UrlFetchApp request in the "Authorization" header.
 */
function makeRequest() {
  var service = getService();
  var resource = 'sites';
  var parameter = '';
  var response = UrlFetchApp.fetch(restBasePath + resource + '?' + parameter, {
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken()
    }
  });
  return response;
}
pdelteil commented 7 years ago

I have the same issue.

mihaiaperghis commented 7 years ago

Instead of

//.setParam('approval_prompt', 'force');

try

.setParam('approval_prompt', 'auto');

Also, did you activate the Search Console API in the Developer Console project where you set up the OAuth2 credentials?

Peter-Valenta commented 7 years ago

Thank you, mihaiaperghis - after changing to 'auto' authorization now works like a charm and im making all the calls to Search Console API that I need!

oshliaer commented 7 years ago

@mihaiaperghis, thanks!

mihaiaperghis commented 7 years ago

Glad it works!

PS: I'm using the same API for my Search Analytics for Sheets add-on.

Peter-Valenta commented 7 years ago

Yes, I use your plugin for some time now, its really fantastic for pulling data. But I needed something a bit more tailored this time around, into an existing sheet that already includes a bunch of automation and custom functions.

Specifically I have the need to pull data automatically on a schedule from cell values and put the data back into a table in specific spots. Sort of an automated SERP tracker/reporting system.

erickoledadevrel commented 7 years ago

Is it safe to close this issue now?

mihaiaperghis commented 7 years ago

Think it is, setting approval_prompt to auto seems to have done the trick.