OAuth2 with Google Cloud Print

二次信任 提交于 2019-12-08 02:08:15

问题


I had written a Google Apps Script that connected to Google Cloud Print to automate some printing. The script would auto-run on a time interval, search for relevant files, and if found it would sent them to my printer. My code used OAuthConfig and was working fine, but now that class has been deprecated and after a weekend of trial & error and scouring the interwebs I can't get it to work with OAuth2.

Here's the OAuthConfig code that was working fine:

function printDoc(docId, docTitle, myPrinterId) {

  var scope = 'https://www.googleapis.com/auth/cloudprint';
  var url = 'https://www.google.com/cloudprint/submit'; 
  var payloadOfSubmit = {
    "printerid" : myPrinterId, 
    "title" : docTitle,
    "content"  : docId, 
    "contentType" : "google.kix"  
  };

  var fetchArgs = googleOAuth_('google', scope, payloadOfSubmit); 
  fetchArgs.method = 'POST';
  var responseOfSubmit = UrlFetchApp.fetch(url, fetchArgs);
  var jsonOfSubmit = JSON.parse(responseOfSubmit.getContentText()); 

  return jsonOfSubmit;
}

function googleOAuth_(name, scope, payloadData) {
  var oAuthConfig = UrlFetchApp.addOAuthService(name);
  oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
  oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
  oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
  oAuthConfig.setConsumerKey("anonymous");
  oAuthConfig.setConsumerSecret("anonymous");
  return {
    oAuthServiceName:name, 
    oAuthUseToken:"always", 
    muteHttpExceptions:true, 
    payload:payloadData
  }; 
}  

I've successfully connected the github library for OAuth2. However, what's different about the instructions provided there, and on many other sites, is that they assume that the code will be deployed as a web service where a user is prompted to manually click to authorize the request. In my case the code will be saved on a Google Apps Script file, and the Cloud Printer is on the same Google account, so I never needed this manual intervention or back & forth with my original OAuthconfig.

My first attempt by adapting the instructions was:

function printDoc2(docId, docTitle, myPrinterId) {

  var url = 'https://www.google.com/cloudprint/submit';
  var scope = 'https://www.googleapis.com/auth/cloudprint'; 
  var payloadOfSubmit = {
    "printerid" : myPrinterId, 
    "title" : docTitle,
    "content"  : docId, 
    "contentType" : "google.kix",
  };

  var accessToken = googleOAuth_('google', scope).getAccessToken();

  var params = {
    method:"POST",
    headers: {"Authorization": "Bearer " + accessToken},
    muteHttpExceptions:true,
    payload:payloadOfSubmit
  };

  var responseOfSubmit = UrlFetchApp.fetch(url, params);
  //Logger.log(responseOfSubmit);
  var jsonOfSubmit = JSON.parse(responseOfSubmit.getContentText()); 

  return jsonOfSubmit;
}

function googleOAuth2_(name, scope) {

  return OAuth2.createService(name)
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setClientId("anonymous")
    .setClientSecret("anonymous")
    .setProjectKey(ScriptApp.getProjectKey())
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope(scope)
    .setCallbackFunction('authCallback');
}

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

But this gives me an error "Access not granted or expired" when it tries to run the line:

var accessToken = googleOAuth_('google', scope).getAccessToken();

So I found a apps ScriptApp Method getOAuthToken which seemed like it might give me the token I need. I replaced the above line with:

var accessToken = ScriptApp.getOAuthToken();

And the code executes but my response from the server is "Error 403 User credentials required".

Here's my third attempt based on @Mogsdad's suggestion:

function sendPrintJob(docId,myPrinterId,docTitle) {

  var payloadOfSubmit = {
            "printerid" : myPrinterId, 
            "title" : docTitle,
            "content"  : docId, 
            "contentType" : "google.kix" ,
  };

  var request = {
    "method": "POST",
    "headers":{"Authorization": "Bearer "+ScriptApp.getOAuthToken()},    
    "muteHttpExceptions": true
  };

  var responseOfSubmit = UrlFetchApp.fetch("https://www.google.com/cloudprint/submit", request);
  Logger.log(responseOfSubmit);
}

I've tried a number of variations, including creating a Developer Console Project and using the Client ID provided there, but I keep getting stuck at these two issues (access not granted, or credentials required). If anyone can provide any help I'd really appreciate it.


回答1:


Here are the steps that allowed me to connect Google Apps Script to Google Cloud Print, so I could then submit GCP jobs (these steps are all started from within Google Apps Script):

  1. Add the OAuth2 library (https://github.com/googlesamples/apps-script-oauth2) to your Google Apps Script by going to: Resources > Libraries > Find Library MswhXl8fVhTFUH_Q3UOJbXvxhMjh3Sh48 > Select
  2. Create new web application in Developer Console Resources > Developer Console Project > Click the project link > APIs & Auth > Credentials > Add Credentials > OAuth2.0 Client ID > Web Application > Set Authorized redirect URIs to the format
    https://script.google.com/macros/d/{PROJECT KEY}/usercallback where project key is under File > Project Properties and copy your client ID and client secret
  3. Add the ID and Secret to "getCloudPrintService()" code below (replace client_id and client_secret)
  4. Go to Run > ShowURL and authorize the script.
  5. Open the Logger (Cmd + Enter), copy the URL and paste it in a new browser tab to complete the authorization.
  6. Go to https://www.google.com/cloudprint/#printers , select your printer, click details, expand advanced details, and copy your Printer ID (it will be of the format 555aa555-5a55-5555-5555-55555a55a555)
  7. Add the printer id to "printGoogleDocument()" code below (replace myPrinterId)

This resource was helpful in figuring the steps out: http://ctrlq.org/code/20061-google-cloud-print-with-apps-script, and you may also find these links helpful:

  • https://developers.google.com/cloud-print/docs/appInterfaces
  • https://mashe.hawksey.info/2015/10/setting-up-oauth2-access-with-google-apps-script-blogger-api-example/

function showURL() {
  var cpService = getCloudPrintService();
  if (!cpService.hasAccess()) {
    Logger.log(cpService.getAuthorizationUrl());
  } else {
    Logger.log("You already have access to this service.");
  }
}
 
function getCloudPrintService() {
  return OAuth2.createService('print')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setClientId(client_id)
    .setClientSecret(client_secret)
    .setCallbackFunction('authCallback')
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope('https://www.googleapis.com/auth/cloudprint')
    .setParam('login_hint', Session.getActiveUser().getEmail())
    .setParam('access_type', 'offline')
    .setParam('approval_prompt', 'force');
}
 
function authCallback(request) {
  var isAuthorized = getCloudPrintService().handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('You can now use Google Cloud Print from Apps Script.');
  } else {
    return HtmlService.createHtmlOutput('Cloud Print Error: Access Denied');
  }
}

function getPrinterList() {
  var response = UrlFetchApp.fetch('https://www.google.com/cloudprint/search', {
    headers: {
      Authorization: 'Bearer ' + getCloudPrintService().getAccessToken()
    },
    muteHttpExceptions: true
  }).getContentText();
 
  var printers = JSON.parse(response).printers;
 
  for (var p in printers) {
    Logger.log("%s %s %s", printers[p].id, printers[p].name, printers[p].description);
  }
}

function printGoogleDocument(docId, docTitle) {  
  // For notes on ticket options see https://developers.google.com/cloud-print/docs/cdd?hl=en
  var ticket = {
    version: "1.0",
    print: {
      color: {
        type: "STANDARD_COLOR"
      },
      duplex: {
        type: "NO_DUPLEX"
      },
    }
  };
   
  var payload = {
    "printerid" : myPrinterId,
    "content"   : docId,
    "title"  : docTitle,
    "contentType" : "google.kix", // allows you to print google docs
    "ticket"    : JSON.stringify(ticket),
  };
 
  var response = UrlFetchApp.fetch('https://www.google.com/cloudprint/submit', {
    method: "POST",
    payload: payload,
    headers: {
      Authorization: 'Bearer ' + getCloudPrintService().getAccessToken()
    },
    "muteHttpExceptions": true
  });
 
  // If successful, should show a job here: https://www.google.com/cloudprint/#jobs

  response = JSON.parse(response);
  if (response.success) {
    Logger.log("%s", response.message);
  } else {
    Logger.log("Error Code: %s %s", response.errorCode, response.message);
  }
  return response;
}



回答2:


The scope "https://www.googleapis.com/auth/cloudprint" has to be included explicitly in the manifest file

appscript.json (View > Show manifest file)

{
  "timeZone": "Europe/Paris",
  "dependencies": {
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/documents",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/cloudprint"
  ],
  "exceptionLogging": "STACKDRIVER"
}

Code.gs

function listPrinters() {
  var options = {
    headers: {
      authorization: 'OAuth ' + ScriptApp.getOAuthToken()
    }
  }
  var response = UrlFetchApp.fetch('https://www.google.com/cloudprint/search', options);
  Logger.log(response);
}


来源:https://stackoverflow.com/questions/30565554/oauth2-with-google-cloud-print

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!