diff --git a/packages/@n8n/client-oauth2/src/ClientOAuth2.ts b/packages/@n8n/client-oauth2/src/ClientOAuth2.ts index 6074cf11cd..c9e21a6a42 100644 --- a/packages/@n8n/client-oauth2/src/ClientOAuth2.ts +++ b/packages/@n8n/client-oauth2/src/ClientOAuth2.ts @@ -4,7 +4,9 @@ /* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as qs from 'querystring'; +import { Agent } from 'https'; import axios from 'axios'; +import type { AxiosRequestConfig } from 'axios'; import { getAuthError } from './utils'; import type { ClientOAuth2TokenData } from './ClientOAuth2Token'; import { ClientOAuth2Token } from './ClientOAuth2Token'; @@ -18,6 +20,7 @@ export interface ClientOAuth2RequestObject { body?: Record; query?: qs.ParsedUrlQuery; headers?: Headers; + ignoreSSLIssues?: boolean; } export interface ClientOAuth2Options { @@ -32,6 +35,7 @@ export interface ClientOAuth2Options { state?: string; body?: Record; query?: qs.ParsedUrlQuery; + ignoreSSLIssues?: boolean; } class ResponseError extends Error { @@ -40,6 +44,8 @@ class ResponseError extends Error { } } +const sslIgnoringAgent = new Agent({ rejectUnauthorized: false }); + /** * Construct an object that can handle the multiple OAuth 2.0 flows. */ @@ -86,7 +92,7 @@ export class ClientOAuth2 { url += (url.indexOf('?') === -1 ? '?' : '&') + query; } - const response = await axios.request({ + const requestConfig: AxiosRequestConfig = { url, method: options.method, data: qs.stringify(options.body), @@ -95,7 +101,13 @@ export class ClientOAuth2 { // Axios rejects the promise by default for all status codes 4xx. // We override this to reject promises only on 5xxs validateStatus: (status) => status < 500, - }); + }; + + if (options.ignoreSSLIssues) { + requestConfig.httpsAgent = sslIgnoringAgent; + } + + const response = await axios.request(requestConfig); const body = this.parseResponseBody(response.data); diff --git a/packages/@n8n/client-oauth2/src/CodeFlow.ts b/packages/@n8n/client-oauth2/src/CodeFlow.ts index 7d3b842329..20679188a8 100644 --- a/packages/@n8n/client-oauth2/src/CodeFlow.ts +++ b/packages/@n8n/client-oauth2/src/CodeFlow.ts @@ -53,13 +53,13 @@ export class CodeFlow { * the user access token. */ async getToken( - uri: string | URL, + urlString: string, opts?: Partial, ): Promise { - const options = { ...this.client.options, ...opts }; + const options: ClientOAuth2Options = { ...this.client.options, ...opts }; expects(options, 'clientId', 'accessTokenUri'); - const url = uri instanceof URL ? uri : new URL(uri, DEFAULT_URL_BASE); + const url = new URL(urlString, DEFAULT_URL_BASE); if ( typeof options.redirectUri === 'string' && typeof url.pathname === 'string' && @@ -70,7 +70,7 @@ export class CodeFlow { if (!url.search?.substring(1)) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new TypeError(`Unable to process uri: ${uri.toString()}`); + throw new TypeError(`Unable to process uri: ${urlString}`); } const data = diff --git a/packages/@n8n/client-oauth2/src/utils.ts b/packages/@n8n/client-oauth2/src/utils.ts index 44317e5dc6..9d57c78b22 100644 --- a/packages/@n8n/client-oauth2/src/utils.ts +++ b/packages/@n8n/client-oauth2/src/utils.ts @@ -63,14 +63,15 @@ export function auth(username: string, password: string): string { */ export function getRequestOptions( { url, method, body, query, headers }: ClientOAuth2RequestObject, - options: any, + options: ClientOAuth2Options, ): ClientOAuth2RequestObject { const rOptions = { url, method, body: { ...body, ...options.body }, query: { ...query, ...options.query }, - headers: { ...headers, ...options.headers }, + headers: headers ?? {}, + ignoreSSLIssues: options.ignoreSSLIssues, }; // if request authorization was overridden delete it from header if (rOptions.headers.Authorization === '') { diff --git a/packages/cli/src/credentials/oauth2Credential.api.ts b/packages/cli/src/credentials/oauth2Credential.api.ts index e2cf481cee..b6a9573ba6 100644 --- a/packages/cli/src/credentials/oauth2Credential.api.ts +++ b/packages/cli/src/credentials/oauth2Credential.api.ts @@ -265,6 +265,7 @@ oauth2CredentialController.get( redirectUri: `${getInstanceBaseUrl()}/${restEndpoint}/oauth2-credential/callback`, scopes: split(scopes, ','), scopesSeparator: scopes.includes(',') ? ',' : ' ', + ignoreSSLIssues: get(oauthCredentials, 'ignoreSSLIssues') as boolean, }; if (oauthCredentials.grantType === 'pkce') { diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 7f2e66265d..aefb5d5003 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -1092,6 +1092,7 @@ export async function requestOAuth2( clientSecret: credentials.clientSecret as string, accessTokenUri: credentials.accessTokenUrl as string, scopes: (credentials.scope as string).split(' '), + ignoreSSLIssues: credentials.ignoreSSLIssues as boolean, }); let oauthTokenData = credentials.oauthTokenData as ClientOAuth2TokenData; @@ -1131,6 +1132,9 @@ export async function requestOAuth2( }, oAuth2Options?.tokenType || oauthTokenData.tokenType, ); + + (requestOptions as OptionsWithUri).rejectUnauthorized = !credentials.ignoreSSLIssues; + // Signs the request by adding authorization headers or query parameters depending // on the token-type used. const newRequestOptions = token.sign(requestOptions as ClientOAuth2RequestObject); diff --git a/packages/nodes-base/credentials/OAuth2Api.credentials.ts b/packages/nodes-base/credentials/OAuth2Api.credentials.ts index 22b0d8258c..ed61e2d639 100644 --- a/packages/nodes-base/credentials/OAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/OAuth2Api.credentials.ts @@ -104,5 +104,12 @@ export class OAuth2Api implements ICredentialType { ], default: 'header', }, + { + displayName: 'Ignore SSL Issues', + name: 'ignoreSSLIssues', + type: 'boolean', + default: false, + doNotInherit: true, + }, ]; } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 5dfc701eef..ed2f1f88a7 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -1116,6 +1116,7 @@ export interface INodeProperties { extractValue?: INodePropertyValueExtractor; modes?: INodePropertyMode[]; requiresDataPath?: 'single' | 'multiple'; + doNotInherit?: boolean; } export interface INodePropertyModeTypeOptions { diff --git a/packages/workflow/src/NodeHelpers.ts b/packages/workflow/src/NodeHelpers.ts index 4b10ce92a9..8e658c0e43 100644 --- a/packages/workflow/src/NodeHelpers.ts +++ b/packages/workflow/src/NodeHelpers.ts @@ -1601,6 +1601,8 @@ export function mergeNodeProperties( ): void { let existingIndex: number; for (const property of addProperties) { + if (property.doNotInherit) continue; + existingIndex = mainProperties.findIndex((element) => element.name === property.name); if (existingIndex === -1) {