Scope needed for getAttachments? - gmail-addons

In a Gmail Add-on, this line:
var attachments = message.getAttachments();
fails with a message Access denied: : Not allowed for full access to mail message.
The appscripts.json manifest file contains:
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata",
"https://www.googleapis.com/auth/gmail.addons.current.message.action",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/script.locale"
]
What else is needed? I've not found any mention of which scope is required for getAttachments.

You can include this
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
and remove these 2:
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata",
"https://www.googleapis.com/auth/gmail.addons.current.message.action"
This works for me.

Related

How to manage Google Contacts via Google Apps Script? (errors with permission)

I have tried all sorts of things I have read and can think of:
Added the scope to the manifest file:
{
"timeZone": "America/Fortaleza",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "People",
"version": "v1",
"serviceId": "peopleapi"
}
]
},
"oauthScopes": [
"https://www.google.com/m8/feeds",
"https://www.googleapis.com/auth/spreadsheets"
],
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
Enabled the People API in Services
I've deleted all apps permissions and started from scratch
I've tried changing the browser, in case cookies were a possibility
This is how I'm at this point trying to get a contact:
function onEdit(e) {
var sht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Contacts");
var rng = e.range;
var row = rng.getRow();
var drng = sht.getRange(row, 2, 1, 22).getValues();
var email = sht.getRange(row, 2, 1, 1).getValue();
var gmailContact = ContactsApp.getContactsByEmailAddress(email);
Logger.log(gmailContact);
}
This is the error I'm getting:
You do not have the permission to call ContactsApp.getContactsByEmailAddress Required permissions: https://www.google.com/m8/feeds
I'm trying to read contacts now and will get it updated as I edit a row containing an existing contact.
Appreciate your help!
As pointed out by #Tanaike, the issues is due to the fact that this currently running on a simple trigger basis and not on an installable one. Thank you!
Reference:
Simple Triggers > Restrictions:
They cannot access services that require authorization.
Installable Triggers
Installable version can call services that require authorization. The installable version runs with the authorization of the user who created the trigger, even if another user with edit access opens the spreadsheet.

Session.getActiveUser().getEmail() gives two values when creating a sidebar and when calling a appscript method from templated html

I have been using https://github.com/labnol/apps-script-starter as a starter guide to create app script projects for Google Slides.
But I came across an issue related to the context (active user) that the appscript function is called. While calling the method Session.getActiveUser().getEmail() in the onOpen() function I get the correct active account but when I call the same function from a templated HTML I get the default account for that browser. Has anyone faced this issue? A solution will be great.
const onOpen = (e) => {
Logger.log(Session.getActiveUser().getEmail());
Logger.log(Session.getEffectiveUser().getEmail());
..
}
const customFunction = () => {
Logger.log(Session.getActiveUser().getEmail());
Logger.log(Session.getEffectiveUser().getEmail());
..
}
const showSidebar = () => {
const template = HtmlService.createTemplateFromFile('gra-main-v3');
template.include = include;
const html = template.evaluate().setTitle('New Sidebar');
SlidesApp.getUi().showSidebar(html);
};
gra-main-v3.html
<HTML>
...
<script>
google.script.run.customFunction();
</script>
..
</HTML>
I have two google accounts logged in to the same browser emailA#gmail.com (default) and emailB#gmail.com. When using the default account everything works as expected, but while using the second account the custom function called from the templated HTML gives the wrong result.
Case 1 When emailA#gmail.com is active:
Results in onOpen = emailA#gmail.com
Results in customFunction = emailA#gmail.com
Case 2 When emailB#gmail.com is active:
Results in onOpen = emailB#gmail.com
Results in customFunction = emailA#gmail.com
appscript.json
{
"timeZone": "Asia/Calcutta",
"runtimeVersion": "V8",
"dependencies": {
"enabledAdvancedServices": [],
"libraries": []
},
"webapp": {
"access": "ANYONE",
"executeAs": "USER_ACCESSING"
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/presentations.currentonly",
"https://www.googleapis.com/auth/presentations",
"https://www.googleapis.com/auth/userinfo.email"
]
}
This is a multi-account sign-in issue. Please navigate to the corresponding issue on issue tracker and star it, so that Google knows it's important to solve.
Meanwhile a couple of work-arounds have been proposed, which both boil down to alerting the user that there is a multi-account issue:
By Romain Vilard
Comment 117 in the tracker.
The idea is to compare the effective user to the current user, or a document owner to the current user, and if they are not the same, then alert the user that they need to sign out out of other accounts.
That's the best we can do for now.
From the documentation:
getEffectiveUser():
Gets information about the user under whose authority the script is running. If the script is a web app set to "execute as me" (the developer), this returns the developer's user account. If the script is running under an installable trigger, this returns the account of the user who created the trigger. In most other scenarios, this returns the same account as getActiveUser().
So when you call the function from templated HTML, it return the account that own this script (emailA#gmail.com I guess).
You can set Execute as to `User accessing the web-app' if necessary (also reffer to add-on).

Google Apps Script API Authorizations for DriveApp.getFileById

I have a Google Cloud Platform Project (GCP) that runs almost all scripts/functions directly, but a couple through an API. Everything is only accessible internally to the G Suite domain, and the OAuth consent screen Application Type is "Internal".
I have a script, which is not called via an API, but directly on a timer every x minutes. It performs a
DriveApp.getFileByID(pictureID)
This works great! No problems.
I also have a different script, in the same GCP Project, which instead of running triggered by a timer, runs by being called through an API. Here is the line of code that calls it (not really important):
var result = UrlFetchApp.fetch(url, options);
The API script runs great! Until it gets to the following lines:
try { var file = DriveApp.getFileById(pictureID); }
catch (e) {
Logger.log('e = ' + JSON.stringify(e));
return;
}
The result of the log is
e = {"name":"Exception"}
I verified that the pictureID is the same as it is in the non-API script that works. I am doing a "try" in this API-run script to make sure that the software has access to the file, not to actually access it.
I am pretty sure that this is an authorization issue. I've been using GCP only for a little while now, have some experience with authorizations, but not a lot.
Here are some details around the authorizations...
The project's scripts Project Properties (File/Project Properties) shows that it needs the following OAuth Scopes:
According to Google's documentation at https://developers.google.com/apps-script/reference/drive/drive-app#getfilebyidid,
Scripts that use this method require authorization with one or more of the following scopes:
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive
Here are the scopes that I've defined now on the GCP Oauth consent screen:
As you can see, I've added drive, drive.readonly, & drive.file (which doesn't really seem to be needed).
On top of it all, this particular image file is stored in the Google Drive of the owner of the GCP Project, scripts, and top-level admin of the G Suite domain. That will not always be the case, as users will be sharing images from their own Google Drive to this software/GCP owner. However, I have a feeling that even now the script triggered by a timer would work with those user-shared files, but not the script called through an API.
I'm pretty sure this is an Auth issue, but I am missing something somewhere.
Thank you for your help!
Update:
Here is the code from the script that CALLS the API script (changed some for confidentiality), as I'm wondering if perhaps the problem may not be on the client/calling side. Perhaps I'm not getting the OAuthToken correctly? Or the token doesn't have the correct permissions?
var token = ScriptApp.getOAuthToken();
var header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + token,
};
var parms = [id];
var data = {
"function": "updateSettings",
"parameters": parms,
"devMode": true,
}
var options = {
"method":"POST",
"headers": header,
"muteHttpExceptions": true,
"payload": JSON.stringify(data)
};
// Call the API
var result = UrlFetchApp.fetch(url, options);
I figured it out after contacting Google, and unfortunately not getting much help, and a few more hours of trying and research...
What I was missing was adding "oathScopes" to the manifest/appsscript.json file. I played around with the scopes needed and ended up with the following two shown below. I figured out the solution by looking at this answer: Using AuthToken obtained via ScriptApp.getAuthToken() to call web apps in GAS.
Inside the script editor, go to View/Show manifest file.
Here is what it looked like before:
{
"timeZone": "America/New_York",
"dependencies": {
},
"webapp": {
"access": "MYSELF",
"executeAs": "USER_DEPLOYING"
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
and here is what it looks like now...
{
"timeZone": "America/New_York",
"dependencies": {
},
"webapp": {
"access": "MYSELF",
"executeAs": "USER_DEPLOYING"
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/script.external_request"
]
}
After adding, I saved appsscript.json. I then went to my runOnEdit trigger (the calling script), removed and re-added it. However, that caused a nasty looking error:
I looked that up, and found this post: Receiving error when creating new trigger on Apps Script. So, I ran the code in the script editor, and indeed it brought up the auth screen. I approved, re-added the trigger, and now everything works great in calling the API!

Gmail property missing from event fired from gmail add-on contextualTriggers

I'm redeveloping an gmail addon I wrote in the past (yay typescript and clasp) and I'm trying to support mobile. I'm searching the message content using the following to get the message
function onGmailMessage(e) {
console.log(e);
// Get the ID of the message the user has open.
let messageId = e.gmail.messageId;
// Get an access token scoped to the current message and use it for GmailApp
let accessToken = e.gmail.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
This works great on when called from a desktop browser. However, when called from mobile iOS Gmail the gmail property is missing from e. I'm using V8 runtime with the following scopes:
"oauthScopes": [
"https://www.googleapis.com/auth/calendar.addons.execute",
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.googleapis.com/auth/drive.addons.metadata.readonly",
"https://www.googleapis.com/auth/gmail.addons.current.action.compose",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/script.locale",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/script.external_request"],
"runtimeVersion": "V8",
Should this be available, or is there a different approach to support mobile/iOS?
Thanks
-Matt

Apps Script Error - "Execution failed: Login Required"

I have the following line of code in my apps script file, followed by a basic onOpen() function to build a menu:
var listContainers = TagManager.Accounts.Containers.list('accounts/' + accountId);
When that line of code is commented out, the menu is created. But if that line is not commented out, the menu is not created, and the script doesn't run.
When I check the execution transcript (View --> Execution transcript), I see the following error associated with the line shown above:
Execution failed: Login Required
Under Resources-->Advanced Google Services, I have turned on the Tag Manager API, and I have also enabled that API in the Google API Console. The accountId is one for a GTM account that I am the owner of.
This is what my manifest file looks like (View --> Show manifest file):
{
"timeZone": "America/Mexico_City",
"oauthScopes": [
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/tagmanager.delete.containers",
"https://www.googleapis.com/auth/tagmanager.edit.containers",
"https://www.googleapis.com/auth/tagmanager.edit.containerversions",
"https://www.googleapis.com/auth/tagmanager.manage.users",
"https://www.googleapis.com/auth/tagmanager.publish"
],
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "TagManager",
"serviceId": "tagmanager",
"version": "v2"
}]
},
"exceptionLogging": "STACKDRIVER",
"executionApi": {
"access": "ANYONE"
}
}
I feel like all of my grounds are covered here, so I'm not understanding why it is giving me that Login Required error. I have googled up-and-down for a solution, and can't find anything indicating I am doing anything wrong.
Any ideas would be greatly appreciated...thanks!
Global variables can't access external services that require authorization in the same way that simple triggers like onOpen and onEdit as well as custom functions can't access them.
If you really want that listContainers be a global variable You could initialize it at the global scope by doing something like
var listContainers;
Then on a function called by a user interface or an installable trigger assign the tag manager list to that variable in the following way:
listContainers = TagManager.Accounts.Containers.list('accounts/' + accountId);