From 9bc4a44cfba1742e7f8e4c4107faa502c9f2ffcd Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Fri, 27 May 2022 17:01:49 +0300 Subject: [PATCH 01/13] :zap: wip --- packages/core/src/NodeExecuteFunctions.ts | 2 + .../ShopifyOAuth2Api.credentials.ts | 55 +++++++++++++++++++ .../ShopifyTokenApi.credentials.ts | 42 ++++++++++++++ .../nodes/Shopify/GenericFunctions.ts | 30 +++++++--- .../nodes-base/nodes/Shopify/Shopify.node.ts | 50 +++++++++++++++++ packages/nodes-base/package.json | 2 + 6 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index fc33761a96af0..0e1f5d003750a 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -887,6 +887,8 @@ export async function requestOAuth2( }); const oauthTokenData = credentials.oauthTokenData as clientOAuth2.Data; + // eslint-disable-next-line no-console + console.log(oauthTokenData); const token = oAuthClient.createToken( get(oauthTokenData, oAuth2Options?.property as string) || oauthTokenData.accessToken, diff --git a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts new file mode 100644 index 0000000000000..fbc150d82d8e3 --- /dev/null +++ b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts @@ -0,0 +1,55 @@ +import { + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class ShopifyOAuth2Api implements ICredentialType { + name = 'shopifyOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Shopify OAuth2 API'; + documentationUrl = 'shopify'; + properties: INodeProperties[] = [ + { + displayName: 'Shop Subdomain', + name: 'shopSubdomain', + required: true, + type: 'string', + default: '', + description: 'Only the subdomain without .myshopify.com', + }, + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden', + default: '=https://{{$self["shopSubdomain"]}}.myshopify.com/admin/oauth/authorize', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden', + default: '=https://{{$self["shopSubdomain"]}}.myshopify.com/admin/oauth/access_token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden', + default: 'write_orders,read_orders,write_products,read_products', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden', + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden', + default: 'header', + }, + ]; +} diff --git a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts b/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts new file mode 100644 index 0000000000000..06f441fc6f6ab --- /dev/null +++ b/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts @@ -0,0 +1,42 @@ +import { + IAuthenticateHeaderAuth, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class ShopifyTokenApi implements ICredentialType { + name = 'shopifyTokenApi'; + displayName = 'Shopify Access Token API'; + documentationUrl = 'shopify'; + properties: INodeProperties[] = [ + { + displayName: 'Access Token', + name: 'accessToken', + required: true, + type: 'string', + default: '', + }, + { + displayName: 'Shop Subdomain', + name: 'shopSubdomain', + required: true, + type: 'string', + default: '', + description: 'Only the subdomain without .myshopify.com', + }, + ]; + authenticate: IAuthenticateHeaderAuth = { + type: 'headerAuth', + properties: { + name: 'X-Shopify-Access-Token', + value: '={{$credentials?.accessToken}}', + }, + }; + test: ICredentialTestRequest = { + request: { + baseURL: '=https://{{$credentials?.shopSubdomain}}.myshopify.com/admin/api/2019-10', + url: '/products.json', + }, + }; +} diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 4648a9e9c99aa..540663d5eb7a9 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -11,7 +11,7 @@ import { } from 'n8n-core'; import { - IDataObject, NodeApiError, NodeOperationError, + IDataObject, NodeApiError, } from 'n8n-workflow'; import { @@ -19,12 +19,23 @@ import { } from 'change-case'; export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any - const credentials = await this.getCredentials('shopifyApi'); - const headerWithAuthentication = Object.assign({}, - { Authorization: `Basic ${Buffer.from(`${credentials.apiKey}:${credentials.password}`).toString(BINARY_ENCODING)}` }); + + const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2') as string; + + let credentials; + let credentialType = 'shopifyOAuth2Api'; + + if (authenticationMethod === 'shopifyApi') { + credentials = await this.getCredentials('shopifyApi'); + credentialType = 'shopifyApi'; + } else if (authenticationMethod === 'accessToken') { + credentials = await this.getCredentials('shopifyTokenApi'); + credentialType = 'shopifyTokenApi'; + } else { + credentials = await this.getCredentials('shopifyOAuth2Api'); + } const options: OptionsWithUri = { - headers: headerWithAuthentication, method, qs: query, uri: uri || `https://${credentials.shopSubdomain}.myshopify.com/admin/api/2019-10${resource}`, @@ -32,6 +43,12 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions json: true, }; + if (authenticationMethod === 'apiKey') { + const headerWithAuthentication = Object.assign({}, + { Authorization: `Basic ${Buffer.from(`${credentials.apiKey}:${credentials.password}`).toString(BINARY_ENCODING)}` }); + options.headers = headerWithAuthentication; + } + if (Object.keys(option).length !== 0) { Object.assign(options, option); } @@ -42,13 +59,12 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions delete options.qs; } try { - return await this.helpers.request!(options); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } catch (error) { throw new NodeApiError(this.getNode(), error); } } - export async function shopifyApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; diff --git a/packages/nodes-base/nodes/Shopify/Shopify.node.ts b/packages/nodes-base/nodes/Shopify/Shopify.node.ts index 9a0c147876632..483519efd4278 100644 --- a/packages/nodes-base/nodes/Shopify/Shopify.node.ts +++ b/packages/nodes-base/nodes/Shopify/Shopify.node.ts @@ -57,9 +57,59 @@ export class Shopify implements INodeType { { name: 'shopifyApi', required: true, + displayOptions: { + show: { + authentication: [ + 'apiKey', + ], + }, + }, + }, + { + name: 'shopifyTokenApi', + required: true, + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'shopifyOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + { + name: 'API Key', + value: 'apiKey', + description: 'Private apps are deprecated and can\'t be created as of January 2022', + }, + ], + default: 'apiKey', + }, { displayName: 'Resource', name: 'resource', diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index e1a6f00494d6b..00076abe40211 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -260,6 +260,8 @@ "dist/credentials/ServiceNowBasicApi.credentials.js", "dist/credentials/Sftp.credentials.js", "dist/credentials/ShopifyApi.credentials.js", + "dist/credentials/ShopifyTokenApi.credentials.js", + "dist/credentials/ShopifyOAuth2Api.credentials.js", "dist/credentials/Signl4Api.credentials.js", "dist/credentials/SlackApi.credentials.js", "dist/credentials/SlackOAuth2Api.credentials.js", From 0f859aa4b01a4444af227212c666c2beb723c9ae Mon Sep 17 00:00:00 2001 From: ricardo Date: Thu, 16 Jun 2022 14:50:30 -0400 Subject: [PATCH 02/13] :zap: Add includeAccessTokenInHeader option to OAuth2 --- packages/core/src/NodeExecuteFunctions.ts | 17 +++++++++++++++++ .../credentials/ShopifyOAuth2Api.credentials.ts | 6 +++--- .../nodes/Shopify/GenericFunctions.ts | 12 +++++++++--- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 1d93eb615be60..f60e9e4b32b63 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -942,6 +942,14 @@ export async function requestOAuth2( newRequestOptions?.headers?.Authorization.split(' ')[1]; } + if (oAuth2Options?.includeAccessTokenInHeader) { + Object.assign(newRequestOptions, { + headers: { + [oAuth2Options.includeAccessTokenInHeader]: token.accessToken, + }, + }); + } + return this.helpers.request!(newRequestOptions).catch(async (error: IResponseError) => { const statusCodeReturned = oAuth2Options?.tokenExpiredStatusCode === undefined @@ -1009,6 +1017,15 @@ export async function requestOAuth2( if (isN8nRequest) { return this.helpers.httpRequest(newRequestOptions); } + + if (oAuth2Options?.includeAccessTokenInHeader) { + Object.assign(newRequestOptions, { + headers: { + [oAuth2Options.includeAccessTokenInHeader]: token.accessToken, + }, + }); + } + return this.helpers.request!(newRequestOptions); } diff --git a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts index fbc150d82d8e3..23589d0e8e9bf 100644 --- a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts @@ -37,19 +37,19 @@ export class ShopifyOAuth2Api implements ICredentialType { displayName: 'Scope', name: 'scope', type: 'hidden', - default: 'write_orders,read_orders,write_products,read_products', + default: 'write_orders read_orders write_products read_products', }, { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden', - default: '', + default: 'access_mode=value', }, { displayName: 'Authentication', name: 'authentication', type: 'hidden', - default: 'header', + default: 'body', }, ]; } diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 540663d5eb7a9..8874f24566ce3 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -11,7 +11,8 @@ import { } from 'n8n-core'; import { - IDataObject, NodeApiError, + IAdditionalCredentialOptions, + IDataObject, IOAuth2Options, NodeApiError, } from 'n8n-workflow'; import { @@ -25,7 +26,7 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions let credentials; let credentialType = 'shopifyOAuth2Api'; - if (authenticationMethod === 'shopifyApi') { + if (authenticationMethod === 'apiKey') { credentials = await this.getCredentials('shopifyApi'); credentialType = 'shopifyApi'; } else if (authenticationMethod === 'accessToken') { @@ -43,6 +44,11 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions json: true, }; + const oAuth2Options: IOAuth2Options = { + tokenType: 'Bearer', + includeAccessTokenInHeader: 'X-Shopify-Access-Token', + }; + if (authenticationMethod === 'apiKey') { const headerWithAuthentication = Object.assign({}, { Authorization: `Basic ${Buffer.from(`${credentials.apiKey}:${credentials.password}`).toString(BINARY_ENCODING)}` }); @@ -59,7 +65,7 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions delete options.qs; } try { - return await this.helpers.requestWithAuthentication.call(this, credentialType, options); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options, { oauth2: oAuth2Options }); } catch (error) { throw new NodeApiError(this.getNode(), error); } From 07d93ab7a684b6480cf8b70ca42a29ee11691ab2 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Mon, 20 Jun 2022 11:46:44 +0300 Subject: [PATCH 03/13] :hammer: fixed build error, fixed trigger node when using token auth --- packages/core/src/NodeExecuteFunctions.ts | 1 + .../ShopifyTokenApi.credentials.ts | 7 ++ .../nodes/Shopify/ShopifyTrigger.node.ts | 66 ++++++++++++++++++- packages/workflow/src/Interfaces.ts | 1 + 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index f60e9e4b32b63..73a1881cf3e92 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -277,6 +277,7 @@ async function parseRequestObject(requestObject: IDataObject) { // If we have body and possibly form if (requestObject.form !== undefined) { // merge both objects when exist. + // @ts-ignore requestObject.body = Object.assign(requestObject.body, requestObject.form); } axiosConfig.data = requestObject.body as FormData | GenericValue | GenericValue[]; diff --git a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts b/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts index 06f441fc6f6ab..ed4d9054ecf99 100644 --- a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts @@ -25,6 +25,13 @@ export class ShopifyTokenApi implements ICredentialType { default: '', description: 'Only the subdomain without .myshopify.com', }, + { + displayName: 'App Secret Key', + name: 'appSecretKey', + type: 'string', + default: '', + description: 'Optional secret key needed to verify the webhook when using Shopify Trigger node', + }, ]; authenticate: IAuthenticateHeaderAuth = { type: 'headerAuth', diff --git a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts index fb9d862efd04a..70f2c1a95d069 100644 --- a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts +++ b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts @@ -36,6 +36,35 @@ export class ShopifyTrigger implements INodeType { { name: 'shopifyApi', required: true, + displayOptions: { + show: { + authentication: [ + 'apiKey', + ], + }, + }, + }, + { + name: 'shopifyTokenApi', + required: true, + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'shopifyOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, }, ], webhooks: [ @@ -47,6 +76,27 @@ export class ShopifyTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + { + name: 'API Key', + value: 'apiKey', + description: 'Private apps are deprecated and can\'t be created as of January 2022', + }, + ], + default: 'apiKey', + }, { displayName: 'Topic', name: 'topic', @@ -356,14 +406,26 @@ export class ShopifyTrigger implements INodeType { async webhook(this: IWebhookFunctions): Promise { const headerData = this.getHeaderData() as IDataObject; const req = this.getRequestObject(); - const credentials = await this.getCredentials('shopifyApi'); + const authentication = this.getNodeParameter('authentication') as string; + let secret = ''; + + if (authentication === 'apiKey') { + const credentials = await this.getCredentials('shopifyApi'); + secret = credentials.sharedSecret as string; + } + + if (authentication === 'accessToken') { + const credentials = await this.getCredentials('shopifyTokenApi'); + secret = credentials.appSecretKey as string; + } + const topic = this.getNodeParameter('topic') as string; if (headerData['x-shopify-topic'] !== undefined && headerData['x-shopify-hmac-sha256'] !== undefined && headerData['x-shopify-shop-domain'] !== undefined && headerData['x-shopify-api-version'] !== undefined) { // @ts-ignore - const computedSignature = createHmac('sha256', credentials.sharedSecret as string).update(req.rawBody).digest('base64'); + const computedSignature = createHmac('sha256', secret).update(req.rawBody).digest('base64'); if (headerData['x-shopify-hmac-sha256'] !== computedSignature) { return {}; } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index bca49df2743e4..bc596a16d9372 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -44,6 +44,7 @@ export interface IOAuth2Options { tokenType?: string; keepBearer?: boolean; tokenExpiredStatusCode?: number; + includeAccessTokenInHeader?: string; } export interface IConnection { From 7f30434ca63626834bb56646d865d62094e6c94c Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Mon, 20 Jun 2022 13:27:38 +0300 Subject: [PATCH 04/13] :hammer: fixed trigger when using oauth2 --- .../credentials/ShopifyTokenApi.credentials.ts | 13 +++++++------ .../nodes-base/nodes/Shopify/GenericFunctions.ts | 10 ++++++++-- .../nodes-base/nodes/Shopify/ShopifyTrigger.node.ts | 5 +++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts b/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts index ed4d9054ecf99..8f8a94ae653b8 100644 --- a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts @@ -18,19 +18,20 @@ export class ShopifyTokenApi implements ICredentialType { default: '', }, { - displayName: 'Shop Subdomain', - name: 'shopSubdomain', + displayName: 'App Secret Key', + name: 'appSecretKey', required: true, type: 'string', default: '', - description: 'Only the subdomain without .myshopify.com', + description: 'Secret key needed to verify the webhook when using Shopify Trigger node', }, { - displayName: 'App Secret Key', - name: 'appSecretKey', + displayName: 'Shop Subdomain', + name: 'shopSubdomain', + required: true, type: 'string', default: '', - description: 'Optional secret key needed to verify the webhook when using Shopify Trigger node', + description: 'Only the subdomain without .myshopify.com', }, ]; authenticate: IAuthenticateHeaderAuth = { diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 8874f24566ce3..ad77076213b76 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -29,11 +29,14 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions if (authenticationMethod === 'apiKey') { credentials = await this.getCredentials('shopifyApi'); credentialType = 'shopifyApi'; + } else if (authenticationMethod === 'accessToken') { credentials = await this.getCredentials('shopifyTokenApi'); credentialType = 'shopifyTokenApi'; + } else { credentials = await this.getCredentials('shopifyOAuth2Api'); + } const options: OptionsWithUri = { @@ -50,8 +53,10 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions }; if (authenticationMethod === 'apiKey') { - const headerWithAuthentication = Object.assign({}, - { Authorization: `Basic ${Buffer.from(`${credentials.apiKey}:${credentials.password}`).toString(BINARY_ENCODING)}` }); + const headerWithAuthentication = Object.assign( + {}, + { Authorization: `Basic ${Buffer.from(`${credentials.apiKey}:${credentials.password}`).toString(BINARY_ENCODING)}` }, + ); options.headers = headerWithAuthentication; } @@ -64,6 +69,7 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions if (Object.keys(query).length === 0) { delete options.qs; } + try { return await this.helpers.requestWithAuthentication.call(this, credentialType, options, { oauth2: oAuth2Options }); } catch (error) { diff --git a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts index 70f2c1a95d069..084041421fb56 100644 --- a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts +++ b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts @@ -419,6 +419,11 @@ export class ShopifyTrigger implements INodeType { secret = credentials.appSecretKey as string; } + if (authentication === 'oAuth2') { + const credentials = await this.getCredentials('shopifyOAuth2Api'); + secret = credentials.clientSecret as string; + } + const topic = this.getNodeParameter('topic') as string; if (headerData['x-shopify-topic'] !== undefined && headerData['x-shopify-hmac-sha256'] !== undefined From c452c907631f9821e3672c6177d64457ae99b4cd Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Mon, 20 Jun 2022 14:11:30 +0300 Subject: [PATCH 05/13] :hammer: changed default auth method to access token --- packages/nodes-base/nodes/Shopify/Shopify.node.ts | 2 +- packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Shopify/Shopify.node.ts b/packages/nodes-base/nodes/Shopify/Shopify.node.ts index 483519efd4278..dee2a2a98f3a5 100644 --- a/packages/nodes-base/nodes/Shopify/Shopify.node.ts +++ b/packages/nodes-base/nodes/Shopify/Shopify.node.ts @@ -108,7 +108,7 @@ export class Shopify implements INodeType { description: 'Private apps are deprecated and can\'t be created as of January 2022', }, ], - default: 'apiKey', + default: 'accessToken', }, { displayName: 'Resource', diff --git a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts index 084041421fb56..9960ebed07d84 100644 --- a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts +++ b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts @@ -95,7 +95,7 @@ export class ShopifyTrigger implements INodeType { description: 'Private apps are deprecated and can\'t be created as of January 2022', }, ], - default: 'apiKey', + default: 'accessToken', }, { displayName: 'Topic', From af2cfcecf491731c65e0d575eba15dffafcbc30b Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 29 Jun 2022 15:22:46 -0400 Subject: [PATCH 06/13] :zap: Improvements --- packages/cli/src/Server.ts | 6 ++-- packages/core/src/NodeExecuteFunctions.ts | 1 - ...s => ShopifyAccessTokenApi.credentials.ts} | 6 ++-- .../ShopifyOAuth2Api.credentials.ts | 31 +++++++++++++++++++ .../nodes/Shopify/GenericFunctions.ts | 11 ++----- .../nodes-base/nodes/Shopify/Shopify.node.ts | 3 +- .../nodes/Shopify/ShopifyTrigger.node.ts | 3 +- packages/nodes-base/package.json | 2 +- 8 files changed, 43 insertions(+), 20 deletions(-) rename packages/nodes-base/credentials/{ShopifyTokenApi.credentials.ts => ShopifyAccessTokenApi.credentials.ts} (88%) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 822e9f93c2346..8c81040ce31a1 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -2171,9 +2171,9 @@ class App { clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, - redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ - this.restEndpoint - }/oauth2-credential/callback`, + // redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ + // this.restEndpoint + // }/oauth2-credential/callback`, scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), }; diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 73a1881cf3e92..367a8e3a4177f 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -1156,7 +1156,6 @@ export async function httpRequestWithAuthentication( node, additionalData.timezone, ); - return await httpRequest(requestOptions); } catch (error) { throw new NodeApiError(this.getNode(), error); diff --git a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts b/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts similarity index 88% rename from packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts rename to packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts index 8f8a94ae653b8..67b37bca49f34 100644 --- a/packages/nodes-base/credentials/ShopifyTokenApi.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts @@ -5,8 +5,8 @@ import { INodeProperties, } from 'n8n-workflow'; -export class ShopifyTokenApi implements ICredentialType { - name = 'shopifyTokenApi'; +export class ShopifyAccessTokenApi implements ICredentialType { + name = 'shopifyAccessTokenApi'; displayName = 'Shopify Access Token API'; documentationUrl = 'shopify'; properties: INodeProperties[] = [ @@ -18,7 +18,7 @@ export class ShopifyTokenApi implements ICredentialType { default: '', }, { - displayName: 'App Secret Key', + displayName: 'APP Secret Key', name: 'appSecretKey', required: true, type: 'string', diff --git a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts index 23589d0e8e9bf..6f3edd6adf90b 100644 --- a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts @@ -19,6 +19,37 @@ export class ShopifyOAuth2Api implements ICredentialType { default: '', description: 'Only the subdomain without .myshopify.com', }, + { + displayName: 'Grant Type', + name: 'grantType', + type: 'hidden', + default: 'authorizationCode', + }, + { + displayName: 'Client ID', + name: 'clientId', + type: 'string', + default: '', + required: true, + hint: 'Be aware that Shopify refers to the Client ID as API Key' + }, + { + displayName: 'Client Secret', + name: 'clientSecret', + type: 'string', + typeOptions: { + password: true, + }, + default: '', + required: true, + hint: 'Be aware that Shopify refers to the Client Secret as API Secret Key' + }, + { + displayName: 'Grant Type', + name: 'grantType', + type: 'hidden', + default: 'authorizationCode', + }, { displayName: 'Authorization URL', name: 'authUrl', diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index ad77076213b76..1d70e1090ff33 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -31,12 +31,11 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions credentialType = 'shopifyApi'; } else if (authenticationMethod === 'accessToken') { - credentials = await this.getCredentials('shopifyTokenApi'); - credentialType = 'shopifyTokenApi'; + credentials = await this.getCredentials('shopifyAccessTokenApi'); + credentialType = 'shopifyAccessTokenApi'; } else { credentials = await this.getCredentials('shopifyOAuth2Api'); - } const options: OptionsWithUri = { @@ -53,11 +52,7 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions }; if (authenticationMethod === 'apiKey') { - const headerWithAuthentication = Object.assign( - {}, - { Authorization: `Basic ${Buffer.from(`${credentials.apiKey}:${credentials.password}`).toString(BINARY_ENCODING)}` }, - ); - options.headers = headerWithAuthentication; + Object.assign(options, { auth: { username: credentials.apiKey, password: credentials.password } }) } if (Object.keys(option).length !== 0) { diff --git a/packages/nodes-base/nodes/Shopify/Shopify.node.ts b/packages/nodes-base/nodes/Shopify/Shopify.node.ts index dee2a2a98f3a5..a2ea6652af029 100644 --- a/packages/nodes-base/nodes/Shopify/Shopify.node.ts +++ b/packages/nodes-base/nodes/Shopify/Shopify.node.ts @@ -66,7 +66,7 @@ export class Shopify implements INodeType { }, }, { - name: 'shopifyTokenApi', + name: 'shopifyAccessTokenApi', required: true, displayOptions: { show: { @@ -105,7 +105,6 @@ export class Shopify implements INodeType { { name: 'API Key', value: 'apiKey', - description: 'Private apps are deprecated and can\'t be created as of January 2022', }, ], default: 'accessToken', diff --git a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts index 9960ebed07d84..770105d43f2c6 100644 --- a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts +++ b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts @@ -45,7 +45,7 @@ export class ShopifyTrigger implements INodeType { }, }, { - name: 'shopifyTokenApi', + name: 'shopifyAccessTokenApi', required: true, displayOptions: { show: { @@ -92,7 +92,6 @@ export class ShopifyTrigger implements INodeType { { name: 'API Key', value: 'apiKey', - description: 'Private apps are deprecated and can\'t be created as of January 2022', }, ], default: 'accessToken', diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 6498ecd16da92..b015414caaa24 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -261,7 +261,7 @@ "dist/credentials/ServiceNowBasicApi.credentials.js", "dist/credentials/Sftp.credentials.js", "dist/credentials/ShopifyApi.credentials.js", - "dist/credentials/ShopifyTokenApi.credentials.js", + "dist/credentials/ShopifyAccessTokenApi.credentials.js", "dist/credentials/ShopifyOAuth2Api.credentials.js", "dist/credentials/Signl4Api.credentials.js", "dist/credentials/SlackApi.credentials.js", From e154493ffa2b3a4dfc97a37d911adc19cbb3eab4 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 29 Jun 2022 15:46:17 -0400 Subject: [PATCH 07/13] :zap: Improvements --- packages/cli/src/Server.ts | 6 ++--- .../ShopifyAccessTokenApi.credentials.ts | 27 ++++++++++--------- .../nodes/HttpRequest/HttpRequest.node.ts | 4 +++ .../nodes/Shopify/GenericFunctions.ts | 1 - 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 8c81040ce31a1..822e9f93c2346 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -2171,9 +2171,9 @@ class App { clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, - // redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ - // this.restEndpoint - // }/oauth2-credential/callback`, + redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ + this.restEndpoint + }/oauth2-credential/callback`, scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), }; diff --git a/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts b/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts index 67b37bca49f34..f0c074a01f741 100644 --- a/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts @@ -1,5 +1,5 @@ import { - IAuthenticateHeaderAuth, + IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, @@ -11,34 +11,35 @@ export class ShopifyAccessTokenApi implements ICredentialType { documentationUrl = 'shopify'; properties: INodeProperties[] = [ { - displayName: 'Access Token', - name: 'accessToken', + displayName: 'Shop Subdomain', + name: 'shopSubdomain', required: true, type: 'string', default: '', + description: 'Only the subdomain without .myshopify.com', }, { - displayName: 'APP Secret Key', - name: 'appSecretKey', + displayName: 'Access Token', + name: 'accessToken', required: true, type: 'string', default: '', - description: 'Secret key needed to verify the webhook when using Shopify Trigger node', }, { - displayName: 'Shop Subdomain', - name: 'shopSubdomain', + displayName: 'APP Secret Key', + name: 'appSecretKey', required: true, type: 'string', default: '', - description: 'Only the subdomain without .myshopify.com', + description: 'Secret key needed to verify the webhook when using Shopify Trigger node', }, ]; - authenticate: IAuthenticateHeaderAuth = { - type: 'headerAuth', + authenticate: IAuthenticateGeneric = { + type: 'generic', properties: { - name: 'X-Shopify-Access-Token', - value: '={{$credentials?.accessToken}}', + headers: { + 'X-Shopify-Access-Token': '={{$credentials?.accessToken}}' + }, }, }; test: ICredentialTestRequest = { diff --git a/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts b/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts index b19453870cd29..ba0ecce9dc3ec 100644 --- a/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts +++ b/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts @@ -1216,6 +1216,10 @@ export class HttpRequest implements INodeType { boxOAuth2Api: { includeCredentialsOnRefreshOnBody: true, }, + shopifyOAuth2Api: { + tokenType: 'Bearer', + includeAccessTokenInHeader: 'X-Shopify-Access-Token', + }, }; const additionalOAuth2Options = oAuth2Options[nodeCredentialType]; diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 1d70e1090ff33..bc6cb5c32f8f3 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -11,7 +11,6 @@ import { } from 'n8n-core'; import { - IAdditionalCredentialOptions, IDataObject, IOAuth2Options, NodeApiError, } from 'n8n-workflow'; From 2e46e908a44c81a9ee09ea93965f0de4f19fe424 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 29 Jun 2022 16:13:20 -0400 Subject: [PATCH 08/13] :zap: Improvements --- .../credentials/ShopifyAccessTokenApi.credentials.ts | 2 +- .../credentials/ShopifyOAuth2Api.credentials.ts | 4 ++-- packages/nodes-base/nodes/Shopify/GenericFunctions.ts | 2 +- packages/nodes-base/nodes/Shopify/Shopify.node.ts | 4 ++-- packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts | 8 +++++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts b/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts index f0c074a01f741..b87e196ff1192 100644 --- a/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyAccessTokenApi.credentials.ts @@ -38,7 +38,7 @@ export class ShopifyAccessTokenApi implements ICredentialType { type: 'generic', properties: { headers: { - 'X-Shopify-Access-Token': '={{$credentials?.accessToken}}' + 'X-Shopify-Access-Token': '={{$credentials?.accessToken}}', }, }, }; diff --git a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts index 6f3edd6adf90b..625bf5903fe21 100644 --- a/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ShopifyOAuth2Api.credentials.ts @@ -31,7 +31,7 @@ export class ShopifyOAuth2Api implements ICredentialType { type: 'string', default: '', required: true, - hint: 'Be aware that Shopify refers to the Client ID as API Key' + hint: 'Be aware that Shopify refers to the Client ID as API Key', }, { displayName: 'Client Secret', @@ -42,7 +42,7 @@ export class ShopifyOAuth2Api implements ICredentialType { }, default: '', required: true, - hint: 'Be aware that Shopify refers to the Client Secret as API Secret Key' + hint: 'Be aware that Shopify refers to the Client Secret as API Secret Key', }, { displayName: 'Grant Type', diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index bc6cb5c32f8f3..9bed953420683 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -51,7 +51,7 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions }; if (authenticationMethod === 'apiKey') { - Object.assign(options, { auth: { username: credentials.apiKey, password: credentials.password } }) + Object.assign(options, { auth: { username: credentials.apiKey, password: credentials.password } }); } if (Object.keys(option).length !== 0) { diff --git a/packages/nodes-base/nodes/Shopify/Shopify.node.ts b/packages/nodes-base/nodes/Shopify/Shopify.node.ts index a2ea6652af029..c0989c8218cda 100644 --- a/packages/nodes-base/nodes/Shopify/Shopify.node.ts +++ b/packages/nodes-base/nodes/Shopify/Shopify.node.ts @@ -107,8 +107,8 @@ export class Shopify implements INodeType { value: 'apiKey', }, ], - default: 'accessToken', - }, + default: 'apiKey', + }, { displayName: 'Resource', name: 'resource', diff --git a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts index 770105d43f2c6..74df777156d47 100644 --- a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts +++ b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts @@ -94,8 +94,8 @@ export class ShopifyTrigger implements INodeType { value: 'apiKey', }, ], - default: 'accessToken', - }, + default: 'apiKey', + }, { displayName: 'Topic', name: 'topic', @@ -407,6 +407,7 @@ export class ShopifyTrigger implements INodeType { const req = this.getRequestObject(); const authentication = this.getNodeParameter('authentication') as string; let secret = ''; + console.log('llego request'); if (authentication === 'apiKey') { const credentials = await this.getCredentials('shopifyApi'); @@ -414,7 +415,7 @@ export class ShopifyTrigger implements INodeType { } if (authentication === 'accessToken') { - const credentials = await this.getCredentials('shopifyTokenApi'); + const credentials = await this.getCredentials('shopifyAccessTokenApi'); secret = credentials.appSecretKey as string; } @@ -430,6 +431,7 @@ export class ShopifyTrigger implements INodeType { && headerData['x-shopify-api-version'] !== undefined) { // @ts-ignore const computedSignature = createHmac('sha256', secret).update(req.rawBody).digest('base64'); + if (headerData['x-shopify-hmac-sha256'] !== computedSignature) { return {}; } From 34b9f5f69755291e9d32846e07f568a96d427c97 Mon Sep 17 00:00:00 2001 From: ricardo Date: Thu, 7 Jul 2022 15:17:44 -0400 Subject: [PATCH 09/13] :zap: Rename includeAccessTokenInHeader to keyToIncludeInAccessTokenHeader --- packages/core/src/NodeExecuteFunctions.ts | 8 ++++---- packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts | 2 +- packages/nodes-base/nodes/Shopify/GenericFunctions.ts | 2 +- packages/workflow/src/Interfaces.ts | 6 +++++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 203f5bfed077e..af55ecb710b73 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -946,10 +946,10 @@ export async function requestOAuth2( newRequestOptions?.headers?.Authorization.split(' ')[1]; } - if (oAuth2Options?.includeAccessTokenInHeader) { + if (oAuth2Options?.keyToIncludeInAccessTokenHeader) { Object.assign(newRequestOptions, { headers: { - [oAuth2Options.includeAccessTokenInHeader]: token.accessToken, + [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, }, }); } @@ -1022,10 +1022,10 @@ export async function requestOAuth2( return this.helpers.httpRequest(newRequestOptions); } - if (oAuth2Options?.includeAccessTokenInHeader) { + if (oAuth2Options?.keyToIncludeInAccessTokenHeader) { Object.assign(newRequestOptions, { headers: { - [oAuth2Options.includeAccessTokenInHeader]: token.accessToken, + [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, }, }); } diff --git a/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts b/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts index ba0ecce9dc3ec..c7352ecb60f36 100644 --- a/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts +++ b/packages/nodes-base/nodes/HttpRequest/HttpRequest.node.ts @@ -1218,7 +1218,7 @@ export class HttpRequest implements INodeType { }, shopifyOAuth2Api: { tokenType: 'Bearer', - includeAccessTokenInHeader: 'X-Shopify-Access-Token', + keyToIncludeInAccessTokenHeader: 'X-Shopify-Access-Token', }, }; diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 9bed953420683..bbf4d6c94508d 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -47,7 +47,7 @@ export async function shopifyApiRequest(this: IHookFunctions | IExecuteFunctions const oAuth2Options: IOAuth2Options = { tokenType: 'Bearer', - includeAccessTokenInHeader: 'X-Shopify-Access-Token', + keyToIncludeInAccessTokenHeader: 'X-Shopify-Access-Token', }; if (authenticationMethod === 'apiKey') { diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index daef3b8d9a261..fd97eb7b66509 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -38,13 +38,17 @@ export interface IBinaryData { id?: string; } +// All properties in this interface except for +// "includeCredentialsOnRefreshOnBody" will get +// removed once we add the OAuth2 hooks to the +// credentials file. export interface IOAuth2Options { includeCredentialsOnRefreshOnBody?: boolean; property?: string; tokenType?: string; keepBearer?: boolean; tokenExpiredStatusCode?: number; - includeAccessTokenInHeader?: string; + keyToIncludeInAccessTokenHeader?: string; } export interface IConnection { From f28434a20b7f502e363f094998358d3b8cc732e1 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 13 Jul 2022 09:39:46 -0400 Subject: [PATCH 10/13] :zap: Assign values to only header property --- packages/core/src/NodeExecuteFunctions.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 2508221e84329..e8b9112c4a8d4 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -956,10 +956,8 @@ export async function requestOAuth2( } if (oAuth2Options?.keyToIncludeInAccessTokenHeader) { - Object.assign(newRequestOptions, { - headers: { - [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, - }, + Object.assign(newRequestOptions.headers, { + [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, }); } @@ -1073,10 +1071,8 @@ export async function requestOAuth2( } if (oAuth2Options?.keyToIncludeInAccessTokenHeader) { - Object.assign(newRequestOptions, { - headers: { - [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, - }, + Object.assign(newRequestOptions.headers, { + [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, }); } From 28d9b03822f55a0bcfca0d9640f783a818405cc6 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 13 Jul 2022 09:40:13 -0400 Subject: [PATCH 11/13] :fire: Remove unreachable code --- packages/core/src/NodeExecuteFunctions.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index e8b9112c4a8d4..25f486f092c13 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -1066,9 +1066,6 @@ export async function requestOAuth2( // Make the request again with the new token const newRequestOptions = newToken.sign(requestOptions as clientOAuth2.RequestObject); - if (isN8nRequest) { - return this.helpers.httpRequest(newRequestOptions); - } if (oAuth2Options?.keyToIncludeInAccessTokenHeader) { Object.assign(newRequestOptions.headers, { From 2e60190612f649d997323ec5c84f6bb3e8c3f186 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 13 Jul 2022 09:41:49 -0400 Subject: [PATCH 12/13] :zap: Add keyToIncludeInAccessTokenHeader when isN8nRequest --- packages/core/src/NodeExecuteFunctions.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 25f486f092c13..e028db3ebf8b8 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -996,6 +996,13 @@ export async function requestOAuth2( credentials, ); const refreshedRequestOption = newToken.sign(requestOptions as clientOAuth2.RequestObject); + + if (oAuth2Options?.keyToIncludeInAccessTokenHeader) { + Object.assign(newRequestOptions.headers, { + [oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken, + }); + } + return this.helpers.httpRequest(refreshedRequestOption); } throw error; From fdb1bca2e020f35526d0bdad7f625269706d6b5a Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 13 Jul 2022 09:42:55 -0400 Subject: [PATCH 13/13] :zap: Add CC grant type when isN8nRequest --- packages/core/src/NodeExecuteFunctions.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index e028db3ebf8b8..bba6e421693b5 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -978,10 +978,24 @@ export async function requestOAuth2( Authorization: '', }; } - const newToken = await token.refresh(tokenRefreshOptions); + + let newToken; + Logger.debug( `OAuth2 token for "${credentialsType}" used by node "${node.name}" has been renewed.`, ); + // if it's OAuth2 with client credentials grant type, get a new token + // instead of refreshing it. + if (OAuth2GrantType.clientCredentials === credentials.grantType) { + newToken = await token.client.credentials.getToken(); + } else { + newToken = await token.refresh(tokenRefreshOptions); + } + + Logger.debug( + `OAuth2 token for "${credentialsType}" used by node "${node.name}" has been renewed.`, + ); + credentials.oauthTokenData = newToken.data; // Find the credentials if (!node.credentials || !node.credentials[credentialsType]) {