We're using Gsuite Enterprise/Piazzaweb at work. I'm using basic scripts to get information from my own mailbox, no problems there. But I want to pull information from a couple of shared mailboxes in which I'm delegated. For example billing#mycompany.com. But I can't find any information regarding this and if it's even possible to reach a delegated mailbox with App script.
I've been sending e-mails from the delegated mailbox with app script without any problem.
Hopefully somebody can tell me more about this.
Thanks in advance.
In order to use Service Accounts in Apps Script, you have to do the following:
Build the OAuth service for your Service Account with the following library: OAuth2 for Apps Script.
Call the corresponding API by making the corresponding HTTP request via UrlFetchApp.fetch(url, params).
1. Build the OAuth service:
Once you have added the specified library to your script, you have to do this in order to build the OAuth service with delegated credentials, :
const PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n';
const CLIENT_EMAIL = 'xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com'; // Service account email
const USER_EMAIL = 'email_address_to_impersonate'; // Account to impersonate
const SCOPE = 'https://www.googleapis.com/auth/gmail.readonly' // Change according to your preferences
function getGmailService() {
return OAuth2.createService('Gmail: ' + USER_EMAIL)
.setTokenUrl('https://oauth2.googleapis.com/token')
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
.setSubject(USER_EMAIL)
.setPropertyStore(PropertiesService.getScriptProperties())
.setScope(SCOPE);
}
Where PRIVATE_KEY refers to the key found in the Service Account JSON credentials, CLIENT_EMAIL is the Service Account email, and USER_EMAIL refers to the email address whose INBOX you want to access.
It's important to note that SCOPE should be one of the scopes you specified when delegating authority.
2. Access the API:
Once the OAuth service is built, you use it to retrieve an access token with which you can access the desired resource. Of course, GmailApp service and the Advanced Gmail Service won't be of use here, since you have to access the API using the service you just built. Because of this, the way to access the API will be making the corresponding HTTP request via UrlFetch.
I'm not sure what you want to do exactly, and the exact request would depend on that, but for example, if you wanted to list the messages from the impersonated user, you could do this:
function readInbox() {
var service = getGmailService();
console.log(service.getAccessToken());
if (service.hasAccess()) {
var url = 'https://www.googleapis.com/gmail/v1/users/me/messages';
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
console.log(JSON.stringify(result, null, 2));
}
}
Note:
I'm assuming here that you have already created a Service Account and granted domain-wide authority, and that your issue was just using it in Apps Script.
In Gmail API Reference, check the HTTP request, Parameters and Request body sections corresponding to the API method you want to use, if you need information on how to make the HTTP request (URL to access, HTTP method, request parameters, etc.).
Reference:
OAuth2 for Apps Script
Apps Script OAuth2 > GoogleServiceAccount.gs
You cant use delegation from apps script as apps script doesn't support using service accounts. You would need to make an oauth2 flow and have that user authorize it. Answer provided by #Zig Mandel
On top of what #ZigMandel said: unless this is a google domains or google for work account to my knowledge you cant use service accounts with gmail. you need to use Oauth2. Answer provided by #DalmTo
I don't know if this would solve the problem, but a feature request has been placed on Google Cloud Community and on Google Issue Tracker to enable access to Gmail Apps Script add-ons from delegated mailboxes.
The more upvotes, the more chances Google seriously looks at this issue ;).
Related
Background:
Google Workspace Add-ons for Gmail allow access the currently-open email in the API on contextualTrigger with the scope:
https://www.googleapis.com/auth/gmail.addons.current.message.readonly
An access token must be passed using GmailApp.setCurrentMessageAccessToken(accessToken) in order to grant access to this currently opened email:
var accessToken = e.gmail.accessToken;
var messageId = e.gmail.messageId;
// The following function enables short-lived access to the current
// message in Gmail. Access to other Gmail messages or data isn't
// permitted.
GmailApp.setCurrentMessageAccessToken(accessToken);
var mailMessage = GmailApp.getMessageById(messageId);
From the above documentation:
setCurrentMessageAccessToken(accessToken)
Sets the current message access token that enables the script to access the current GmailMessage properties.
Only Gmail add-on projects using Gmail current message scopes require this method.
Unfortunately, the link to the pages on access tokens and current message scopes are at current both broken and result in 404 pages, so I can't find out more information from here.
Question:
How can one achieve the same funcitonality using the Gmail API directly instead of GmailApp?
The documentation for Gmail: users.messages.get states that https://www.googleapis.com/auth/gmail.addons.current.message.readonly is a valid scope to call this method, however, there does not seem to be an equivalent of GmailApp.setCurrentMessageAccessToken(accessToken) for the Gmail API.
Things that do not work:
Using ScriptApp.getOAuthToken() - as per this documentation, "The access token than enables Gmail scopes is not the same as the access token returned by ScriptApp.getOAuthToken(). You must use the token provided in the action event object."
Providing the access token from the event object as the token in the Authorization: Bearer header also throws a 403 error.
The saught functionality would akin to:
var accessToken = e.gmail.accessToken
var messageId = e.gmail.messageId
Gmail.setCurrentMessageAccessToken(accessToken) // made up method
var mailMessage = Gmail.Users.Messages.get("me", messageId)
I think that in that case, in order to include the access token, how about directly requesting to the endpoint of the Gmail API using UrlFetchApp? When your script is converted, it becomes as follows. In this case, the scope can be selected from Authorization Scopes.
Sample script:
var accessToken = "###";
var messageId = "###";
var userId = "me";
var url = `https://gmail.googleapis.com/gmail/v1/users/${userId}/messages/${messageId}`;
var res = UrlFetchApp.fetch(url, {headers: {authorization: `Bearer ${accessToken}`}});
console.log(res.getContentText())
Note:
In this case, please be carefult about the scopes. Ref
References:
Method: users.messages.get
fetch(url, params)
Added:
Your goal is as follows.
You want to use the method of "users.messages.get" of Gmail API using the scope of gmail.addons.current.message.readonly.
From our discussions in the comment, I summarized the current situation as follows.
When I checked the including scopes from the access token retrieved by ScriptApp.getOAuthToken() for the GAS project which set the manifest file, I could confirm that the scopes of gmail.addons.current.message.readonly and script.external_request.
When I saw the official document, in order to use the method of "users.messages.get", gmail.addons.current.message.readonly is shown as Requires one of the following OAuth scopes:.
But, when the scope of gmail.addons.current.message.readonly is used for the method of "users.messages.get", such error like Missing access token for authorization. occurs.
When the scope of gmail.readonly is included, the error could be removed.
From above situation, how about reporting this to the Google issue tracker? Ref
Answer:
At the moment, this is not possible to do.
Issue rundown:
GmailApp has no method or class to allow labels to be manipulated on individual messages, only threads.
The accessToken provided in the contextual trigger appears to be distinct to the OAuth token and so can not be used to authenticate the call to read the current message.
There is no way to pass the token via the Gmail API so that the current message can be used.
The only way to get the message is if a scope such as gmail.readonly is used.
While https://www.googleapis.com/auth/gmail.addons.current.message.readonly is listed as a valid scope for messages.get, there does not appear to be any way to actually use the scope when connecting to the Gmail API.
Further steps:
I have filed a feature request here to add the ability to manipulate message labels from the GmailApp class in Apps Script. Please star this issue to see its priority increased.
On this note, there was a feature request for this already at this page for the same feature, but as it is over two years old without a response from Google I have filed another.
I have filed documentation feedback for messages.get using the Send feedback button to either have the scope gmail.addons.current.message.readonly clarified, or removed from the page.
I'm using Apps Script API to run a function with the service account's credential.
I added all scopes required in Rest resource API https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run.
But when i run this script below it failed.
function run(){
var CREDENTIALS = {
"private_key": "Your Private key",
"client_email": "Your Client email",
"client_id": "Your Client ID",
"user_email": "Your Email address",
"api_key": "Your API key"
};
var service = getService(CREDENTIALS.client_email,CREDENTIALS.private_key);
service.reset();
if (service.hasAccess()) {
var url = 'https://script.googleapis.com/v1/projects/[SCRIPT ID]:run';
var body = {
"function": [FUNCTION NAME]
};
var params = {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
},
method: 'post',
playload : JSON.stringify(body),
contentType: 'application/json',
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, params);
Logger.log(response);
}
else {
Logger.log(service.getLastError());
}
}
function getService(email, privateKey) {
return OAuth2.createService('Service Account')
// Set the endpoint URL.
.setTokenUrl('https://oauth2.googleapis.com/token')
// Set the private key and issuer.
.setPrivateKey(privateKey)
.setIssuer(email)
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject([USER EMAIL])
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/script.external_request');
}
I've got a 404 Error and I think it comes from the scopes list.
So I can't run a script deployed as an API Executable with the OAuth2.0 token.
Which scopes should I choose to run a function via an HTTP request?
In your run function, for the params object you should have payload not playload.
You want to use Apps Script API with the service account.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
Unfortunately, in the current stage, the method of scripts.run in Apps Script API cannot be used with the service account. The official document says as follows. About this, when I tested this, I could confirm that the method of scripts.run in Apps Script API cannot be used with the service account.
Warning: The Apps Script API doesn't work with service accounts.
From above situation, as the workaround, how about using the access token retrieved by OAuth2? In order to use the Apps Script API with OAuth2, it is required to link Cloud Platform Project to Google Apps Script Project. About this, you can see the flow for linking them at here.
Note:
I think that when you use OAuth2, Oleg Valter's comment and TheAddonDepot's answer are very useful.
References:
Executing Functions using the Apps Script API
Linking Cloud Platform Project to Google Apps Script Project
If this was not the direction you want, I apologize.
Added:
You want to make several users run the script as the owner who is you.
From your replying, I could understand like above. When Apps Script API is used for above situation, the credential information is required to give each user. When each user uses the access token retrieved by your credential information, your goal can be achieve. But I cannot recommend this. So in your case, I would like to use Web Apps to achieve your goal. The flow is as follows.
1. Prepare script.
Please prepare your script. For example, in the current stage, you want to make users run a function of myFunction(), please put the following sample script.
function doGet(e) {
var values = e; // When you want to give the values by requesting, you can use the event object like "e".
var res = myFunction(values);
return ContentService.createTextOutput(res);
}
In this case, the GET method is used. When you want to only run the function, you can use this script. When you want to run the function by giving the large data, you can use doPost() instead of doGet().
2. Deploy Web Apps.
On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
Select "Me" for "Execute the app as:".
By this, the script is run as the owner.
Here, when "Anyone" is set, the script is run as each user. In this case, it is required to share the script to each user. And the access token is required to be used. Please be careful this.
Select "Anyone, even anonymous" for "Who has access to the app:".
In this case, no access token is required to be request. I think that as the test case, I recommend this setting.
Of course, you can also use the access token. At that time, please set this to "Anyone".
Click "Deploy" button as new "Project version".
Automatically open a dialog box of "Authorization required".
Click "Review Permissions".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Click "OK".
Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
3. Run the function using Web Apps.
This is a sample curl command for executing myFunction with Web Apps. Please set your Web Apps URL. At above settings of Web Apps, each user can access by the following curl command.
curl -GL \
-d "key=value" \
"https://script.google.com/macros/s/###/exec"
When key=value is used as the query parameter like above, at doGet(e), you can retrieve value using e.parameter.key.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
You can deploy the script as a Web App. To do so, go to Publish > Deploy as web app. Set the Execute the app as: field to Me (youremail). This way you can share the script as a browser link, and any user will run the script with your credentials.
You can add a some user interface with a confirmation message so the users know they have successfully executed the script. You can find the documentation in this link.
I'm trying to call StackExchange's API, using Google Apps Script and Google Sheets. I can't figure out where in the OAuth process things are going wrong. My current code is not granting access:
function getStackExchangeService_() {
var CLIENT_ID = PropertiesService.getScriptProperties().getProperty('SE_CLIENT_ID');
var CLIENT_SECRET = PropertiesService.getScriptProperties().getProperty('SE_CLIENT_SECRET');
return OAuth2.createService('StackExchange')
.setAuthorizationBaseUrl('https://stackoverflow.com/oauth')
.setTokenUrl('https://stackoverflow.com/oauth/access_token')
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setRedirectUri('https://stackexchange.com/oauth/login_success')
.setScope('global');
}
When I call this and log the response I always get "false":
var service = getStackExchangeService_();
Logger.log(service.hasAccess());
Thanks for the help!
The most obvious issue is that global is not a valid Stack Exchange scope.
Probably use .setScope('read_inbox'); to start.
Also, be sure that:
You have registered your app and configured it for explicit (server-side) OAuth2. (Omit the port information in your case.)
Apparently you should use https://script.google.com/macros/d/{SCRIPT ID}/usercallback for the Application Website, per these instructions??.
Which means you would use script.google.com for the OAuth Domain.
i want to get an authorized token for my App Script Application for YouTube Analytics.. Currently i am using cGoa Library to get an Authorized token which requires authentication once as provided in this link .. But for security purpose, we don't want to use external script like the so called cGoa Library. Please i need to know any other ways to acquires Oauth2 access token for YouTube Analytics in Google App Script. Our major objective id to have the token once and for all future uses because we want to automatically run the script on a daily basis. So we need something that will works all time without require more authentication. Currently this is the code i use to get token with cGoa Llibrary.
function oneOffScript() {
var options = {
packageName: 'youtube-analytics', // user defined
clientId: '942097727718-t663ep631aqhd75610g4etpcn3foj827.apps.googleusercontent.com',
clientSecret:'Ng_9HJ34hKSPesvRA6hb85KR',
// following method to add Google scopes. Here we are mixing YouTube Data and Analytics (prefixed yt-)
// in the same service allowing calls to both APIs with the same token (we can do this because both are Google APIs)
// More on mixing services http://ramblings.mcpher.com/Home/excelquirks/goa/mutipleconsent
scopes : cGoa.GoaApp.scopesGoogleExpand (['youtube',
'youtube.force-ssl',
"youtubepartner",
"youtubepartner-channel-audit",
"yt-analytics-monetary.readonly",
"yt-analytics.readonly"]),
service:'google' // always this for Google APIs. Ref: http://ramblings.mcpher.com/Home/excelquirks/goa/services
};
// store one off in user properties
cGoa.GoaApp.setPackage(PropertiesService.getUserProperties(), options);
}
function doGet(e) {
var goa = cGoa.GoaApp.createGoa ('youtube-analytics',
PropertiesService.getUserProperties()).execute (e);
if (goa.needsConsent()) {
return goa.getConsent();
}
Please your response is highly appreciated. Thanks
Currently my code only modifies my signature, because when I put the email of the other person in my domain, the error: Not Found (line 9, file "Code") appears.
My current code:
function myFunction() {
var newSignature = Gmail.newSendAs();
newSignature.signature = "signature";
var listEmails = [
"leticia#domain.com"]
var updateSignature = Gmail.Users.Settings.SendAs.update(newSignature, "me", listEmails)
}
I am developing using APPS SCRIPT.
Any suggestions for me to be able to change the signature of someone else in my domain?
To change other people Gmail settings in your domain you'll need to be a domain Admin, then create a service account with domain-wide authority, then whitelist it in the Admin Console. Then use said service account and authentication token generated to authenticate your requests to the Gmail API.
This built-in Apps Script Gmail integration was not made for that use-case. The intended usage is to setup your own settings, or individual users that explicitly authorize your application to run on their behalf. This sendAs is there because one might have multiple Gmail signatures, depending on their selected send-as/from alias.
Note that simply authorizing an script with your GSuite admin account won't allow to the script to perform domain-wide operations. That'd be too dangerous, therefore the somewhat convoluted service-account setup is required.
Link to the relevant documentation