Apps Script Needs to Check Permissions and Request onOpen - google-apps-script

Is there a way to check OAuth permissions from the onOpen() simple trigger in apps script?
My app creates and shares spreadsheets with my users, and when they open the spreadsheet, I need to be able to check if they've authorized the required scopes yet. Simple triggers can't natively run functions that require authorized scopes. But installable triggers can. I've created a function that will prompt the user for permissions and then install the needed triggers with those scopes. I have a custom menu item that runs this function just fine, but the simple onOpen() trigger can't run that function because it requests OAuth scopes permissions. Many of our users wont realize they need to run that function from the menu for the spreadsheet to work.
My initial idea was to have a hidden cell somewhere in the spreadsheet with a true/false value that starts as "false". When the user opens the spreadsheet, the onOpen() simple trigger:
checks that cell's value
if it's false then pops up a ui alert for the user.
The alert tells them to use a custom menu that onOpen() added which will run a function that will:
prompt the user for permissions
add onOpen and onEdit installable triggers for that user so the rest
of my app works for that user when they interact with the
spreadsheet.
change the hidden cell's value to "true" so that the simple onOpen()
trigger doesn't keep prompting the user.
This works great! But only for the first user who accesses the spreadsheet. Once they authorize the permissions, that hidden cell turns to "true" and subsequent users wont be prompted to run the menu function.
My second idea was instead of having that hidden cell as a true/false value, make it a list of the user's gmail accounts that have given permissions (accessed via Session.getActiveUser().getEmail()). But in order to get the current user's email, I need a OAuth scope! So the simple onOpen() trigger cannot even check if the current user is a new user or not...
Is there another way to check if the active user has authorized the OAuth scopes needed from the simple onOpen() trigger?
I don't want to just rely on my users to know that they have to run that special menu function in order for things to work.

You can use ScriptApp.getAuthorizationInfo()
if (ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL).getAuthorizationStatus() == ScriptApp.AuthorizationStatus.NOT_REQUIRED) {
// user has authorized the OAuth scopes
}

Related

onSpecialEdit() for Google App Script on a sheet with many users executes for all, each time

This can't be right. What am I missing?
I have a Google Sheet with scripts. The scripts are using certain ui functions that require authentication and special permissions coming from an onEdit() event. Therefore, I had to create onSpecialEdit() and the custom trigger to run that function with event source of the spreadsheet and event type onEdit(). This all works fine. Script does what it should.
From what I've seen, each person who needs to have this ability and access the special edit will need their own trigger. That is created for them automatically. Triggers all work for each person.
Now, what I see is that when I do an action that triggers onEdit(), it appears to run the onSpecialEdit() 20 times...once for each person who has that trigger....I'm assuming.
This can't be right. What am I missing? Is there another way to create triggers where they are only executed by the person triggering? Is this approach wrong?
Issue:
You want to have an onEdit trigger that has access to services that require authorization.
These services should have access private of the user who has caused the trigger to run by editing the spreadsheet.
Since the services require authorization, it cannot be a simple but an installed trigger (Restrictions).
Since installed triggers act on behalf of the user who installed them, they can only access data of the user who installed them.
Therefore, each user whose data should be accessed needs to install the trigger, so multiple triggers should be installed.
A trigger runs whenever any user edits the spreadsheet, it doesn't have to be the user who installed the trigger. Because of this, all of the installed onEdit triggers will run whenever any user edits the spreadsheet.
Solution:
Use Session.getActiveUser() and Session.getEffectiveUser() to check whether the user editing the spreadsheet (getActiveUser) is the same as the one who installed the spreadsheet (getEffectiveUser). If it's not, stop the function execution. This way, only one of the multiple onEdit triggers will make the corresponding actions.
Code sample:
function onEditTrigger(e) {
const active = Session.getActiveUser().getEmail();
const effective = Session.getEffectiveUser().getEmail();
if (active === effective) {
// TRIGGER ACTIONS
}
}

Sheet embedded function -only works for spreadsheet owner, not "anyone"

Long time learner/lurker, first time poster:
Howdy folks-
I've deployed a web-app that runs as the user (not me). It creates a form and a linked-spreadsheet in the
users drive with "anyone" access and "edit" permissions.
Bound to this spreadsheet is an installable "onOpen" trigger that creates a UI menu item to the right of the
spreadsheet's "help" menu, and on that menu item is a function that manipulates the data in the spreadsheet.
That all works properly -for the user who created the form+spreadsheet.
Here's my problem:
The use-case for my web-app is to allow someone to create the form and spreadsheet then hand it off to
someone to use the form and view responses and tally votes via the spreadsheet.
Because the spreadsheet's access is "anyone", the person to whom the spreadsheet is handed-off can EDIT
the spreadsheet, but the "onOpen" trigger never fires unless and until the opening-user signs in.
Then, the next problem:
Assuming the handed-off person signs in and sees the new UI menu item, they cannot successfully RUN the
function on the menu. It appears only the user that originally created the form+spreadsheet is the one
who can run the funtion from the UI (addMenu) menu.
Questions:
what's the best/recommended way to detect a user is not signed-in ..so I can present a modal alert
when the spreadsheet is first opened? The goal is to alert the user that if they signed-in, they would
see the menu item by activating the installable trigger.
Can a modal alert be presented when a user is not signed-in? I'm not sure how that code would be triggered
too, as it's likely the same problem as not triggering the onOpen UI-addMenu code
what's the best/recommended way to set the permissions for the spreadsheet's (embedded) menu-function -so
the user opening the spreadsheet can run it vs. only the original creator of the spreadsheet.
web-app example: https://forms.fattm.org/contest-form-creator
(several forms can be created, but the problem described above relates only to the form
named: CONTEST BALLOT (Item 1179) ..so you can un-check the others before hitting submit)
Thanks for your time/thoughts/help!
Welcome to SO!. The script you have that creates a custom menu using onOpen is a container-bound one, bound to the spreadsheet you are sharing. However the onOpen function is a trigger function which must be installed first (for each user that wants to use it) There is no way to get around this, unless you publish an add on, in which case the users would still have to install the add on the sheet you are providing.

Google Apps Script AddOn: onChange trigger programatically with misleading permission scope text

By Google documentation, an onChange trigger for a Spreadsheet addon has to be created programmatically via ScriptApp.newTrigger(...)....
newtrigger requires the scope :
https://www.googleapis.com/auth/script.scriptapp
When the user is asked for granting permissions, the text presented is:
Allow this application to run when you are not present
I understand this function can create time based trigger, but for a simple onChange in a Spreadsheet, I would understand if the user declines it due to that text.
Is there a way to have an onChange trigger without this scope for changes (not onEdit) in spreadsheets?
Answer:
The only way to create a trigger on behalf of a user programmatically is using ScriptApp.newTrigger(), and this method requires the https://www.googleapis.com/auth/script.scriptapp scope. No other scope will allow this to run.
You can however set up an onChange() trigger for the Spreadsheet, which will run regardless of who makes the change to the sheet.
More Information:
When you set up a trigger, you are allowing an app/script/function to run, on behalf of you, without you manually running the function. Asking a user to create a trigger will require them to authorise that they consent to the script doing something on their behalf.
You can however, set up the trigger yourself. As long as the function that will run on the trigger doesn't violate any of the installable trigger restrictions then this will run on the Sheet as long as it has been authorised to run by you.
Remember though, that if you set up the trigger yourself, the script will always execute as you, not as the user. So if the function, for example, sends an email, then the email will be sent on behalf of you - as you were the one that authorised the script.
Setting up an Installable Trigger manually:
If setting up the trigger to run as you is an acceptable solution, you can do so by following these steps:
Save the script with the save icon, press the run button (►), and confirm the authentication of running the script.
From here, following the Edit > Current project's triggers menu item, you will have a new page open in the G Suite Developer Hub. Click the + Add Trigger button in the bottom right and set up the trigger settings as follows:
Choose which function to run: <your-function-name>
Choose which deployment should run: Head
Select event source: From Spreadsheet
Select event type: On change
And press save.
This will now run on all changes made to the sheet - regardless of who made the change. Just remember that as far as execution goes, you're the script runner.
References:
Class ScriptApp | Apps Script - .newTrigger(functionName) method
Installable Triggers | Apps Script - Restrictions
Event Objects | Apps Script - Google Sheets events: Change

How to run a spreadsheet bound script as the creator (not viewer/editor)

I made some script with my spreadsheet, which uses some Trigger and SendEmail functionalities. I have menu items in spreadsheet to control these triggers and sendemails.
Now when I share this spreadsheet with someone other, when he tried to access the trigger or sendemail functions from menu, the script asks for authorization as that user. IF authorized it will function as that user. e.g. send email as that user or make a new trigger as that user. This makes things double and useless
I want that any user accessing the script can use those functionalities, but won't require authorization to run as that user. The script should run as the creator of the sheet, so that no double triggering occurs. How should I do it?
You could create a web-app. Web-apps have the ability to run either as the user himself, or as the developer/publisher.
Under Execute the app as, select whose authorization the app should
run with: your account (the developer's) or the account of the user
who visits the app (see permissions).
https://developers.google.com/apps-script/guides/web
This web-app wouldn't have a sheet linked to it, but if you only use 1 sheet you can have the web-app access the sheet through the ID of the sheet. You could use your existing menu-items to trigger the webapp
Would this be a possible solution for your problem?

Script triggered onEdit runs into permissions error when trying to edit protected sheet

I have a problem with a Google script for a spreadsheet. I have a script installed onEdit through the Resources menu. The script copies some cells from an unprotected sheet to a protected sheet. The script works fine when I'm logged in to my account (I'm the owner of the spreadsheet), but runs into permission errors when run from a collaborator's account who has no access to the protected sheet. Shouldn't the script run as me (the account owner) and not run into permission issues?
Another script in the same spreadsheet that runs onFormSubmit is able to edit a protected sheet when a collaborator submits a form, even though he has no access to the protected sheet. This suggests the problem is not with permissions per se, but that the problem is specific to the onEdit trigger, right?
This is expected behavior. The onEdit trigger is one of three 'simple triggers'.
From the docs:
"These simple triggers run in response to actions in Google Spreadsheets, and they run as the active user. For example, if Bob opens the Spreadsheet, then the onOpen function runs as Bob, irrespective of who added the script to the Spreadsheet. For this reason, the simple triggers are restricted in what they are permitted to do:
They cannot execute when the Spreadsheet is opened in read-only mode.
They cannot determine the current user.
They cannot access any services that require authentication as that
user. For example, the Google Translate service is anonymous and can
be accessed by the simple triggers. Google Calendar, Gmail, and Sites
are not anonymous and the simple triggers cannot access those
services.
They can only modify the current Spreadsheet. Access to other
Spreadsheets is forbidden.
Have to use installed trigger for it to work