OAuth2 support through library returns The state t

2019-08-13 17:48发布

I have spreadsheets that need to be converted to PDF by various users in my organisation. In order to do that I am trying to use OAuth2 (https://github.com/googlesamples/apps-script-oauth2).

When I am using it in a main spreadsheet embedded script it works. However I am trying to move the OAuth2 code to a separate library as there will be many spreadsheets that will need the same functionality. When I am using this library I am getting the message "The state token is invalid or has expired. Please try again."

The library code:

var PROPERTY_KEY = "ExportPdfOauth2";

function initialStore() {
  setAuthenticationPackage_( {
    clientId : '10511......b43bu.apps.googleusercontent.com',
    clientSecret : 'WYUsq...-h_',
    projectKey : 'MxJ.......xOvW',
    scopes : ['https://spreadsheets.google.com/feeds/']
  });
}

function setAuthenticationPackage_(package) {
  PropertiesService.getScriptProperties().setProperty(PROPERTY_KEY, JSON.stringify(package));
}

function getAuthenticationPackage_() {
  var p = PropertiesService.getScriptProperties().getProperty(PROPERTY_KEY);
  return p ? JSON.parse(p) : {};
}

function getExportPdfService() {
  // 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.

  var package = getAuthenticationPackage_();

  return OAuth2.createService('exportPdf')

      // 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(package.clientId)
      .setClientSecret(package.clientSecret)

      // Set the project key of the script using this library.
      .setProjectKey(package.projectKey)

      // 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(package.scopes)

      // 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');
}

function authCallback(request) {
  var user = Session.getActiveUser().getEmail();
  var exportPdfService = getExportPdfService();
  var isAuthorized = exportPdfService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success');
  } else {
    return HtmlService.createHtmlOutput('Failure');
  }
}

function getExportPdfRequest() {
  var user = Session.getActiveUser().getEmail();
  var exportPdfService = getExportPdfService();
  if (!exportPdfService.hasAccess()) {
    var template = HtmlService.createTemplate('Authorisation required in order to enable conversion to pdf. You will need to perform the operation again once the authorisation is complete.')+
      '<br /><br />'+
      '<a href="<?= authorizationUrl ?>" target="_blank">'+
      'Authorise'+
      '</a>');
    template.authorizationUrl = exportPdfService.getAuthorizationUrl();
    var page = template.evaluate();
    SpreadsheetApp.getUi().showSidebar(page);
    return null;
  }

  var request = {
    headers: {
      Authorization: 'Bearer ' + exportPdfService.getAccessToken()
    }
  };

  return request;
}

The library is called by the embedded script as follows (please note that I put a callback function here that will call back the library):

function test(){
  var id = 'ABCD.....'; // Spreadsheet to be converted to pdf
  var name = 'test.pdf';
  var domain = 'XXXXX';

  var pdfContent = spreadsheetToPDF(id,domain,name);
  if (pdfContent) DocsList.createFile(pdfContent);
}

// Convert spreadsheet to PDF file.
function spreadsheetToPDF(id,domain,name) {
  SpreadsheetApp.flush();

  var request = testPdfLib.getExportPdfRequest();
  if (!request) return null;

  //define the params URL to fetch
  var params = '?fitw=true&exportFormat=pdf&format=pdf&size=A4&portrait=true&sheetnames=false&printtitle=false&gridlines=false&pagenum=CENTER';
  var url = "https://docs.google.com/a/"+domain+"/spreadsheets/d/"+id+"/export"+params;

  //fetching file url
  var blob = UrlFetchApp.fetch(url, request);
  blob = blob.getBlob().setName(name);

  //return file
  return blob;
}

function authCallback(request) {
  testPdfLib.authCallback(request);
}

I have also looked at another post at How to correctly construct state tokens for callback urls in Managed Libraries? but still can not figure out what I am doing wrong as I have provided the callback function at the main calling script. At the developers console my REDIRECT URIS points to the library. I tried to point it to the main script but I get the same results (though I wouldn't like to do so as the script will be copied by many users and it will be impossible to create a new client Id for each instance of the calling script).

Appreciate any help!

0条回答
登录 后发表回答