8

I need to execute a GAS service on behalf of a user that is logged to my system. So I have her/his access token. I would like somehow to transfer the token to the web app and without having to authorize again the user to use it for some activities. Can this be accomplished? Thank you.

EDIT: I think I didn't explain right what I try to accomplish. Here is the work flow I try to achieve:

  1. We authorize a user visiting our website using OAuth2 and Google;
  2. We get hold of her/his access token that Google returns;
  3. There is a Google Apps Script web app that is executed as the user running the web app;
  4. We want to call this app (3) by providing the access token (2) so Google not to ask again for authorization;
  5. Actually, we want to call this app (3) not by redirecting the user to it but by calling it as a web service.

Thanks

5
  • I really doubt it, but would help me immensily. Commented Mar 24, 2015 at 11:25
  • This can be accomplished is a few ways. How is the script ran, from the IDE, trigger, drive app, etc? Commented Mar 24, 2015 at 15:38
  • @SpencerEaston it is called from my backend. Commented Mar 24, 2015 at 18:07
  • Are you getting the tokens via a service account from your backend? Commented Mar 24, 2015 at 18:10
  • @SpencerEaston yes, we use OAuth2. Commented Mar 24, 2015 at 20:22

3 Answers 3

10

Martin's answer worked for me in the end, but when I was making a prototype there was a major hurdle.

I needed to add the following scope manually, as the "automatic scope detection system" of google apps script did not ask for it: "https://www.googleapis.com/auth/drive.readonly". This resulted in UrlFetchApp.fetch always giving 401 with additional information I did not understand. Logging this additional information would show html, including the following string

Sorry, unable to open the file at this time.</p><p> Please check the address and try again.

I still don't really understand why "https://www.googleapis.com/auth/drive.readonly" would be necessary. It may have to do with the fact that we can use the /dev url, but who may use the /dev url is managed is checked using the drive permissions of the script file.

That said, the following setup then works for me (it also works with doGet etc, but I chose doPost). I chose to list the minimally needed scopes explicitly in the manifest file, but you can also make sure the calling script will ask for permissions to access drive in different ways. We have two google apps script projects, Caller and WebApp.

In the manifest file of Caller, i.e. appsscript.json

{
  ...
  "oauthScopes": 
  [
    "https://www.googleapis.com/auth/drive.readonly",
    "https://www.googleapis.com/auth/script.external_request"]
}

In Code.gs of Caller

function controlCallSimpleService(){

  var webAppUrl ='https://script.google.com/a/DOMAIN/macros/s/id123123123/exec';

//  var webAppUrl = 
//  'https://script.google.com/a/DOMAIN/macros/s/id1212121212/dev'

  var token = ScriptApp.getOAuthToken();

  var options = {
    'method' : 'post'
    , 'headers': {'Authorization': 'Bearer '+  token}
    , muteHttpExceptions: true
  };

  var response = UrlFetchApp.fetch(webAppUrl, options);
  Logger.log(response.getContentText());
}

In Code.gs of WebApp (the web app being called)

function doPost(event){
  return ContentService.createTextOutput("Hello World");
}
Sign up to request clarification or add additional context in comments.

1 Comment

Yes! The key for me was adding the oauthscope to read docs: https://www.googleapis.com/auth/drive.readonly because the script I was calling tries to access a document, therefore the user doing the request needed to also have access to said document
3

The hard answer is NO you can't use the built-in services of Apps Script with a service token. But if you already have the token for a user generated by a service account, access to the users data is pretty similar to any other language. All calls would be to the REST interface of the service your token is scoped for.

Take this small script for example. It will build a list of all the user's folders and return them as JSON:

 function doGet(e){
  var token = e.parameter.token;
  var folderArray = [];
  var pageToken = "";
  var query = encodeURIComponent("mimeType = 'application/vnd.google-apps.folder'");
  var params = {method:"GET",
                contentType:'application/json',
                headers:{Authorization:"Bearer "+token},
                muteHttpExceptions:true
               };

  var url = "https://www.googleapis.com/drive/v2/files?q="+query;

  do{
    var results = UrlFetchApp.fetch(url,params); 
    if(results.getResponseCode() != 200){
      Logger.log(results);
      break;
    }

    var folders = JSON.parse(results.getContentText());
    url = "https://www.googleapis.com/drive/v2/files?q="+query;  

    for(var i in folders.items){
      folderArray.push({"name":folders.items[i].title, "id":folders.items[i].id})
    }

    pageToken = folders.nextPageToken;
    url += "&pageToken="+encodeURIComponent(pageToken);
  }while(pageToken != undefined)

  var folderObj = {};
  folderObj["folders"] = folderArray;      
  return ContentService.createTextOutput(JSON.stringify(folderObj)).setMimeType(ContentService.MimeType.JSON);
}

You do miss out on a lot of the convenience that makes Apps Script so powerful, mainly the built in services, but all functionality is available through the Google REST APIs.

1 Comment

Thanks for the reply but this is not what we need. Please, check the edit with, I guess, better explanation.
3

I found a way! Just include the following header in the request:

Authorization: Bearer <user's_access_token>

2 Comments

Hey. I also want to do the same with my chrome extension and app script. But using this method does not work for me. I have ensured that both the app script and extension are part of the same project in the google developer console. Is there anything else I need to take care of?
Thank you, this works - however, I realized the access token expires after an hour which doesn't work for making this a long-term solution. Is there any workaround that doesn't involve another intermediary step where we exchange the user's refresh token for an access token?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.