0

I have a simple app script that searches drive for a folder named 'something' and lists the names of files inside if the folder doesn't exist it creates it.

function doGet() {
 var folders = DriveApp.getFoldersByName('something');
 if (folders.hasNext()) {
  var files = folders.next().getFiles();
    var fileNames  = [];
    while (files.hasNext()) {
      var file = files.next();
      fileNames.push(file.getName())
    };
    return ContentService.createTextOutput(JSON.stringify({
        'status': 'success',
        'output': fileNames
    }))

  } else {
    DriveApp.createFolder('something');
    return ContentService.createTextOutput(JSON.stringify({
        'status': 'success',
        'output': 'folder created'
    }))
  }
}

I deploy the script as a web app, execute the app as myself, test it out using a fetch call from a react app in a browser and everything work as expected.

Then I republish as a web app, executing as the user accessing the web app. This time nothing works. I get the following error in the browser

Access to fetch at 'https://script.google.com/macros/s/AKfycbx4eQ60DziUy4hSjnJidW9WVwBsT_qruQHa_BrK508T4oD9ILY/exec' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I expected an error since there is no authentication. I set up google OAuth2 on the react app, added scripts and drive scopes, register the app in google developer console and set everything up so that I get an accessToken that I can send in the header with the fetch call to the google script.

But I still get the same error as above.

How am I supposed to authorize an app script app so that anyone can run it?

I am trying to create a web app with a google sign in button, when the user signins in it calls the appscript I deployed and searches their drive for a folder names 'something' so that I can display the names of files in my web app.

This is not a cors issue, if I execute the web app as myself there is no cors problems and zero changes to the code. My question is regarding how I authorise an app script? I only mention the cors error because I was showing what I have tried so far.

Josh Pittman
  • 7,024
  • 7
  • 38
  • 66
  • 1. Try setting X-frame options to Allow-all with htmlservice OR 2. use doPost() OR 3. Use a backend to fetch – TheMaster Apr 03 '19 at 12:15
  • So you are saying this is just a cors issue, what I am trying to do makes sense and is possible using appscript? – Josh Pittman Apr 03 '19 at 12:24
  • Isn't that what your error states? I don't see why not... But I'm doubtful whether you can bypass cors. – TheMaster Apr 03 '19 at 12:25
  • cors errors are usually a catch-all for any kind of error from an appscript deployed as a web app. Please help me understand why you think any of these solutions would work when my current app works if I execute it as myself. Doesn't it working rule out cors actually being an issue? Happy to try these, if you are sure they will help. – Josh Pittman Apr 03 '19 at 13:11
  • 3
    @tehhowch reopened – sideshowbarker Apr 03 '19 at 13:29
  • 1
    I can't guarantee anything, but I believe what you're trying to do is entirely possible and that this still is a cors issue or as the answer states `Google's error page does not include any CORS headers.`-> So, you have a error somewhere. You have to provide error logs(view>Stackdriver logging) – TheMaster Apr 03 '19 at 14:18
  • As I linked in comment on my answer, OP should probably review Tanaike's post on web apps: https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script – tehhowch Apr 03 '19 at 14:27

1 Answers1

1

You will receive a CORS error if any function in your call throws an exception, since you have no try...catch handler. When a deployed webapp fails to return a valid response (i.e. either an HtmlOutput or TextOutput), Google's error page does not include any CORS headers.

To prevent these "masking" CORS errors, wrap your web app entry points with try...catch so that the actual error can be reported.

Even without wrapping your webapp's entry points with try...catch, it is very likely that you are able to access the underlying error by viewing your project's Stackdriver logs / Stackdriver Error Reporting.

This is a simple example, demonstrating some event object validation to ensure that the request is from somewhere you care about & is well-formed, and then calls a function that may throw an exception for invalid / unauthorized operations. In either case, it returns an opaque user-facing response ("Sorry!") and for errors will generate a Stackdriver log. It cannot catch the exception that occurs for an execution time quota breach, since your script is simply killed.

function doGet(e) {
  if (!e || !validate_(e)) {
    return fail_();
  }
  try {
    return walkDriveContent(...);
  }
  catch (err) {
    console.error({message: "Drive walk failed: " + err.message, stack: err.stack, error: err});
    return fail_();
  }
}

function fail_() {
  return ContentService.createTextOutput("Sorry!");
}

function validate_(eventObj) {
 /** your code here that ensures this is a valid event object you can work with */
 // return true;
 //     or
 // return false;
}

function walkDriveContent(folderName) {
  /**
   * Your code that may throw exceptions here, but otherwise
   * returns TextOutput or HtmlOutput
   */
}
tehhowch
  • 9,645
  • 4
  • 24
  • 42
  • Thanks, I'll try this. But does this means you are saying that my general approach is correct and I shoudl be able to authentcate a script by passing in teh access token in the header. there is nothing else I have to do? – Josh Pittman Apr 03 '19 at 13:32
  • @josh Off the top of my head, yes, if you've obtained an OAuth2 access token that contains the required scopes, then you can **execute** your web app as that user. You may or may not be able to **access** the web app as that user. I suggest updating your question to be about resolving the masking CORS error, and then when you have the underlying error pinned down, ask a new question about that. Probably you should review https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script – tehhowch Apr 03 '19 at 13:38
  • I'm not too worried about cors, I'll figure that out or ask another question, this question is titled 'How do I authorize an app script in a web interface?'. – Josh Pittman Apr 03 '19 at 13:44
  • @josh Yes, that's the title, but then your question body is almost entirely about a CORS error. This answer of mine demonstrates how you can prevent getting that CORS error (and instead access the underlying error that created the CORS error). – tehhowch Apr 03 '19 at 13:49
  • Thank you. I appreciate your help. I did my best to explicitly state that this is not a cors issue at the bottom of the question, and that I only mention the cors error because I was showing what I have tried so far. My concern is that I don;t want to spend ages fixing teh cors error if what I am trying to do in fundamentally not possible. Have you ever granted access to an app script app via a web app by passing in a token? – Josh Pittman Apr 03 '19 at 13:54