refactor(core): Google service account remove duplicated functions (no-changelog) (#6368)

This commit is contained in:
Michael Kret
2023-06-06 19:19:24 +03:00
committed by GitHub
parent 3a6af3b2a2
commit 05c4229cd7
14 changed files with 134 additions and 719 deletions

View File

@@ -8,64 +8,12 @@ import type {
JsonObject, JsonObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow'; import { NodeApiError, NodeOperationError } from 'n8n-workflow';
import { getGoogleAccessToken } from '../../GenericFunctions';
import moment from 'moment-timezone';
import * as jwt from 'jsonwebtoken';
async function getAccessToken(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
credentials: IDataObject,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim();
const scopes = ['https://www.googleapis.com/auth/bigquery'];
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,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
method: string, method: string,
resource: string, resource: string,
body: any = {}, body: any = {},
qs: IDataObject = {}, qs: IDataObject = {},
uri?: string, uri?: string,
@@ -102,7 +50,7 @@ export async function googleApiRequest(
throw new NodeOperationError(this.getNode(), 'No credentials got returned!'); throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
} }
const { access_token } = await getAccessToken.call(this, credentials as IDataObject); const { access_token } = await getGoogleAccessToken.call(this, credentials, 'bigquery');
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
return await this.helpers.request(options); return await this.helpers.request(options);

View File

@@ -1,58 +1,9 @@
import type { OptionsWithUri } from 'request'; import type { OptionsWithUri } from 'request';
import moment from 'moment-timezone';
import * as jwt from 'jsonwebtoken';
import type { IExecuteFunctions, IExecuteSingleFunctions, ILoadOptionsFunctions } from 'n8n-core'; import type { IExecuteFunctions, IExecuteSingleFunctions, ILoadOptionsFunctions } from 'n8n-core';
import type { IDataObject, JsonObject } from 'n8n-workflow'; import type { IDataObject, JsonObject } from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow'; import { NodeApiError, NodeOperationError } from 'n8n-workflow';
import { getGoogleAccessToken } from '../../../GenericFunctions';
async function getAccessToken(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
credentials: IDataObject,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim();
const scopes = ['https://www.googleapis.com/auth/bigquery'];
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,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
@@ -94,7 +45,7 @@ export async function googleApiRequest(
throw new NodeOperationError(this.getNode(), 'No credentials got returned!'); throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
} }
const { access_token } = await getAccessToken.call(this, credentials as IDataObject); const { access_token } = await getGoogleAccessToken.call(this, credentials, 'bigquery');
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
return await this.helpers.request(options); return await this.helpers.request(options);

View File

@@ -8,72 +8,12 @@ import type {
JsonObject, JsonObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import { getGoogleAccessToken } from '../GenericFunctions';
import moment from 'moment-timezone';
import jwt from 'jsonwebtoken';
interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
async function getAccessToken(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = ['https://www.googleapis.com/auth/books'];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
method: string, method: string,
resource: string, resource: string,
body: any = {}, body: any = {},
qs: IDataObject = {}, qs: IDataObject = {},
uri?: string, uri?: string,
@@ -108,10 +48,7 @@ export async function googleApiRequest(
privateKey: string; privateKey: string;
}; };
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'books');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;

View File

@@ -1,7 +1,6 @@
import type { OptionsWithUri } from 'request'; import type { OptionsWithUri } from 'request';
import type { import type {
ICredentialTestFunctions,
IDataObject, IDataObject,
IExecuteFunctions, IExecuteFunctions,
IExecuteSingleFunctions, IExecuteSingleFunctions,
@@ -11,69 +10,7 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import moment from 'moment-timezone'; import { getGoogleAccessToken } from '../GenericFunctions';
import jwt from 'jsonwebtoken';
interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
export async function getAccessToken(
this:
| IExecuteFunctions
| IExecuteSingleFunctions
| ILoadOptionsFunctions
| ICredentialTestFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = ['https://www.googleapis.com/auth/chat.bot'];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
@@ -112,10 +49,7 @@ export async function googleApiRequest(
} else { } else {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'chat');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
responseData = await this.helpers.request(options); responseData = await this.helpers.request(options);
} }

View File

@@ -8,69 +8,7 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import moment from 'moment-timezone'; import { getGoogleAccessToken } from '../GenericFunctions';
import jwt from 'jsonwebtoken';
interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
async function getAccessToken(
this: IExecuteFunctions | ILoadOptionsFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/documents',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | ILoadOptionsFunctions,
@@ -104,10 +42,7 @@ export async function googleApiRequest(
if (authenticationMethod === 'serviceAccount') { if (authenticationMethod === 'serviceAccount') {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'docs');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
return await this.helpers.request(options); return await this.helpers.request(options);

View File

@@ -10,75 +10,12 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import moment from 'moment-timezone'; import { getGoogleAccessToken } from '../GenericFunctions';
import jwt from 'jsonwebtoken';
interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
async function getAccessToken(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.photos.readonly',
];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions,
method: string, method: string,
resource: string, resource: string,
body: any = {}, body: any = {},
qs: IDataObject = {}, qs: IDataObject = {},
uri?: string, uri?: string,
@@ -111,10 +48,7 @@ export async function googleApiRequest(
if (authenticationMethod === 'serviceAccount') { if (authenticationMethod === 'serviceAccount') {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'drive');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
return await this.helpers.request(options); return await this.helpers.request(options);

View File

@@ -0,0 +1,105 @@
import type { IExecuteFunctions, IExecuteSingleFunctions, ILoadOptionsFunctions } from 'n8n-core';
import type { ICredentialTestFunctions, IDataObject, IPollFunctions } from 'n8n-workflow';
import type { OptionsWithUri } from 'request';
import moment from 'moment-timezone';
import * as jwt from 'jsonwebtoken';
const googleServiceAccountScopes = {
bigquery: ['https://www.googleapis.com/auth/bigquery'],
books: ['https://www.googleapis.com/auth/books'],
chat: ['https://www.googleapis.com/auth/chat.bot'],
docs: [
'https://www.googleapis.com/auth/documents',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
],
drive: [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.photos.readonly',
],
gmail: [
'https://www.googleapis.com/auth/gmail.labels',
'https://www.googleapis.com/auth/gmail.addons.current.action.compose',
'https://www.googleapis.com/auth/gmail.addons.current.message.action',
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
],
sheetV1: [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
],
sheetV2: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.metadata',
],
slides: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/presentations',
],
translate: [
'https://www.googleapis.com/auth/cloud-translation',
'https://www.googleapis.com/auth/cloud-platform',
],
};
type GoogleServiceAccount = keyof typeof googleServiceAccountScopes;
export async function getGoogleAccessToken(
this:
| IExecuteFunctions
| IExecuteSingleFunctions
| ILoadOptionsFunctions
| ICredentialTestFunctions
| IPollFunctions,
credentials: IDataObject,
service: GoogleServiceAccount,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = googleServiceAccountScopes[service];
const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim();
credentials.email = ((credentials.email as string) || '').trim();
const now = moment().unix();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}

View File

@@ -4,7 +4,6 @@ import { simpleParser } from 'mailparser';
import type { import type {
IBinaryKeyData, IBinaryKeyData,
ICredentialDataDecryptedObject,
IDataObject, IDataObject,
IExecuteFunctions, IExecuteFunctions,
IExecuteSingleFunctions, IExecuteSingleFunctions,
@@ -16,10 +15,6 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow'; import { NodeApiError, NodeOperationError } from 'n8n-workflow';
import moment from 'moment-timezone';
import jwt from 'jsonwebtoken';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import isEmpty from 'lodash.isempty'; import isEmpty from 'lodash.isempty';
@@ -44,62 +39,7 @@ export interface IAttachments {
} }
import MailComposer from 'nodemailer/lib/mail-composer'; import MailComposer from 'nodemailer/lib/mail-composer';
import { getGoogleAccessToken } from '../GenericFunctions';
async function getAccessToken(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions,
credentials: ICredentialDataDecryptedObject,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/gmail.labels',
'https://www.googleapis.com/auth/gmail.addons.current.action.compose',
'https://www.googleapis.com/auth/gmail.addons.current.message.action',
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
];
const now = moment().unix();
credentials.email = (credentials.email as string).trim();
const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions,
@@ -139,7 +79,7 @@ export async function googleApiRequest(
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
credentialType = 'googleApi'; credentialType = 'googleApi';
const { access_token } = await getAccessToken.call(this, credentials); const { access_token } = await getGoogleAccessToken.call(this, credentials, 'gmail');
(options.headers as IDataObject).Authorization = `Bearer ${access_token}`; (options.headers as IDataObject).Authorization = `Bearer ${access_token}`;
} }

View File

@@ -1,7 +1,6 @@
import type { OptionsWithUri } from 'request'; import type { OptionsWithUri } from 'request';
import type { import type {
ICredentialTestFunctions,
IDataObject, IDataObject,
IExecuteFunctions, IExecuteFunctions,
IExecuteSingleFunctions, IExecuteSingleFunctions,
@@ -9,10 +8,7 @@ import type {
JsonObject, JsonObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import { getGoogleAccessToken } from '../../GenericFunctions';
import moment from 'moment-timezone';
import jwt from 'jsonwebtoken';
export interface IGoogleAuthCredentials { export interface IGoogleAuthCredentials {
delegatedEmail?: string; delegatedEmail?: string;
@@ -21,63 +17,6 @@ export interface IGoogleAuthCredentials {
privateKey: string; privateKey: string;
} }
export async function getAccessToken(
this:
| IExecuteFunctions
| IExecuteSingleFunctions
| ILoadOptionsFunctions
| ICredentialTestFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
method: string, method: string,
@@ -114,10 +53,7 @@ export async function googleApiRequest(
if (authenticationMethod === 'serviceAccount') { if (authenticationMethod === 'serviceAccount') {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'sheetV1');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
return await this.helpers.request(options); return await this.helpers.request(options);

View File

@@ -22,10 +22,10 @@ import type {
} from './GoogleSheet'; } from './GoogleSheet';
import { GoogleSheet } from './GoogleSheet'; import { GoogleSheet } from './GoogleSheet';
import type { IGoogleAuthCredentials } from './GenericFunctions'; import { googleApiRequest, hexToRgb } from './GenericFunctions';
import { getAccessToken, googleApiRequest, hexToRgb } from './GenericFunctions';
import { versionDescription } from './versionDescription'; import { versionDescription } from './versionDescription';
import { getGoogleAccessToken } from '../../GenericFunctions';
export class GoogleSheetsV1 implements INodeType { export class GoogleSheetsV1 implements INodeType {
description: INodeTypeDescription; description: INodeTypeDescription;
@@ -71,10 +71,8 @@ export class GoogleSheetsV1 implements INodeType {
credential: ICredentialsDecrypted, credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> { ): Promise<INodeCredentialTestResult> {
try { try {
const tokenRequest = await getAccessToken.call( const tokenRequest = await getGoogleAccessToken.call(this, credential.data!, 'sheetV1');
this,
credential.data! as unknown as IGoogleAuthCredentials,
);
if (!tokenRequest.access_token) { if (!tokenRequest.access_token) {
return { return {
status: 'Error', status: 'Error',

View File

@@ -4,18 +4,14 @@ import type {
INodeCredentialTestResult, INodeCredentialTestResult,
} from 'n8n-workflow'; } from 'n8n-workflow';
import type { IGoogleAuthCredentials } from '../transport'; import { getGoogleAccessToken } from '../../../GenericFunctions';
import { getAccessToken } from '../transport';
export async function googleApiCredentialTest( export async function googleApiCredentialTest(
this: ICredentialTestFunctions, this: ICredentialTestFunctions,
credential: ICredentialsDecrypted, credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> { ): Promise<INodeCredentialTestResult> {
try { try {
const tokenRequest = await getAccessToken.call( const tokenRequest = await getGoogleAccessToken.call(this, credential.data!, 'sheetV2');
this,
credential.data! as unknown as IGoogleAuthCredentials,
);
if (!tokenRequest.access_token) { if (!tokenRequest.access_token) {
return { return {
status: 'Error', status: 'Error',

View File

@@ -1,6 +1,5 @@
import type { OptionsWithUri } from 'request'; import type { OptionsWithUri } from 'request';
import type { import type {
ICredentialTestFunctions,
IDataObject, IDataObject,
IExecuteFunctions, IExecuteFunctions,
IExecuteSingleFunctions, IExecuteSingleFunctions,
@@ -9,73 +8,7 @@ import type {
JsonObject, JsonObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import moment from 'moment-timezone'; import { getGoogleAccessToken } from '../../../GenericFunctions';
import jwt from 'jsonwebtoken';
export interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
export async function getAccessToken(
this:
| IExecuteFunctions
| IExecuteSingleFunctions
| ILoadOptionsFunctions
| ICredentialTestFunctions
| IPollFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.metadata',
];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.delegatedEmail || credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function apiRequest( export async function apiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions,
@@ -114,10 +47,7 @@ export async function apiRequest(
if (authenticationMethod === 'serviceAccount') { if (authenticationMethod === 'serviceAccount') {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'sheetV2');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;

View File

@@ -8,68 +8,7 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import moment from 'moment-timezone'; import { getGoogleAccessToken } from '../GenericFunctions';
import jwt from 'jsonwebtoken';
interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
async function getAccessToken(
this: IExecuteFunctions | ILoadOptionsFunctions,
credentials: IGoogleAuthCredentials,
) {
// https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/presentations',
];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | ILoadOptionsFunctions,
@@ -106,10 +45,7 @@ export async function googleApiRequest(
if (authenticationMethod === 'serviceAccount') { if (authenticationMethod === 'serviceAccount') {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'slides');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers.Authorization = `Bearer ${access_token}`; options.headers.Authorization = `Bearer ${access_token}`;
return await this.helpers.request(options); return await this.helpers.request(options);
} else { } else {

View File

@@ -9,68 +9,7 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow';
import moment from 'moment-timezone'; import { getGoogleAccessToken } from '../GenericFunctions';
import jwt from 'jsonwebtoken';
interface IGoogleAuthCredentials {
delegatedEmail?: string;
email: string;
inpersonate: boolean;
privateKey: string;
}
async function getAccessToken(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
credentials: IGoogleAuthCredentials,
): Promise<IDataObject> {
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
const scopes = [
'https://www.googleapis.com/auth/cloud-translation',
'https://www.googleapis.com/auth/cloud-platform',
];
const now = moment().unix();
credentials.email = credentials.email.trim();
const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim();
const signature = jwt.sign(
{
iss: credentials.email,
sub: credentials.email,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600,
},
privateKey,
{
algorithm: 'RS256',
header: {
kid: privateKey,
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,
};
return this.helpers.request(options);
}
export async function googleApiRequest( export async function googleApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
@@ -108,16 +47,12 @@ export async function googleApiRequest(
if (authenticationMethod === 'serviceAccount') { if (authenticationMethod === 'serviceAccount') {
const credentials = await this.getCredentials('googleApi'); const credentials = await this.getCredentials('googleApi');
const { access_token } = await getAccessToken.call( const { access_token } = await getGoogleAccessToken.call(this, credentials, 'translate');
this,
credentials as unknown as IGoogleAuthCredentials,
);
options.headers!.Authorization = `Bearer ${access_token}`; options.headers!.Authorization = `Bearer ${access_token}`;
//@ts-ignore
return await this.helpers.request(options); return await this.helpers.request(options);
} else { } else {
//@ts-ignore
return await this.helpers.requestOAuth2.call(this, 'googleTranslateOAuth2Api', options); return await this.helpers.requestOAuth2.call(this, 'googleTranslateOAuth2Api', options);
} }
} catch (error) { } catch (error) {