I deployed a Google Apps Script Web App which uses a service account to access the Google Directory. I deployed it that everyone has access to the app and that the app runs under the user executing it.
I would like to trigger this web app by calling the POST trigger from another app script which is part of a Google form. Something like this:
function onPost(){
var data = {
'name': 'Bob Smith',
'age': 35,
'pets': ['fido', 'fluffy']
};
var options = {
'method' : 'post',
'contentType': 'application/json',.
'payload' : JSON.stringify(data)
};
var resp = UrlFetchApp.fetch('https://script.google.com/a/my.domain.com/m.../exec', options);
}
However, I get back:
Exception: Request failed for https://script.google.com returned code 401.
When I try to use the GET trigger with some query parameters the response back looks like a google sign in page.
How can I trigger my web app from another app script all in the same domain?
Updated code with token:
function onPost(){
var token = ScriptApp.getOAuthToken();
var data = {
'name': 'Bob Smith',
'age': 35,
'pets': ['fido', 'fluffy']
};
var options = {
'headers':'Bearer '+token,
'method' : 'post',
'contentType': 'application/json',.
'payload' : JSON.stringify(data)
};
var resp = UrlFetchApp.fetch('https://script.google.com/a/my.domain.com/m.../exec', options);
}
The response back looks like a Google sign-in page
This is because you received the 401 Unauthorized HTTP status code prompting you that the request should contain valid user credentials. Since Google primarily uses OAuth 2.0, you will need to include an Authorization header set to Bearer YourTokenHere.
As of recently, the OAuth token can be obtained via getOAuthToken(), which will contain a claim in its payload that will match scopes granted to the application that is making the request (either automatically or explicitly).
You can also use the official OAuth 2.0 library if you need more fine-grained control over token management, or build from scratch if you feel like it, the Utilities class has everything you need for generating a custom JWT.
I deployed it that everyone has access to the app and that the app runs under the user executing it
Web Apps have several deployment modes across two permission settings:
"Execute the app as"
Me
User accessing the web app
"Who has access to the app"
Only myself
Anyone
Anyone, even anonymous
Google won't let you just strip the security and expose a public API (which is a good thing), so if you want everyone to be able to access your Web App without authorization, you will have to authorize the script yourself and let your users access the application under your authority.
Only one combination of options allows you to do that:
Execute as me, access to anyone, even anonymous
Related
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:"
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();
}
I'm trying to make a standalone Google Apps Script to access an API through the OAuth2 library, found here: https://github.com/googlesamples/apps-script-oauth2
I have not been able to get any of the examples to work so far, but I think it's because I cannot register my application and set the OAuth redirect URI. This is my first attempt at using OAuth.
Here are the steps I've taken:
Create a new standalone Google App Script
Paste in the contents of Dropbox.gs (for this example)
Add the OAuth2 library to my script
(Resources > Libraries, and paste in the ID listed in the how-to)
Go to the console (Resources > Cloud Platform project) and navigate to
the APIs & Services > Credentials page
Grab the Client ID and Client secret from that page and paste them into my script.
Get the redirect URI from the script (by running logRedirectUri())
Paste the redirect URI into the cloud platform console, and hit save.
I get the error shown at this link (which reads "You do not have permission to perform this action.
Request URI error
From what I've studied, I need this URI entered in order to make this script work. Why won't this save?
Here are some things you can try.
Step one
The web app api
Change from using the appscript API to using a web app API. You can create a new one in the API admin console.
Then select the web app option.
Finally you will have a new API.
Open the new API and get the client ID and secrets.
Paste into the api the redirect from your app.
https://script.google.com/macros/d/{SCRIPT ID}/usercallback
Step two
The authentication and redirect
The next bit is tricky and may need some fiddling.
The first part of the code below generates a URL. The user then needs to be directed to open the generated URL in a new window. The new window will show all the usual google permissions including any scopes. When the user accepts they will be redirected back to your app.
function getAuthURL() {
var driveService = getDriveService();
if (!driveService.hasAccess()) {
var authorizationUrl = driveService.getAuthorizationUrl();
Logger.log(authorizationUrl)
}
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');
}
}
Step three
The access token
Now the user gave permission it is possible to get the access token and pass it along on with fetch requests.
function makeRequest() {
var driveService = getDriveService();
var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
});
// ...
}
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.
I already developed applicaton for google app script that can send email messages, but right now there is a need for me to send gtalk chat/xmpp message to user from my script.
my question is, is it possible for me to send gtalk chat/xmpp message directly from google app script? if it is not possible then is there any work around about this?
Basically, there are two types of messages you can automate through the App script.
Synchronous: building a boot that can respond to user inputs.
Asynchronous: specified trigger-based messaging systems. like submitting a google form or time-based.
Let's talk about Asynchronous
You can do it by Using incoming webhooks.
Create a Google Chart Room > Create a webhook URL. click_here
Use this Code:
function myFunction() {
var WebWhooklink = "PEST_YOUR_WEBHOOK_URL_HERE"
var message = { text: "Hello Chat"};
var payload = JSON.stringify(message);
var options = {
method: 'POST',
contentType: 'application/json',
payload: payload
};
var response = UrlFetchApp.fetch(WebWhooklink, options ).getContentText();
}
There is no built-in support for sending Google Chat messages. You can file a feature request on the issue tracker.
If you are able to make HTTP requests to external services from with in a google app script then you might be able to use an HTTP to XMPP gateway such as this:
http://chatmongers.com/blog/introducing-the-chatmongershttp-to-xmpp-gateway/
There would be a number of constraints you may still have to work through. The most common one being that privacy extensions block all messages from users that are not in the destination user's roster, but that's going to be a problem no matter how you manage to get XMPP messages sent.