Can the Google Apps Script Execution API be called by a service account? - google-apps-script

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

Related

Do AdminDirectory advanced service APIs only work for admins?

I've got the following function in an unpublished Google Apps Script
function getUserFromEmail(userEmail)
{
return AdminDirectory.Users.get(userEmail)
}
I've enabled the AdminDirectory advanced service in the script
As an admin, I run the script, and the default OAuth2 consent screen pops up.
I authorize, and the script runs as expected.
I share the script with a non-admin collaborator (Editor)
When the collaborator runs the script, an error is generated
GoogleJsonResponseException: API call to directory.users.get failed with error: Not Authorized to access this resource/api
Yes, I understand that the collaborator does not have the admin privileges necessary to make this call. I can explicitly give the collaborator the necessary permissions via admin console, but that is not a good solution, since I may want a group of non-admin users to run the script (you can't assign by group). Since there is no explicit way of controlling authentication from Google Apps script (aside from URLFetchApp), it appears that the Advanced Services APIs only really work for admins running the script. Creating a GCP, service account, domain-wide delegation, etc. will not help, since we cannot explicitly control authentication from GAS Advanced Services. We are forced to use the URLFetchApp knothole. Most of the questions/posts for this topic a 5-7 years old, and the landscape for the developer platform has changed substantially. Also, Google developer platform documentation is woefully out of date and refers to entities that no longer exist (like Google APIs Console in https://developers.google.com/admin-sdk/directory/v1/guides/prerequisites)
Is this analysis correct, or have I missed something?
Tried converting project to CGP standard project, enabling Admin SDK library, service account, domain-wide delegation

How do you access the Google Sheets API via a Google Apps Script?

I am trying to call the Google Sheets API via a Google Apps Script. As an example, I have followed the instructions provided here:
https://developers.google.com/sheets/api/quickstart/js
I have generated the client ID and API Key via my Google Cloud Platform project as suggested elsewhere.
Now, I have created a google apps script with two files, the Code.gs file and a file called index.html containing the code copied and pasted from the Google Sheets API Quickstart above (with my client ID and API inserted).
When testing deployment, I get the following error message:
{
"error": "idpiframe_initialization_failed",
"details": "Not a valid origin for the client: https://<some numbers>-script.googleusercontent.com has not been registered for client ID <MY_CLIENT_ID>. Please go to https://console.developers.google.com/ and register this origin for your project's client ID."
}
I have registered script.google.com as an origin for my project.
Am I doing something obviously wrong? How would I proceed from here to get past this error?
If you want to make use of the Sheets API in Apps Script you will have to make use of the Sheets Advanced Service.
For this you will have to go to Services + and then add the Google Sheets API:
Since this is an advanced service, the authorization flow will be handled by Google so you won't have to provide the clientId and the API key.
Reference
Apps Script Advanced Google Services.

OAuth Bug in Apps Script using YouTube Data API

I have used Apps Script successfully on many occasions, and one of the reasons I like it, especially for personal enhancements or projects related to Google Services is just how seamlessly it integrates auth. However, when trying to integrate the YouTube Data API into one of my Google Sheets' Apps Scripts (I am trying to use the sheet to manage a YouTube playlist), I encountered an error that I have never encountered before.
The code is very simple, I am just trying to get some data from a playlist to return to the logger in the context of my Google Sheets Apps Script. Note that this Apps Script belongs to the same account as the YouTube playlist. The OAuth Client Verification docs specifically state:
Note: Verification is not required for Apps Script projects whose
owner and users belong to the same Google Workspace domain or
customer.
However, when I run my script, the OAuth screen says the app is unverified (this has never happened when I have used any other APIs accessing my own account in Apps Script), and even though I authenticate and it says "Authentication Successful", the script is blocked and it repeatedly (as in forever, in an endless loop) asks me to authenticate again.
Completely at a loss for what is going on. 1.) I shouldn't have to verify this script per the docs I referenced above, and I have never had to before for accessing my own content. 2.) The successful authentication but then failing and repeatedly asking me to authenticate again is driving me mad.
Please advise!
Code is very simple, just trying to get this to return ANYTHING:
const syncVideos = () => {
let response = YouTube.PlaylistItems.list('snippet,contentDetails', {'playlistId': '<REDACTED>'});
Logger.log(response);
}
Answer: This turned out to not be a code or OAuth issue really, but more of an unintuitive procedure when authenticating, i.e. when authenticating with Google to access one of your channel's data through the YouTube Data API, authenticate with your main channel, even if requesting data from other channels connected to your account.

Send message to Google Chat using the REST API (Google example not working in 2020)

Where do I even begin... (Google, why must you hurt me this way?)
Background Info
I have created a new chatbot using Google Apps Script, which receives messages from users in Google Chat and responds synchronously with a single message (each message can only have one response from the chatbot).
Now I need a way to send asynchronous messages so that the bot can send messages on its own, or send multiple separate responses at a time.
The problem
The Google Chat REST API has a method to create a message asynchronously, but this method (spaces.messages.create) does not work! There are no working examples of this method from 2020.
Here is Google's example code for creating a message using the REST API.
The problem is that in their example, the SCOPE is set to a URL that no longer exits:
var SCOPE = 'https://www.googleapis.com/auth/chat.bot';
If you navigate to that URL, you will see this 404 error:
Not Found
Error 404
Furthermore, if you check the list of available OAuth2 scopes, you will notice that there are no scopes related to Hangouts or Chat, and there is no mention of the chat.bot the scope which was used in the example code.
What have I tried?
I have read through every question on StackOverflow that is related to this Chat API, plus every tutorial for the REST API.
The official Apps Script tutorial from Google does not work because the chat.bot scope no longer exists:
Async Messages using Apps Script
These StackOverflow solutions all make use of the same non-existant chat.bot scope:
Send private message without event
Asynchronously Respond in new Hangout Chat using rest API
404 truncated server response on Apps Script Bot
This StackOverflow user says they were able to use the chat scope (i.e. googleapis.com/auth/chat), but that scope does not exist either:
Error 400: invalid_scope
In conclusion
How to send messages from Google Apps Script to Google Chat using the Google Chat REST API?
It seems that Google's documentation is outdated, and none of the examples for this API work as of August 2020. They are either unaware that their REST API does not work, or they deprecated the REST API without telling anyone.
Answer:
I can confirm that the chat.bot scope does indeed exist. To set up a chat bot with the REST API, you must use a service account.
More Information:
As per the documentation you linked on Developing bots with Apps Script, for sending async messages on trigger:
...the only way to achieve this currently is via the external HTTP API (see documentation). This requires the use of a Cloud service account (see documentation) via the OAuth2 for Apps Script library.
This means, that you must first set up a service account in the GCP console so that the chat.bot scope can be used for these messages. The whole process can be quite arduous for the unintitiated, so I will provide the steps from start to finish here.
The Process:
Creating a Service Account:
Navigate to the Google Cloud Console and create a new GCP Project. Hit Select a project at the top of the page and click NEW PROJECT.
You will need to provide a Project name, the other fields should be filled out for you automatically.
Press CREATE - a new pop-up will appear in the top-right of the screen confirming that a new project is being created. Once loaded, you can click VIEW.
Click the ☰ icon in the top-left, and follow the APIs & Services > Credentials menu item.
At the top of this page, click + CREATE CREDENTIALS > Service Account.
Give the service account a name and a description, and press CREATE, followed by CONTINUE, and finally DONE.
Your service account has now been created.
Creating Service Account Credentials:
These will be needed for the code provided in the example from the Developing bots with Apps Script page.
After creating the Service Account, you will be redirected back to the list of Credentials you can use for the GCP Project. Under the Service Accounts section, click you newly-created service account. This will be called service-account-name#project-name-XXXXXX.iam.gserviceaccount.com
Click ADD KEY > Create new key
Keep JSON selected, and press CREATE.
This will initiate a download of a credentials file which you will need to use to access the API as this service account. DO NOT LOSE OR SHARE THIS FILE. If lost, you will need to delete and create new credentials for this account.
Enabling the Hangouts Chat API:
Going back to ☰ > APIs & Services, and select Library.
Search for Hangouts Chat API and click the only result.
Click ENABLE. This will enable the API for your project.
Note: Do not close this tab yet! We will still need to use the GCP console later.
Setting up the Apps Script Project:
Create a new Apps Script project.
Now, you can copy + paste the example from the Async messages page into the new project.
Open up that credentials file that you downloaded from the GCP console.
Copy the private_key value (the one that starts with -----BEGIN PRIVATE KEY----- and paste it into value of SERVICE_ACCOUNT_PRIVATE_KEY in the Apps Script project.
Also copy the client_email value from the credentials file, and paste it into the SERVICE_ACCOUNT_EMAIL in the Apps Script project.
In order to use the Google Apps Script OAuth2 library as in the example, you will need to add the library to the project using the library's script ID.
In the Apps Script project UI, follow the Resources > Libraries... menu item, and copy paste the OAuth2 script ID into the Add a library box
The script ID is 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
This, and the rest of the library can be found on the the OAuth2 for Apps Script GitHub repository
Make sure to select the latest stable version of the library (at time of writing, this is version 38)
Press Save.
Next, you will need to link the Apps Script project to the GCP project you created earlier.
Go back to the GCP Console tab, and follow the ☰ > IAM & Admin > Settings menu item.
Copy the Project number defined on this page.
In your Apps Script Project, follow the Resources > Cloud Platform project... menu item, and paste the Project number into the Enter Project Number here dialog.
Click Set Project.
Setting up the Project Manifest:
In order to use a chat bot in Apps Script, you must include the chat key in the project's manifest.
In the Apps Script UI, click View > Show manifest file.
After the last key-value pair, add the following:
"chat": {
"addToSpaceFallbackMessage": "Thank you for adding me!"
}
Your full manifest file will now look something like this:
{
"timeZone": "Europe/Paris",
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Drive",
"serviceId": "drive",
"version": "v2"
}],
"libraries": [{
"userSymbol": "OAuth2",
"libraryId": "1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF",
"version": "38"
}]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"chat": {
"addToSpaceFallbackMessage": "Thank you for adding me!"
}
}
Save your project.
Final Steps:
You're nearly done! Now, you will need to deply the bot from manifest, and set up the configuration in GCP and set up the trigger which will make the actual call.
Deploying the bot:
In the Apps Script UI, go to Publish > Deploy from manifest... and hit Create in the newly opened dialog.
Note: You can not use the Head deployment if you want to use this for your whole domain, so a new deployment must be created.
Give the deployment a name and description, and press Save.
Once this has finished saving, press Get ID next to the deployment you just created, and copy the Deployment ID.
Setting up GCP configuration:
Going back to the Cloud console, you will need to now navigate to ☰ > APIs & Services > Dashboard.
In the list of enabled APIs at the bottom of this page, select the Hangouts Chat API.
On the left menu, select Configuration.
Set up your bot configuration. You will need to provide a Bot name, Avatar URL, and Description. Set up the functionality settings so that it works in rooms.
Under Connection Settings, select Apps Script project, and paste in your deployment ID from the previous section.
Give your Apps Script bot the relevant permissions, and press Save.
The Elusive Trigger:
The only thing you now need to do is set up your trigger. This is done like a normal Apps Script trigger - from the Edit > Current project's triggers menu item in Apps Script. To complete the example, click the + Add Trigger button in the bottom right and set up the trigger settings as follows:
Choose which function to run: onTrigger
Choose which deployment should run: Head
Select event source: Time-driven
Select type of time based trigger: Minutes timer
Select minute interval: Every minute
And press save.
And you're done! This created bot will now post to all rooms that it is in the current time, every minute.
References:
Service accounts | Cloud IAM Documentation
Understanding service accounts | Cloud IAM Documentation
Developing bots with Apps Script | Google Chat API | Google Developers
Bot-initiated messages - Creating new bots | Google Chat API | Google Developers
GitHub - gsuitedevs/apps-script-oauth2: An OAuth2 library for Google Apps Script.
Google Cloud Console

Making a request to the Google Apps Script API through postman

I am attempting to remotely execute a google apps script function using the apps script api through a postman request.
I first get credentials from the Google API Playground
Then they are inputted into postman as OAuth2 credentials, and inputted in the headers like this:
Then the appropriate link is requested:
Then the body prepared:
And then the response after making the request:
Postman is returning an error 403, which, according to Google's documentation
indicates that "the Cloud Platform project used to authorize the request is not the same as the one used by the script." An error 403 is an authorization error and can mean many things, but let's assume that the error is what google proclaims it to be.
I have full control over the GCP project utilized by my script, but I'm not aware of where to find the project that was used to authorize the request.
Where might I gain access to this GCP project so I can assign my script to this project, thereby eliminating the 403 error?
Thanks!
To use OAuth Access Tokens from the OAuth Playground with Apps Script, you need to specify the correct Client ID and Client Secret from the same project.
In the OAuth Playground. Click on the gear icon (top right). Select "Use your own OAuth credentials". Then enter the Client ID and Secret ID created in the same project as Apps Script.
To query a Google URL using a valid Access token with Postman you can log the access token from apps script and use it after.
In your apps script after validating scope, i.e. running a first time the script, log the token :
function logToken(){
Logger.log(ScriptApp.getOAuthToken());
}
Then in Postman query the Google URL by setting in the header the access token :
"Authorization": "Bearer THE_ACCESS_TOKEN"
Security warning : for security reason I have to tell you that an access token is valid 1 hour so technically if you grant full drive scope to your app with this access token we can browse all your Drive.