diff --git a/packages/nodes-base/credentials/GoogleApi.credentials.ts b/packages/nodes-base/credentials/GoogleApi.credentials.ts
index 9a777496d0..4f411a5229 100644
--- a/packages/nodes-base/credentials/GoogleApi.credentials.ts
+++ b/packages/nodes-base/credentials/GoogleApi.credentials.ts
@@ -10,7 +10,7 @@ export class GoogleApi implements ICredentialType {
documentationUrl = 'google';
properties = [
{
- displayName: 'Email',
+ displayName: 'Service Account Email',
name: 'email',
type: 'string' as NodePropertyTypes,
default: '',
@@ -25,5 +25,25 @@ export class GoogleApi implements ICredentialType {
default: '',
description: 'Use the multiline editor. Make sure there are exactly 3 lines.
-----BEGIN PRIVATE KEY-----
KEY IN A SINGLE LINE
-----END PRIVATE KEY-----',
},
+ {
+ displayName: ' Impersonate a User',
+ name: 'inpersonate',
+ type: 'boolean' as NodePropertyTypes,
+ default: false,
+ },
+ {
+ displayName: 'Email',
+ name: 'delegatedEmail',
+ type: 'string' as NodePropertyTypes,
+ default: '',
+ displayOptions: {
+ show: {
+ inpersonate: [
+ true,
+ ],
+ },
+ },
+ description: 'The email address of the user for which the application is requesting delegated access.',
+ },
];
}
diff --git a/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts
index 56105c4f04..ef3f6187e3 100644
--- a/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts
@@ -103,7 +103,7 @@ function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoa
const signature = jwt.sign(
{
'iss': credentials.email as string,
- 'sub': credentials.email as string,
+ 'sub': credentials.delegatedEmail || credentials.email as string,
'scope': scopes.join(' '),
'aud': `https://oauth2.googleapis.com/token`,
'iat': now,
diff --git a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts
index cabaa1e7d4..36919cba27 100644
--- a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts
@@ -66,6 +66,8 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
} else if (error.response.body.error.message) {
errorMessages = error.response.body.error.message;
+ } else if (error.response.body.error_description) {
+ errorMessages = error.response.body.error_description;
}
throw new Error(`Google Drive error response [${error.statusCode}]: ${errorMessages}`);
@@ -107,7 +109,7 @@ function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoa
const signature = jwt.sign(
{
'iss': credentials.email as string,
- 'sub': credentials.email as string,
+ 'sub': credentials.delegatedEmail || credentials.email as string,
'scope': scopes.join(' '),
'aud': `https://oauth2.googleapis.com/token`,
'iat': now,
diff --git a/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts
index c86752fcb9..3dc2eabe92 100644
--- a/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts
@@ -23,10 +23,15 @@ import {
IEmail,
} from './Gmail.node';
+import * as moment from 'moment-timezone';
+
+import * as jwt from 'jsonwebtoken';
+
const mailComposer = require('nodemailer/lib/mail-composer');
export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string,
endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any
+ const authenticationMethod = this.getNodeParameter('authentication', 0, 'serviceAccount') as string;
let options: OptionsWithUri = {
headers: {
'Accept': 'application/json',
@@ -46,8 +51,22 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
delete options.body;
}
- //@ts-ignore
- return await this.helpers.requestOAuth2.call(this, 'gmailOAuth2', options);
+ if (authenticationMethod === 'serviceAccount') {
+ const credentials = this.getCredentials('googleApi');
+
+ if (credentials === undefined) {
+ throw new Error('No credentials got returned!');
+ }
+
+ const { access_token } = await getAccessToken.call(this, credentials as IDataObject);
+
+ options.headers!.Authorization = `Bearer ${access_token}`;
+ //@ts-ignore
+ return await this.helpers.request(options);
+ } else {
+ //@ts-ignore
+ return await this.helpers.requestOAuth2.call(this, 'gmailOAuth2', options);
+ }
} catch (error) {
if (error.response && error.response.body && error.response.body.error) {
@@ -64,6 +83,8 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
} else if (error.response.body.error.message) {
errorMessages = error.response.body.error.message;
+ } else if (error.response.body.error_description) {
+ errorMessages = error.response.body.error_description;
}
throw new Error(`Gmail error response [${error.statusCode}]: ${errorMessages}`);
@@ -190,3 +211,50 @@ export function extractEmail(s: string) {
const data = s.split('<')[1];
return data.substring(0, data.length - 1);
}
+
+function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject): Promise {
+ //https://developers.google.com/identity/protocols/oauth2/service-account#httprest
+
+ const scopes = [
+ 'https://www.googleapis.com/auth/books',
+ ];
+
+ const now = moment().unix();
+
+ const signature = jwt.sign(
+ {
+ 'iss': credentials.email as string,
+ 'sub': credentials.delegatedEmail || credentials.email as string,
+ 'scope': scopes.join(' '),
+ 'aud': `https://oauth2.googleapis.com/token`,
+ 'iat': now,
+ 'exp': now + 3600,
+ },
+ credentials.privateKey as string,
+ {
+ algorithm: 'RS256',
+ header: {
+ 'kid': credentials.privateKey as string,
+ 'typ': 'JWT',
+ 'alg': 'RS256',
+ },
+ },
+ );
+
+ const options: OptionsWithUri = {
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ method: 'POST',
+ form: {
+ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
+ assertion: signature,
+ },
+ uri: 'https://oauth2.googleapis.com/token',
+ json: true,
+ };
+
+ //@ts-ignore
+ return this.helpers.request(options);
+}
+
diff --git a/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts b/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts
index 33c458db74..5601c259c5 100644
--- a/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts
+++ b/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts
@@ -78,12 +78,46 @@ export class Gmail implements INodeType {
inputs: ['main'],
outputs: ['main'],
credentials: [
+ {
+ name: 'googleApi',
+ required: true,
+ displayOptions: {
+ show: {
+ authentication: [
+ 'serviceAccount',
+ ],
+ },
+ },
+ },
{
name: 'gmailOAuth2',
required: true,
+ displayOptions: {
+ show: {
+ authentication: [
+ 'oAuth2',
+ ],
+ },
+ },
},
],
properties: [
+ {
+ displayName: 'Authentication',
+ name: 'authentication',
+ type: 'options',
+ options: [
+ {
+ name: 'Service Account',
+ value: 'serviceAccount',
+ },
+ {
+ name: 'OAuth2',
+ value: 'oAuth2',
+ },
+ ],
+ default: 'oAuth2',
+ },
{
displayName: 'Resource',
name: 'resource',
diff --git a/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts
index 1ac6a2658b..21831fc9b7 100644
--- a/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts
@@ -94,7 +94,7 @@ function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoa
const signature = jwt.sign(
{
'iss': credentials.email as string,
- 'sub': credentials.email as string,
+ 'sub': credentials.delegatedEmail || credentials.email as string,
'scope': scopes.join(' '),
'aud': `https://oauth2.googleapis.com/token`,
'iat': now,