Sudden permissions problem with SpreadsheetApp.openByID - google-apps-script

The code is supposed to copy some data from another spreadsheet and then I manipulate it.
This code worked yesterday, then I tried adding a library today and now it is still throwing this error even after removing the library and rolling back my code to the version yesterday.
Error Message:
You do not have permission to call SpreadsheetApp.openById. Required
permissions: https://www.googleapis.com/auth/spreadsheets (line 21,
file "Import")
Code:
data = SpreadsheetApp.openById('1-SyJRflR1YYYYDRo3e7xnDhYYYY6hIGDBKODl9XXXXX').getSheetByName("g1.1").getDataRange().getValues();
I am NOT using "//#OnlyCurrentDoc" anywhere in either of my documents so that is not my problem.
Google sheets API is turned on for this project, as checked from the resources menu.
The library function I was attempting to use was a simple time one (nothing to do with sheets), and it is not currently being called anywhere in my project and the library has been detached.

Possible Issues:
If your library included //#OnlyCurrentDoc and you authorized after adding the library, your permissions might've just included the current document.
If you had explicitly set scopes1, then those scopes might not have included https://www.googleapis.com/auth/spreadsheets
Possible Solutions:
If you want to still use the library, add the //#NotOnlyCurrentDoc in your main script and re-run the function. If not remove the library and re-run your function.
An opposing annotation, #NotOnlyCurrentDoc, is available if your script includes a library that declares #OnlyCurrentDoc, but the master script actually requires access to more than the current file.
Removing the explicit scopes in your manifest will trigger the normal authorization flow.
If all else fails, visit your account, find your project and remove all access. Now, re-running any function in your script will trigger normal authorization flow.

Related

Why can't I open a file by ID that I have installed my add-on in?

According to the Apps Script documentation:
openById(id)
Opens the spreadsheet with the given ID. ...
...
Authorization
Scripts that use this method require authorization with one or more of the following scopes:
https://www.googleapis.com/auth/spreadsheets.currentonly
https://www.googleapis.com/auth/spreadsheets
I would like to use openById with the spreadsheets.currentonly scope since it suits my needs. However, even after adding the scope to the appsscript.json manifest, the following code fails for me:
const sheet_id = SpreadsheetApp.getActiveSpreadsheet().getId();
SpreadsheetApp.openById(sheet_id);
Exception: You do not have permission to call SpreadsheetApp.openById. Required permissions: https://www.googleapis.com/auth/spreadsheets
I understand that I could just use the https://www.googleapis.com/auth/spreadsheets scope, but since the docs say I should be able to use spreadsheets.currentonly, that is what I want to do. What am I doing wrong here?
Update:
This answer is related, but the workarounds suggested don't work for me. The code snippet I shared above was executed in the script editor. I also don't intend to create a custom function. I intend to run this code in the doPost(e) function of a web app script.
OAuth scope ids are sometimes reachable links that can provide useful information about the scope.
I got the following message after navigating to https://www.googleapis.com/auth/spreadsheets.currentonly:
You are receiving this error either because your input OAuth2 scope name is invalid or it refers to a newer scope that is outside the domain of this legacy API.
This API was built at a time when the scope name format was not yet standardized. This is no longer the case and all valid scope names (both old and new) are catalogued at https://developers.google.com/identity/protocols/oauth2/scopes. Use that webpage to lookup (manually) the scope name associated with the API you are trying to call and use it to craft your OAuth2 request.
Ultimately, I ended up here which lists the following scopes for the Sheets API:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/spreadsheets.readonly
It looks like the https://www.googleapis.com/auth/spreadsheets.currentonly scope is no longer valid. I suspect this happened as a result of Version 3 of the Sheets API hitting its sunset date on August 2, 2021 (a few weeks ago as of this writing). Maybe the scope wasn't carried over to Version 4 of the Sheets API.
This issue certainly deserves further investigation. I suggest that you report the issue using Google's Issue Tracker using the following link:
https://issuetracker.google.com/components/191608.

Centralized script for multiple spreadsheets

I have created a Notification script and would like to use it for multiple spreadsheets (produced by Forms). I would like to have a centralized script and referencing it on my multiple spreadsheets in order to simplify maintenance.
I have tried to use the SpreadsheetApp.openById() like below but looks like this feature has been disabled for security reasons and is not supported anymore.
function append()
{
SpreadsheetApp.openById("1xdePF..........................");
}
Any idea on how to use the same script for multiple spreadsheets?
The documentation specifies that openById opens the spreadsheet with the given ID.
You cannot use this method to open a script.
To open an Apps Script, go to https://script.google.com/home/my and clock on the script of your choice.
For bound scripts, you open the document to which the script is bound and go to Tools->Script Editor
Preface
This answer is supplementary to ziganotschka's, since you indeed cannot access the script bound to a document by opening said document with openById(). The answer, instead, covers other issues you had and suggests additional ways to solve your task.
Problem
When trying to use openById() you receive an error message of the following structure:
Exception: Document [doc id here] is missing (perhaps it was deleted, or maybe you don't have read access?)
You mentioned in comments that the id is 58 chars (I bit it is 57) and obtained from "properties", which explains the error - there is no spreadsheet with such an id, because file id and script id you extracted are not the same thing. If you ever need to extract current id programmatically (here it is assumed to be called from a script bound to spreadsheet, but other services have similar methods), you can call getActiveSpreadsheet() -> getId().
Solutions
You stated that you need a maintenance script, so how about creating a standalone script project that is deployed in a way any document can access:
As a library
Any script that has a saved version can be a library that can be used by other scripts by adding its id to the list accessible from Resources->Libraries menu.
As a Web App
Any script that has a doGet, doPost function (or both) can be deployed as a Web App, essentially exposing it to the net. Since you said that the spreadsheets are "produced" by Forms, and you created a "notification" script, I assume you are interested in a FormSubmit event. When you deploy as a Web App, you get a url (don't forget to choose an appropriate permission).
After that, it is only a matter of making sure that:
each Form has an installable onFormSubmit trigger (you can instlall it via [please see refs for docs] ScriptApp.newTrigger('callback name').forForm('form ref').onFormSubmit()).
The callback for the trigger calls the Web App url via UrlFetchApp.fetch() with necessary data (like spreadsheet id or any other info) as query (if using doGet) or request body (if using doPost).
The data you need will be available in the event object constructed on hit to doGet or doPost [note to avoid common misconception: you can't debug event objects in the editor, it can only be done live].
Reference
getActiveSpreadsheet() docs
getId() docs
getFileById() docs
Standalone scripts guide
Bound scripts guide
Libraries guide
Form submit event reference
new Trigger() docs
UrlFetchApp.fetch() docs
doGet / doPost event object docs

Permission denied when trying to create a spreadsheet using Google script API

In the corporate environment with a corporate account I have a Google Document. Think of it as a master document that is being maintained.
I want to add a script to it to create a derived spreadsheet that can be used by people who need a subset of data from the master document.
In the master document I created a script file so the script is bound to the document.
Inside the file I defined a function. Function does some search and processing in the master file without problems but once I try to create or open an existing spreadsheet to extract data from the master document to a derived document the call fails.
I tried several approaches:
SpreadsheetApp.openByUrl();
SpreadsheetApp.create("My test sheet");
and approach covered in the solution
I also tried execution from an installed menu trigger or from debugger - same result.
In all cases I get a pop up with :
Authorization required
{doc name} needs your permission to access your data on Google.
with two buttons to review and cancel.
Clicking review leads to a message "An unexpected error occurred" at the top of the screen.
I tried different machines and browsers and switched V8 engine on and off.
I also made sure that the document is open to all in my org if I try to open an existing doc by URL.
What am I doing wrong?
It turned out that there were two things.
Adding scopes solved the problem.
I had an invalid scope that
was already there (I do not know how it got there, probably from an
example) that I preserved in the manifest and might have misspelled.
That was a mistake. It caused all sorts of instability. Only when I
tried to execute the script in FireFox instead of Chrome was I able
to see a popup that gave me a hint that one of the scopes is invalid
and I need to fix it.
Once I fixed it, everything started to load as expected.

Google App Scripts cannot be given Authorization or Permission

Why am I not able to give permission/authorization to a Google Apps Script that I also made using the same Google account?
It seems like Google doesnt trust myself to use my own Google Apps Script with my own Spreadsheet.
Here is the line of code that breaks everything. If this line doesnt exist, I'm not asked for permission.
var sheet = SpreadsheetApp.getActiveSheet();
So it's trying to access the spreadsheet that created this Google Apps Script, also made using my account but I cant grant permission.
When I run the line of code above, I am told I need to give permissions, so I do by selecting the account name I am already logged into. I am greeted by this error,
This app isn't verified
which unfortunately does not provide competent documentation to troubleshoot.
Any feedback or help would be much appreciated! Thanks!
Click on the "Advanced" link and you'll be able to authorize your script.
To reduce the scope of permissions you request, you also have the option of declaring your script project to be only able to interact with the bound document:
/* #OnlyCurrentDoc */
function myFunction() {
...
This declaration is incompatible with some methods (such as SpreadsheetApp.openById()), and using an incompatible method results in an error in the application execution.
Successfully adding it to your project is generally sufficient to remove the "This application is unsafe" layer of the authentication flow, meaning the authorization and permission list is not hidden behind the "Advanced" tab.
In addition to declaring as current document only, manually editing the requested scopes of your project in its project manifest can help reduce the perceived threat from an unverified application (for example, retaining only the "read_only" version of certain scopes, where applicable). Apps Script documentation offers more details on project manifests.

Duplicate Namespace Including Google Apps Script Library

I've forked this OAuth2 library for Google Apps Script, uploaded it to my Drive as a separate Script, and swapped the SCRIPT ID of the upstream repo with my fork to test changes I've made to the library. However, when I run functions that call library functions, such as OAuth2.createService, I get this TypeError:
Cannot find function createService in object [object Object]
The OAuth identifier is another level down, inside another OAuth object. So the script works with my library when run OAuth2.OAuth2.createService. My fork doesn't have any changes that would do this.
It's not the biggest deal, but I want make a pull request with this sample code. So my question isn't so much about why isn't this code working, it's why is GAS include essentially the same library in these two different ways?
Here's how I've included the libraries in my script.
I'm sure that you are using the built code instead the library directly. You need to modify the code from src folder. Skip dist folder always.
Issue #70