Failure in Fetching Google Script's URL - google-apps-script

URL: https://script.google.com/a/macros/MyDomain.com/s/MyProjectId/exec
To trigger the doGet() inside my Google Script, with URL:
Success: Manually Input above Url in the browser.
Failure: Fetch by Google Script UrlFetchApp.fetch(URL)
Is Fetch by program not allowed for Google Script's URL? Or have I made any stupid mistake?
I have tried to fetch by post method also to test doPost(), but got same result.
N.B.I have added my domain & script.google.com into APIs & auth/Push, and handled OAuth. No more authorization popup. So suppose not these issues.
~~~~~~~~~~~~~~~~~~~~~~~~~
Some people say this problem come from Google Apps's security in front of doGet() & doPost(). But after testing, it is failure no matter I run it on personal Gmail account or Google Apps account, and no matter run it as Developer, Domain users or Anonymous Anyone. Below is the demo code, without including the lengthy standard authorization code.
var REDIRECT_URL = 'https://script.google.com/macros/s/.../exec';
var SysEmail = "EmailAddress";
function doGet(e) {
MailApp.sendEmail(SysEmail, 'Test', 'doGet has received Push Notification');
}
//Running below function should trigger above doGet() theoretically, but not actually.
function TestGetByManual(){
var payload =
{
"kind" : "Test",
};
var options =
{
"method" : "get",
"followRedirects" : true,
"payload" : payload
};
var vResult = UrlFetchApp.fetch(REDIRECT_URL, options);
//vResult is Google's Login page.
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After latest testings, I find that Google behave like this:
Since Google's security is so strong, setting parameter "followRedirects" to true is helpless. When Fetch meet Google before doGet() or doPost(), it will be stopped due to Authority reviewing. If it is fetched by manual (e.g. users' installation through browser), an authorization window will be pop-up (esp. if doGet() include authorization code inside). But if it is fetched by another App like my case, everything seems stop and doGet() and its authorization code will never be called!
The only workaround is: If I can set the App to execute as me and accessible by Anonymous, then there will be no Authority checking and everything go directly. Unluckily, even ignoring security issue, only personal Gmail account can make this setting. I, using Google Apps account, cannot make this "Anonymous" setting.
If authorization code will be called, I think there is still hope. But if my testing result is correct and no authorization code will be called before stop, I believe it is a dead corner.
The case here is still better. If the caller is not a controllable custom function, but an uncontrollable system function like Drive.changes.watch(), everything will be further helpless. (See my another post, which is my ultimate goal: Use Google Script's Web App as Webhook to receive Push Notification directly)
If any senior here can contact Google's Arun Nagarajan, really please help get a confirmation from him.

Works fine for me with the following code:
var REDIRECT_URL = 'https://script.google.com/macros/s/AKf.../exec';
var SysEmail = "anemail";
function doGet(e) {
MailApp.sendEmail(SysEmail, 'Test', 'doGet has received Push Notification');
}
function TestGetByManual(){
var vResult = UrlFetchApp.fetch(REDIRECT_URL);
Logger.log(vResult);
}
Make sure you save a new version and re-publish the webapp with that version and using the correct published url.

Related

How to add authentication to a Google apps script without it expiring?

I have a Google apps script project which I use as a web application to save some data gathered from a web page (JavaScript only) to My Google sheets (think of it as a simple database for just me).
It's no need to use auth for anyone else other than my account, because, I don't use their accounts/data for anything at all. I just need to use my account only, so, when I deploy it, I make it execute as me, and accessible to anyone:
.
When I click deploy after previous screen, it asks for my permission (the consent dialogue) to access my account's data, which I grant it, and after that everything is good, and the HTTP requests to this script works just fine.
.
The problem is:
This authentication expires after maybe 7 days (I'm not sure) and the script stops working, I discover that when the HTTP requests to it return error 403
To fix that and make it work again, I need to run the script again from the Google apps script editor which asks for the permissions (consent) again:
.
I can't use it like that and the web page stop working when the authentication gets revoked!
I'm not publishing the script (I don't want/don't need to). Do I?
My question is, how can I add the authentication in a way that makes it lasts and stops asking me for it over and over again?
The script on Google apps script works like this:
function doPost(request) {
return checkRequest(request);
}
function checkRequest(request) {
//check the request & save the sent data to a google sheet here;
//...
return sendResponse({
success: true,
data: {result: 'Saved the data!' }
});
}
function sendResponse(response) {
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
And I call it from the web page using Ajax HTTP POST request, like this:
jQuery.ajax({
url: 'https://script.google.com/macros/s/{script-id}/exec',
method: 'POST',
dataType: "json",
data: {key: 'value'},
success: function (response) {
console.log(response);
},
error: function (response) {
console.error(response);
}
});
And this is the response the script returns after few days when the authentication expires:
This has been reported to Google
There is already a report on Google's Issue Tracker which detail the same kind of behaviour:
Random Deauthorizations for script since Editor Update with no changes to code
Google does seem to know about this issue. From the issue tracker link, a response was given:
[...] reviewing the documentation, it reads:
Authorizations by a test user will expire seven days from the time of consent.
Source
So I'm not sure we should expect anything different from these tests.
Also re-reading this thread, in your first comment you said that this was affecting projects that already published. Though I understand that you fixed the original projects that were having issues by un-linking the GCP projects.
A possible fix would be filling the scopes manually in the manifest for these types of issues. This is because Apps Script tries to infer the scopes needed at runtime, and sometimes this can result in Apps Script trying to gain more permissive scope than what is authorized.
See Setting Explicit Scopes
However, token expiry in around 7 days is to be expected for projects in "testing" state.
Which seems to be the case for the users in the report thread.
There are multiple reasons that this expiration may occur, as explained in the Using OAuth 2.0 to Access Google APIs page.
That being said, if it's causing problems you can file your own bug about it here in the Google Apps Script component.
References:
Random Deauthorizations for script since Editor Update with no changes to code
Using OAuth 2.0 to Access Google APIs | Google Identity
Setting up your OAuth consent screen - Google Cloud Platform Console Help
Authorization Scopes - Setting explicit scopes | Apps Script | Google Developers

HTTP Request to a function in Google Scripts

Since I'm not experienced at all with HTTP Request and Google Scripts, I'm having trouble wraping my head around it.
So, my problem is the following:
I'm currently trying to get information in my lua script and send it to a google Spreadsheet. However, the way the google spreadsheet should save the info would be dependent on which function on the Google Script I'm calling and passing information.
SO, my question is: How would my lua script (that only gives me access to HTTP Requests at this time) connect to a specific function like the one bellow?
function callName(name) {
// Get the last Row and add the name provided
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1,1).setValue([name]);
}
Also, I think my script is wrong as well, but I'm more worried about how to actually make the connection.
Answer:
You can publish your script as a Web Application and use URL parameters to pass the script the information you need.
More Information:
From the Google documentation about web apps:
If you build a user interface for a script, you can publish the script as a web app. For example, a script that lets users schedule appointments with members of a support team would best be presented as a web app so that users can access it directly from their browsers.
However, even without building a user interface, you can use this functionality to run scripts on your sheet by utilising HTTP requests.
Modifying your Script:
In order to allow your script to accept URL parameters, you must first modify your code so that processing is done on a HTTP GET request. You can do this with the Apps Script doGet() function and the event parameter e:
function doGet(e) {
callName(e.parameter.name);
}
function callName(name) {
// Get the last Row and add the name provided
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1,1).setValue([name]);
}
Setting up the Web App:
From the Apps Script user interface, follow the Publish > Deploy as web app... menu item, and in the newly-opened modal window, you'll want to select the following settings:
Project version: New
Execute the app as: Me (your-email#address.here)
Who has access to the app: Anyone, even anonymous
And click Deploy. Here, you will be given a URL in a new, smaller modal in the following form:
https://script.google.com/a/your-domain.com/macros/s/some-script-id/exec
Making the request:
The rest of this is now trivial - you can make your HTTP request to the script URL in the previous step, but providing the URL parameter that you need in order to give te app the information of the value you wish to set.
For example, if you want to set the value to the number 20, make your get request as so:
GET https://script.google.com/a/your-domain.com/macros/s/some-script-id/exec?name=20
Note the ?name=20 at the end gives the Web App the parameter name with a value of 20. The doGet(e) function reads this from e.parameter.name and sends it to your callName(name) function for processing and execution.
References:
Web Apps | Apps Script | Google Developers
Request Parameters

Google Apps Script returns Sorry, unable to open the file at this time. Please check the address and try again

I am trying to POST a request to a simple app script.
Here's the code:
var BACKEND_URL = "https://script.google.com/macros/s/AKfycbyYAWMv6O8Xld1EvqPuBk9EgxgfpVNly3dyX3JkSc3h/dev";
data = {"obj1": "data1", "obj2": "data2"};
function doGet() {
var resp = UrlFetchApp.fetch(BACKEND_URL, {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(data),
'muteHttpExceptions': true
});
Logger.log(resp);
}
And here's the code for the server which should accept POST requests:
function doGet() {
}
function doPost(e) {
Logger.log("posted");
return;
}
NOTE: there's no user part in the URL, so this question isn't the same as a similar one posted in SO.
As a response a get an html page where it's said that sorry, Sorry, unable to open the file at this time. Please check the address and try again.
I've re-checked the page and it does exist.
In your case, the project with doGet() including UrlFetchApp.fetch() is the different from the project with doPost(e). And you want to call doPost() by running doGet() including UrlFetchApp.fetch(). If my understanding your situation is correct, can you confirm the following points.
BACKEND_URL you are using includes dev. This is used for using the latest code. But there is a limitation as follows. So please try to use Current web app URL like https://script.google.com/macros/s/#####/exec.
The second is the link labeled latest code and ends in /dev. This URL can only be accessed by users who have edit access to the script. This instance of the app always runs the most recently saved code — not necessarily a formal version — and is intended for quick testing during development.
At Web Apps, when there are no return messages, the error message of The script completed but did not return anything. is returned. If you want to get the return message, please set it using Content Service.
When the script at Web Apps side was modified, please redeploy the Web Apps as a new version. By this, the latest script is reflected to Web Apps.
References :
Deploying a script as a web app
Content Service
Edit :
When you deploy Web Apps, please try the following settings.
"Me" for "Execute the app as:"
"Anyone, even anonymous" for "Who has access to the app:"

Authentication issue with Google Apps Script deployed as a web app

I am facing HTTP 401 errors while trying to call a deployed Apps Script (as a web app, accessible to "anyone") from a second GAS with UrlFetch and a bearer in authorization header. The scripts were working fine for months until around two weeks ago.
Here are two small scripts to reproduce the error.
Script A - Deployed as a web app, accessible to "Anyone".
function doGet(e) {
var params = e.parameter.params;
console.info("Parameters : " + JSON.stringify(e.parameter));
return ContentService.createTextOutput("Success");
}
Script B - Calling the script A via UrlFetch
function callURL() {
var param = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
followRedirects : true,
muteHttpExceptions:true,
};
var url = "https://script.google.com/macros/s/<script_A_deployed_url>/exec?param1=test";
var resp = UrlFetchApp.fetch(url,param);
if(resp.getContentText() != "Success"){
console.info(resp.getContentText());
throw resp.getContentText();
}
}
Can you confirm the following points again?
For the client side, are there some functions except for your script of the client side in the project? If there is only the script in the project, the scope for accessing to Web Apps is not enough. The scopes for Drive API are required to be included in the scope of access token.
You can see the current scopes at File -> Project properties -> Scopes.
For example, those are the following scopes.
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive
In my log, when Who has access to the app: is installed as Anyone, I confirmed that from April 11, 2018, it is required to be shared the project to access to Web Apps. This might be due to the update of Google.
Please share the project of Web Apps with users and try again.
For the Web Apps server side, if you set User accessing the web app for Execute the app as:, please authorize the scopes using own browser. This authorization is required to do only one time.
If these were not useful for your situation, I'm sorry.
Tanaike pointed me in the right direction. Apparently, some internal rules recently changed in the authentication mechanism for Apps Script deployed as a web app.
For B script, the default scope with UrlFetch is https://www.googleapis.com/auth/script.external_request, but it looks like we now need at least read access to A script, which means we also need Drive scopes.
In order to achieve that, you can for example have this function in B script to authorize them.
function setScope() {
DriveApp.getRootFolder();
}

UrlFetchApp doGet not called and return html with "Meet Google Drive"

I must be losing my mind as suddenly I can't get a simple web app to work.
When I use the URL generated in the browser everything works fine. When I run the script the result is HTML content with Meet Google Drive.
Here is the code:
function doGet(e) {
return ContentService.createTextOutput('blabla').setMimeType(ContentService.MimeType.TEXT);
}
function client() {
var url = 'https://script.google.com/macros/s/AKfycby2NVrhG0O5fE6gTgage2QPYH3UJ2s23AJDLnB9YL69uyDFlmM/exec';
Logger.log(url);
var result = UrlFetchApp.fetch(url);
Logger.log(result);
}
Both doGet and the client functions are at the same file which was never a problem. A version was saved and the app deployed as anyone accessing the app and run as user accessing the app. result.getContentText() returns the same html output. result.getResponseCode() returns 200 which supposed to be successful however I can't see the doGet being called.
I had the same issue.
When you deploy the WebApp, make sure the authorizations are set to "Anyone, including Anonymous"!