Google API Executable permission denied error - google-apps-script

I have a small Google Apps Script that I have deployed as API executable.
Although I have made the call with a valid token for the scope I'm using (spreadsheets), I can't get past this 403 PERMISSION_DENIED error.
I'm now testing this on the API's Explorer with the same error.
Does anyone have any ideas of what I might be missing?
My script is simple (still incomplete, but it runs - which should be enough for these initial tests):
OAuth Scope required by script (copied from API's Explorer):
https://www.googleapis.com/auth/spreadsheets
function myFunction(get) {
var ss = SpreadsheetApp.openById("1axDlnPaoEwhlb_Vs5SLBvkGJrR-fSeOLuHEdSBXCTBg"); // I'm not getting anything from the sheet at this point, I included it just to make sure that the script requires the spreadsheets scope
get += " " + "success!";
return ContentService.createTextOutput(get);
}
The request (copied from API's Explorer):
POST https://script.googleapis.com/v1/scripts/1d1EKj7vi-gzC0gB02qjw_qBPE1zSrZtJp-YUWuKm3NL3-3t6Sixpm0TZ:run?key={YOUR_API_KEY}
{
"function": "myFunction",
"parameters": [
"Hello world!"
]
}
The response (copied from API's Explorer):
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}

You may want to check these 3 things:
Make sure that your app script is associated with the correct Dev Console Project.
Enable Apps Script Execution API.
If you're running a function in an Apps Script using Method: scripts.run, set devMode: true if you're the owner of the script. Otherwise, use the default value which is false.
Also, you should meet the listed requirements in Using the Execution API.
See this related SO post for additional insights.

The script should be associated with the developer console project id that corresponds with OAuth 2.0 client ID used (On the script editor of your current script: Resources > Cloud Platform Project, then enter your project id number)
This was the reason why my script wasn't working (yours may differ, see Teyam's answer which I've marked as the correct one).

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

Deploying Script as Webapp directly from Sheets

Is it, at all, possible to deploy a Google Scripts as web app directly from Google Sheets?
I am trying to add a custom menu to Google Sheets where the user can programmatically deploy the file's script as a web app. And it looks like it is possible to do so by following instructions from this GitHub repo.
But for obvious reasons, I need to set projectId to equal ScriptApp.getScriptId(), so that when the Spreadsheet file is copied, this variable is dynamically set to equal to that script's unique id. However, when executing saveAndDeployNewVersion() from the custom menu I've set up (and even from inside the script itself) I always get deploymentId returned as undefined, before getting a 403 error from the final function in the chain (makeRequest_).
Is there any other approach, OR, can you point out what I'm doing wrong when implementing this script into my own project?
Request failed for https://script.googleapis.com returned code 403. Truncated server response: { "error": { "code": 403, "message": "Apps Script API has not been used in project 575477635395 before or it is disabled. Enable it by vi... (use muteHttpExceptions option to examine full response) (line 211, file "Code")
You need to
Switch to standard GCP.
Enable the apps script api for that GCP
Also see Related answer.

Google Classroom Apps Script - CourseWork.list permission error

I am trying to access course work from my Google Classroom in a Google Apps Script using the Classroom API v1. I followed the steps in the Quickstart to successfully retrieve my course list, but when I tried to access the coursework in one of my classes using the following:
var coursework = Classroom.Courses.CourseWork.list('valid courseId');
I get a 'The caller does not have permission' error. I can successfully retrieve the coursework list using the APIs Explorer, though.
From playing with the APIs Explorer, it looks like the "classroom.coursework.students.readonly" scope is needed for this command. However, that scope doesn't get added to my project when I hit the 'Allow' button in the permission dialog. Is there a way to add it to the scope list for the project? I've searched SO and have seen mention of setting scopes in other languages (python, for instance), but not in Apps Script. I've also seen mention of someone authorizing a scope manually in an Apps Script, but with no explanation on how to do that.
I've hit a wall on this, so if anyone has a suggestion, I'd really appreciate it. Thanks.
Originally addressed by me on this SO thread.
The appropriate Classroom API reference for this task is here.
Looks like even after enabling Advanced Google services..., you only get the following OAuth Scopes added -
https://www.googleapis.com/auth/classroom.courses
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters
You can view these by navigating to File > Project properties > Scopes.
However, when you try the API from the documentation link, under the Credentials > Google OAuth 2.0 tab, it shows 4 more completely different OAuth scopes; those are as follows -
https://www.googleapis.com/auth/classroom.coursework.me
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.coursework.students.readonly
You need to add all 8 of these manually in your Apps script manifest file. To do that, navigate to View & check the Show manifest file. There you need to add this code, perhaps below dependencies -
"oauthScopes": [
"https://www.googleapis.com/auth/classroom.courses",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.profile.emails",
"https://www.googleapis.com/auth/classroom.profile.photos",
"https://www.googleapis.com/auth/classroom.rosters",
"https://www.googleapis.com/auth/classroom.coursework.me",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.coursework.students",
"https://www.googleapis.com/auth/classroom.coursework.students.readonly"
],
Note1: Only adding the newer 4 will not do the trick as the script would assume only these and not the original 5 there were auto-populated when your script ran for the first time.
Note2: The blank line is simply to differentiate between the scopes that get generated automatically vs. the ones you need to add manually (its redundant).
My appsscript.json file looks like this; yours might differ -
{
"timeZone": "Asia/Kolkata",
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Classroom",
"serviceId": "classroom",
"version": "v1"
}]
},
"oauthScopes": [
"https://www.googleapis.com/auth/classroom.courses",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.profile.emails",
"https://www.googleapis.com/auth/classroom.profile.photos",
"https://www.googleapis.com/auth/classroom.rosters",
"https://www.googleapis.com/auth/classroom.coursework.me",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.coursework.students",
"https://www.googleapis.com/auth/classroom.coursework.students.readonly"
],
"exceptionLogging": "STACKDRIVER"
}
I was getting this error repeatedly running the code as both a domain admin and as a teacher of the course I was testing with (i.e. I really should have had access).
In trying to ferret out the permissions issues, I tried making a call to Classroom.Courses.CourseWork.create, which triggered another authorization dialog that included additional permissions for accessing coursework. Even though my create call failed (I was still playing w/ the API and hadn't gotten the syntax right), the permissions it triggered were what I needed to get the course listing correct.
In short, here's the code that initially failed with the permission error you describe:
function getCoursework(id) {
var resp = Classroom.Courses.CourseWork.list(id);
work = resp.courseWork
if (work && work.length > 0) {
for (var i=0; i< work.length; i++) {
piece = work[i]
Logger.log('Work: %s (%s)',piece.title,JSON.stringify(piece));
}
}
}
That code did not trigger a permissions dialog, as it should have. However, once I ran the following (broken) code, I did get a permissions dialog, and then the above code worked:
function createCoursework (id) {
Classroom.Courses.CourseWork.create(id,
{ // doesn't work but triggers permissions correctly
"courseId": id,
"title": 'foo',
"description": 'desc',
});
}
As stated in this thread, make sure that the app script is associated with correct dev console project.
The script should be associated with the dev console project id that corresponds with OAuth 2.0 client ID used (this dev console project should also have "Apps Script Execution API" enabled).
To change the developer console project for an app script select the following menu item: Resources > Developer Console Project...
On this screen enter the project number for your dev console.
You must supply a valid OAuth token to use the API, and this requires a Developer Console Project.
I ahve the same issue - when running a Google Apps script add-on as a teacher of the project, the call to Courses.CourseWork.list works fine.
As soon as I switch to running the same script add-on as a student in the course, I get the 'The caller does not have permission' error.
This is not controllable by the developer of the add-on as beyond turning on the Classroom API, the scopes are not controllable by the developer.
The core issue is - code works for teachers of a course. Code fails for students of the course.
All this while the Classroom APi reference itself works fine. https://developers.google.com/classroom/reference/rest/v1/courses.courseWork/list
This is most likely a bug - at the very least, nothing to do with the generic catch-all answer given by #abielita above.
I see this bug is old so I have little hopes of an answer but here's hoping.

How to grant permission to other gmail accounts for google app script execution api?

So I followed the tutorials in https://developers.google.com/apps-script/guides/rest/quickstart/js and used app engine to write the javascript functions to call some app script functions.
All the functions work well with the email I registered in google drive.
I am trying to add a new gmail user so that when they login to the app engine app they will be able to get the data from the same sheet.
Instead I am getting 401 or 403 errors
Error calling API: {
"error": {
"code": 401,
"message": "ScriptError",
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.apps.script.v1.ExecutionError",
"errorMessage": "Authorization is required to perform that action.",
"errorType": "ScriptError"
}
]
}
I tried to add the users in the script api project on app engine developer console under IAM & Admin. But it still doesn't work.
So how do I add users email to grant them permission? Thank you.
Try to follow the solutions here for your problem about your error code 401. Make sure you generated the correct credentials from the API project associated with your script. Also, you need to enable the "Google Apps Script Execution API". Check this page on how to do that.
For your question on "how to add users email to grant them permission?"
Try to check this documentation.

Can the Google Apps Script Execution API be called by a service account?

I'd like to use a service account to access a Google Sheet via the Apps Script Execution API, but it's not clear from the documentation whether this is supported.
The steps I've tried (which result in a 403 status from the Execution API) are:
Create a new (unbound) Apps Script
Visit the linked Developer Console project
Enable the Execution API
Create a new service account within the same project (downloading
the generated JSON file)
Create a new Google Sheet and share it with the service account's
email address (this is the step I'm least sure about)
Write an apps script function that reads from the spreadsheet
Run the script manually from the Script Editor (to set the scopes
on the script correctly)
Publish the script ("Deploy as API executable"), making it accessible
to 'anyone'
Mint a new OAuth2 token using the service account and the scopes
linked to the script (in our case just
'https://www.googleapis.com/auth/spreadsheets')
Attempt to make a call to the Execution API using the token
This is the response I got:
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
Does this not work because Service Accounts are never able to access the Execution API? Or is there something wrong with the steps above?
Your original 403 error indicates that you have incorrectly set up authentication for your service account. However, even if you get that working, as of now (10 Nov 2015) you cannot execute Apps Scripts via the Service Account.
It's a known bug, and is being tracked in the Apps Scripts Issue Tracker.
Currently(2020), Service accounts cannot work with Apps script API. As written in the documentation,
Warning: The Apps Script API does not work with service accounts.
Your problem is probably that the script is associated with the wrong project (i.e. its own project, instead of the project associated with your Service Account). Here is what you need to do:
From the Scripts editor select the following menu item: Resources > Developer Console Project.
On this screen enter the project number for your dev console.
cf this answer