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.
Related
I'm trying to create a Google Apps Script (GAS) library that I can reuse across my Google Sheets spreadsheets. Following these instructions, here's what I've done so far:
Created a new project in Google Cloud Platform
Enabled Google Sheets API for project in step 1
Set up OAuth 2.0 authentication for project in step 1
Credential type = User data
Scopes = https://www.googleapis.com/auth/spreadsheets (I cannot use spreadsheets.currentonly because one method in this library requires the full spreadsheets scope)
OAuth Client ID = Web application
Configured OAuth consent screen
Publishing status = Testing
User type = External
Test users = Gmail email I used to write the script
In Apps Script editor:
Set the Google Cloud Platform (GCP) Project to Standard by associating it with the Project Number from step 1
Updated appsscript.json to look like this:
{
"timeZone": "America/Los_Angeles",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "Sheets",
"version": "v4",
"serviceId": "sheets"
}
]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets"
]
}
Created a versioned deployment of my script following these instructions
Shared my GAS project as "Anyone on the internet with this link can view"
And here's the issue I'm facing:
Created a new Sheets using the same Google account that I used to create the GAS library
In the Scripts editor, I added my GAS library by entering the Script ID, then selected the HEAD version (it doesn't seem to matter -- all versions resulted in the same error)
In Code.js, wrote a test function that calls a function from my GAS library
Clicked the Run button to execute the test function; the following prompt appears:
Clicked "Review permissions" and selected the Google account I used to create the GAS library
ISSUE: This error page is displayed:
Now the strange part: if I repeat steps 1-5 above using a different Google account (i.e., not the same account I used to create the GAS library), then it works! I get the expected OAuth consent screen, and after granting permissions, I'm able to use my GAS library.
Has anyone figured out how to reuse a GAS library in a Sheets script that is under the same Google account as the one that was used to create the GAS library?
I just wrote about this here: https://aimanfikri.com/2022/05/09/this-app-is-blocked-error-on-google-apps-script-solution/
In summary:
Configure a 'standard project' on Google Cloud Platform.
Set user access under 'Scopes'
Add relevant user accounts (if you don't have an enterprise Google Workspace)
Change your Apps Script project to the newly configured project using the new 'project number'.
If the app is blocked for your account, there is an easy approach to allow it. Just keep in mind that you would need to be an admin in the domain in order to run these changes. Follow this guide on allowing apps and complete its steps. Feel free to leave a comment if you need more help.
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
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.
I'm hoping to automate some HR work by running a Google App Script via the Execution API. Without getting too much into the details, I'd like to pass employee evaluation data as a parameter into the App Script. The script will then use this data to compile an "Employee Review" GDoc.
So far, I have ran a simple test App Script using the Execution API. For example, I can successfully run a simple function which logs a string or interacts with spreadsheets. So far so good.
But I run into problems when trying to write to a GDoc (which is unfortunately integral to my task). Here's my paired down script:
// TODO: Eventually, we'll pass these variables as arguments
var docId = "MY-DOC-ID";
// Find the team member review doc
var doc = DocumentApp.openById(docId);
// Replace placeholder text
var docBody = doc.getActiveSection();
docBody.replaceText('{{DATE}}', "Date set by App Script!!!");
doc.saveAndClose();
This script works when I press the "Run" button in the App Scripts web UI. But when I try to run via the Execution API, I get:
{
"error": "unauthorized_client",
"error_description": "Unauthorized client or scope in request."
}
So apparently I haven't provided the correct scope? Following the docs, I can find the necessary scope(s) in Project Properties > Scopes which says:
But when I try adding that scope, it wont work. As I said other scopes (e.g. https://www.googleapis.com/auth/spreadsheets) work just fine. Perhaps the auth/documents scope is no longer supported or there's a bug in their API?
Questions
What is the correct scope? I can see a big list here but I don't see https://www.googleapis.com/auth/documents, so?
Any other suggestions? For example, is it possible to write to a Google Doc using the Google Client API directly (i.e. without using App Scripts)?
Doh. I figured out the solution to my problem. While it was a dumb mistake, it's nevertheless worth posting as it may save others confusion in the future.
First, a little context about my setup. I'm authenticating to the Google Client API using a Service Account. Furthermore, as is common when using a service account setup, I am impersonating a user within our organization (specifically my own account).
My missing step (obvious in hindsight)...
Log into the App Script web UI as the person you are impersonating.
Manually run the script by pressing the play button
If the impersonated user has not already granted permissions to access the required scopes, you will be prompted to do so.
After granting access (specifically for the https://www.googleapis.com/auth/documents scope), my authorization error disappeared.
So the lesson: Make sure the account you are impersonating has granted access for all the scopes which your script requires.
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).