mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Do not add Authentication header when authentication
type is body
(#8201)
This commit is contained in:
parent
ccb2b076f8
commit
ac1c642fdd
|
@ -1,6 +1,5 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import * as qs from 'querystring';
|
import * as qs from 'querystring';
|
||||||
import { Agent } from 'https';
|
import { Agent } from 'https';
|
||||||
|
@ -26,6 +25,7 @@ export interface ClientOAuth2Options {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret?: string;
|
clientSecret?: string;
|
||||||
accessTokenUri: string;
|
accessTokenUri: string;
|
||||||
|
authentication?: 'header' | 'body';
|
||||||
authorizationUri?: string;
|
authorizationUri?: string;
|
||||||
redirectUri?: string;
|
redirectUri?: string;
|
||||||
scopes?: string[];
|
scopes?: string[];
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import type { ClientOAuth2, ClientOAuth2Options } from './ClientOAuth2';
|
import type { ClientOAuth2 } from './ClientOAuth2';
|
||||||
import type { ClientOAuth2Token, ClientOAuth2TokenData } from './ClientOAuth2Token';
|
import type { ClientOAuth2Token, ClientOAuth2TokenData } from './ClientOAuth2Token';
|
||||||
import { DEFAULT_HEADERS } from './constants';
|
import { DEFAULT_HEADERS } from './constants';
|
||||||
|
import type { Headers } from './types';
|
||||||
import { auth, expects, getRequestOptions } from './utils';
|
import { auth, expects, getRequestOptions } from './utils';
|
||||||
|
|
||||||
interface CredentialsFlowBody {
|
interface CredentialsFlowBody {
|
||||||
|
client_id?: string;
|
||||||
|
client_secret?: string;
|
||||||
grant_type: 'client_credentials';
|
grant_type: 'client_credentials';
|
||||||
scope?: string;
|
scope?: string;
|
||||||
}
|
}
|
||||||
|
@ -19,10 +22,11 @@ export class CredentialsFlow {
|
||||||
/**
|
/**
|
||||||
* Request an access token using the client credentials.
|
* Request an access token using the client credentials.
|
||||||
*/
|
*/
|
||||||
async getToken(opts?: Partial<ClientOAuth2Options>): Promise<ClientOAuth2Token> {
|
async getToken(): Promise<ClientOAuth2Token> {
|
||||||
const options = { ...this.client.options, ...opts };
|
const options = { ...this.client.options };
|
||||||
expects(options, 'clientId', 'clientSecret', 'accessTokenUri');
|
expects(options, 'clientId', 'clientSecret', 'accessTokenUri');
|
||||||
|
|
||||||
|
const headers: Headers = { ...DEFAULT_HEADERS };
|
||||||
const body: CredentialsFlowBody = {
|
const body: CredentialsFlowBody = {
|
||||||
grant_type: 'client_credentials',
|
grant_type: 'client_credentials',
|
||||||
};
|
};
|
||||||
|
@ -31,15 +35,21 @@ export class CredentialsFlow {
|
||||||
body.scope = options.scopes.join(options.scopesSeparator ?? ' ');
|
body.scope = options.scopes.join(options.scopesSeparator ?? ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clientId = options.clientId;
|
||||||
|
const clientSecret = options.clientSecret;
|
||||||
|
|
||||||
|
if (options.authentication === 'body') {
|
||||||
|
body.client_id = clientId;
|
||||||
|
body.client_secret = clientSecret;
|
||||||
|
} else {
|
||||||
|
headers.Authorization = auth(clientId, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
const requestOptions = getRequestOptions(
|
const requestOptions = getRequestOptions(
|
||||||
{
|
{
|
||||||
url: options.accessTokenUri,
|
url: options.accessTokenUri,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers,
|
||||||
...DEFAULT_HEADERS,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
Authorization: auth(options.clientId, options.clientSecret),
|
|
||||||
},
|
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export { ClientOAuth2, ClientOAuth2Options, ClientOAuth2RequestObject } from './ClientOAuth2';
|
export { ClientOAuth2, ClientOAuth2Options, ClientOAuth2RequestObject } from './ClientOAuth2';
|
||||||
export { ClientOAuth2Token, ClientOAuth2TokenData } from './ClientOAuth2Token';
|
export { ClientOAuth2Token, ClientOAuth2TokenData } from './ClientOAuth2Token';
|
||||||
|
export type * from './types';
|
||||||
|
|
|
@ -1 +1,19 @@
|
||||||
export type Headers = Record<string, string | string[]>;
|
export type Headers = Record<string, string | string[]>;
|
||||||
|
|
||||||
|
export type OAuth2GrantType = 'pkce' | 'authorizationCode' | 'clientCredentials';
|
||||||
|
|
||||||
|
export interface OAuth2CredentialData {
|
||||||
|
clientId: string;
|
||||||
|
clientSecret?: string;
|
||||||
|
accessTokenUrl: string;
|
||||||
|
authentication?: 'header' | 'body';
|
||||||
|
authUrl?: string;
|
||||||
|
scope?: string;
|
||||||
|
authQueryParameters?: string;
|
||||||
|
grantType: OAuth2GrantType;
|
||||||
|
ignoreSSLIssues?: boolean;
|
||||||
|
oauthTokenData?: {
|
||||||
|
access_token: string;
|
||||||
|
refresh_token?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
import type { Headers } from '../src/types';
|
||||||
|
import type { ClientOAuth2Options } from '../src';
|
||||||
import { ClientOAuth2, ClientOAuth2Token } from '../src';
|
import { ClientOAuth2, ClientOAuth2Token } from '../src';
|
||||||
import * as config from './config';
|
import * as config from './config';
|
||||||
|
|
||||||
|
@ -11,18 +13,24 @@ describe('CredentialsFlow', () => {
|
||||||
nock.restore();
|
nock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(() => jest.clearAllMocks());
|
||||||
|
|
||||||
describe('#getToken', () => {
|
describe('#getToken', () => {
|
||||||
const createAuthClient = (scopes?: string[]) =>
|
const createAuthClient = ({
|
||||||
|
scopes,
|
||||||
|
authentication,
|
||||||
|
}: Pick<ClientOAuth2Options, 'scopes' | 'authentication'> = {}) =>
|
||||||
new ClientOAuth2({
|
new ClientOAuth2({
|
||||||
clientId: config.clientId,
|
clientId: config.clientId,
|
||||||
clientSecret: config.clientSecret,
|
clientSecret: config.clientSecret,
|
||||||
accessTokenUri: config.accessTokenUri,
|
accessTokenUri: config.accessTokenUri,
|
||||||
|
authentication,
|
||||||
authorizationGrants: ['credentials'],
|
authorizationGrants: ['credentials'],
|
||||||
scopes,
|
scopes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockTokenCall = (requestedScope?: string) =>
|
const mockTokenCall = async ({ requestedScope }: { requestedScope?: string } = {}) => {
|
||||||
nock(config.baseUrl)
|
const nockScope = nock(config.baseUrl)
|
||||||
.post(
|
.post(
|
||||||
'/login/oauth/access_token',
|
'/login/oauth/access_token',
|
||||||
({ scope, grant_type }) =>
|
({ scope, grant_type }) =>
|
||||||
|
@ -34,10 +42,19 @@ describe('CredentialsFlow', () => {
|
||||||
refresh_token: config.refreshToken,
|
refresh_token: config.refreshToken,
|
||||||
scope: requestedScope,
|
scope: requestedScope,
|
||||||
});
|
});
|
||||||
|
return new Promise<{ headers: Headers; body: unknown }>((resolve) => {
|
||||||
|
nockScope.once('request', (req) => {
|
||||||
|
resolve({
|
||||||
|
headers: req.headers,
|
||||||
|
body: req.requestBodyBuffers.toString('utf-8'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
it('should request the token', async () => {
|
it('should request the token', async () => {
|
||||||
const authClient = createAuthClient(['notifications']);
|
const authClient = createAuthClient({ scopes: ['notifications'] });
|
||||||
mockTokenCall('notifications');
|
const requestPromise = mockTokenCall({ requestedScope: 'notifications' });
|
||||||
|
|
||||||
const user = await authClient.credentials.getToken();
|
const user = await authClient.credentials.getToken();
|
||||||
|
|
||||||
|
@ -45,34 +62,62 @@ describe('CredentialsFlow', () => {
|
||||||
expect(user.accessToken).toEqual(config.accessToken);
|
expect(user.accessToken).toEqual(config.accessToken);
|
||||||
expect(user.tokenType).toEqual('bearer');
|
expect(user.tokenType).toEqual('bearer');
|
||||||
expect(user.data.scope).toEqual('notifications');
|
expect(user.data.scope).toEqual('notifications');
|
||||||
|
|
||||||
|
const { headers, body } = await requestPromise;
|
||||||
|
expect(headers.authorization).toBe('Basic YWJjOjEyMw==');
|
||||||
|
expect(body).toEqual('grant_type=client_credentials&scope=notifications');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('when scopes are undefined, it should not send scopes to an auth server', async () => {
|
it('when scopes are undefined, it should not send scopes to an auth server', async () => {
|
||||||
const authClient = createAuthClient();
|
const authClient = createAuthClient();
|
||||||
mockTokenCall();
|
const requestPromise = mockTokenCall();
|
||||||
|
|
||||||
const user = await authClient.credentials.getToken();
|
const user = await authClient.credentials.getToken();
|
||||||
expect(user).toBeInstanceOf(ClientOAuth2Token);
|
expect(user).toBeInstanceOf(ClientOAuth2Token);
|
||||||
expect(user.accessToken).toEqual(config.accessToken);
|
expect(user.accessToken).toEqual(config.accessToken);
|
||||||
expect(user.tokenType).toEqual('bearer');
|
expect(user.tokenType).toEqual('bearer');
|
||||||
expect(user.data.scope).toEqual(undefined);
|
expect(user.data.scope).toEqual(undefined);
|
||||||
|
|
||||||
|
const { body } = await requestPromise;
|
||||||
|
expect(body).toEqual('grant_type=client_credentials');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('when scopes is an empty array, it should send empty scope string to an auth server', async () => {
|
it('when scopes is an empty array, it should send empty scope string to an auth server', async () => {
|
||||||
const authClient = createAuthClient([]);
|
const authClient = createAuthClient({ scopes: [] });
|
||||||
mockTokenCall('');
|
const requestPromise = mockTokenCall({ requestedScope: '' });
|
||||||
|
|
||||||
const user = await authClient.credentials.getToken();
|
const user = await authClient.credentials.getToken();
|
||||||
expect(user).toBeInstanceOf(ClientOAuth2Token);
|
expect(user).toBeInstanceOf(ClientOAuth2Token);
|
||||||
expect(user.accessToken).toEqual(config.accessToken);
|
expect(user.accessToken).toEqual(config.accessToken);
|
||||||
expect(user.tokenType).toEqual('bearer');
|
expect(user.tokenType).toEqual('bearer');
|
||||||
expect(user.data.scope).toEqual('');
|
expect(user.data.scope).toEqual('');
|
||||||
|
|
||||||
|
const { body } = await requestPromise;
|
||||||
|
expect(body).toEqual('grant_type=client_credentials&scope=');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle authentication = "header"', async () => {
|
||||||
|
const authClient = createAuthClient({ scopes: [] });
|
||||||
|
const requestPromise = mockTokenCall({ requestedScope: '' });
|
||||||
|
await authClient.credentials.getToken();
|
||||||
|
const { headers, body } = await requestPromise;
|
||||||
|
expect(headers?.authorization).toBe('Basic YWJjOjEyMw==');
|
||||||
|
expect(body).toEqual('grant_type=client_credentials&scope=');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle authentication = "body"', async () => {
|
||||||
|
const authClient = createAuthClient({ scopes: [], authentication: 'body' });
|
||||||
|
const requestPromise = mockTokenCall({ requestedScope: '' });
|
||||||
|
await authClient.credentials.getToken();
|
||||||
|
const { headers, body } = await requestPromise;
|
||||||
|
expect(headers?.authorization).toBe(undefined);
|
||||||
|
expect(body).toEqual('grant_type=client_credentials&scope=&client_id=abc&client_secret=123');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#sign', () => {
|
describe('#sign', () => {
|
||||||
it('should be able to sign a standard request object', async () => {
|
it('should be able to sign a standard request object', async () => {
|
||||||
const authClient = createAuthClient(['notifications']);
|
const authClient = createAuthClient({ scopes: ['notifications'] });
|
||||||
mockTokenCall('notifications');
|
void mockTokenCall({ requestedScope: 'notifications' });
|
||||||
|
|
||||||
const token = await authClient.credentials.getToken();
|
const token = await authClient.credentials.getToken();
|
||||||
const requestOptions = token.sign({
|
const requestOptions = token.sign({
|
||||||
|
@ -99,8 +144,8 @@ describe('CredentialsFlow', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make a request to get a new access token', async () => {
|
it('should make a request to get a new access token', async () => {
|
||||||
const authClient = createAuthClient(['notifications']);
|
const authClient = createAuthClient({ scopes: ['notifications'] });
|
||||||
mockTokenCall('notifications');
|
void mockTokenCall({ requestedScope: 'notifications' });
|
||||||
|
|
||||||
const token = await authClient.credentials.getToken();
|
const token = await authClient.credentials.getToken();
|
||||||
expect(token.accessToken).toEqual(config.accessToken);
|
expect(token.accessToken).toEqual(config.accessToken);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { ClientOAuth2Options } from '@n8n/client-oauth2';
|
import type { ClientOAuth2Options, OAuth2CredentialData } from '@n8n/client-oauth2';
|
||||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||||
import Csrf from 'csrf';
|
import Csrf from 'csrf';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
|
@ -7,24 +7,11 @@ import * as qs from 'querystring';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
import split from 'lodash/split';
|
import split from 'lodash/split';
|
||||||
import type { OAuth2GrantType } from 'n8n-workflow';
|
|
||||||
import { ApplicationError, jsonParse, jsonStringify } from 'n8n-workflow';
|
import { ApplicationError, jsonParse, jsonStringify } from 'n8n-workflow';
|
||||||
import { Authorized, Get, RestController } from '@/decorators';
|
import { Authorized, Get, RestController } from '@/decorators';
|
||||||
import { OAuthRequest } from '@/requests';
|
import { OAuthRequest } from '@/requests';
|
||||||
import { AbstractOAuthController } from './abstractOAuth.controller';
|
import { AbstractOAuthController } from './abstractOAuth.controller';
|
||||||
|
|
||||||
interface OAuth2CredentialData {
|
|
||||||
clientId: string;
|
|
||||||
clientSecret?: string;
|
|
||||||
accessTokenUrl?: string;
|
|
||||||
authUrl?: string;
|
|
||||||
scope?: string;
|
|
||||||
authQueryParameters?: string;
|
|
||||||
authentication?: 'header' | 'body';
|
|
||||||
grantType: OAuth2GrantType;
|
|
||||||
ignoreSSLIssues?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CsrfStateParam {
|
interface CsrfStateParam {
|
||||||
cid: string;
|
cid: string;
|
||||||
token: string;
|
token: string;
|
||||||
|
@ -226,6 +213,7 @@ export class OAuth2CredentialController extends AbstractOAuthController {
|
||||||
clientSecret: credential.clientSecret ?? '',
|
clientSecret: credential.clientSecret ?? '',
|
||||||
accessTokenUri: credential.accessTokenUrl ?? '',
|
accessTokenUri: credential.accessTokenUrl ?? '',
|
||||||
authorizationUri: credential.authUrl ?? '',
|
authorizationUri: credential.authUrl ?? '',
|
||||||
|
authentication: credential.authentication ?? 'header',
|
||||||
redirectUri: `${this.baseUrl}/callback`,
|
redirectUri: `${this.baseUrl}/callback`,
|
||||||
scopes: split(credential.scope ?? 'openid', ','),
|
scopes: split(credential.scope ?? 'openid', ','),
|
||||||
scopesSeparator: credential.scope?.includes(',') ? ',' : ' ',
|
scopesSeparator: credential.scope?.includes(',') ? ',' : ' ',
|
||||||
|
|
|
@ -12,6 +12,7 @@ import type {
|
||||||
ClientOAuth2Options,
|
ClientOAuth2Options,
|
||||||
ClientOAuth2RequestObject,
|
ClientOAuth2RequestObject,
|
||||||
ClientOAuth2TokenData,
|
ClientOAuth2TokenData,
|
||||||
|
OAuth2CredentialData,
|
||||||
} from '@n8n/client-oauth2';
|
} from '@n8n/client-oauth2';
|
||||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||||
import type {
|
import type {
|
||||||
|
@ -103,7 +104,6 @@ import {
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
NodeOperationError,
|
NodeOperationError,
|
||||||
NodeSslError,
|
NodeSslError,
|
||||||
OAuth2GrantType,
|
|
||||||
WorkflowDataProxy,
|
WorkflowDataProxy,
|
||||||
createDeferredPromise,
|
createDeferredPromise,
|
||||||
deepCopy,
|
deepCopy,
|
||||||
|
@ -140,7 +140,6 @@ import {
|
||||||
} from './Constants';
|
} from './Constants';
|
||||||
import { extractValue } from './ExtractValue';
|
import { extractValue } from './ExtractValue';
|
||||||
import type { ExtendedValidationResult, IResponseError } from './Interfaces';
|
import type { ExtendedValidationResult, IResponseError } from './Interfaces';
|
||||||
import { getClientCredentialsToken } from './OAuth2Helper';
|
|
||||||
import {
|
import {
|
||||||
getAllWorkflowExecutionMetadata,
|
getAllWorkflowExecutionMetadata,
|
||||||
getWorkflowExecutionMetadata,
|
getWorkflowExecutionMetadata,
|
||||||
|
@ -1215,31 +1214,31 @@ export async function requestOAuth2(
|
||||||
oAuth2Options?: IOAuth2Options,
|
oAuth2Options?: IOAuth2Options,
|
||||||
isN8nRequest = false,
|
isN8nRequest = false,
|
||||||
) {
|
) {
|
||||||
const credentials = await this.getCredentials(credentialsType);
|
const credentials = (await this.getCredentials(
|
||||||
|
credentialsType,
|
||||||
|
)) as unknown as OAuth2CredentialData;
|
||||||
|
|
||||||
// Only the OAuth2 with authorization code grant needs connection
|
// Only the OAuth2 with authorization code grant needs connection
|
||||||
if (
|
if (credentials.grantType === 'authorizationCode' && credentials.oauthTokenData === undefined) {
|
||||||
credentials.grantType === OAuth2GrantType.authorizationCode &&
|
|
||||||
credentials.oauthTokenData === undefined
|
|
||||||
) {
|
|
||||||
throw new ApplicationError('OAuth credentials not connected');
|
throw new ApplicationError('OAuth credentials not connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
const oAuthClient = new ClientOAuth2({
|
const oAuthClient = new ClientOAuth2({
|
||||||
clientId: credentials.clientId as string,
|
clientId: credentials.clientId,
|
||||||
clientSecret: credentials.clientSecret as string,
|
clientSecret: credentials.clientSecret,
|
||||||
accessTokenUri: credentials.accessTokenUrl as string,
|
accessTokenUri: credentials.accessTokenUrl,
|
||||||
scopes: (credentials.scope as string).split(' '),
|
scopes: (credentials.scope as string).split(' '),
|
||||||
ignoreSSLIssues: credentials.ignoreSSLIssues as boolean,
|
ignoreSSLIssues: credentials.ignoreSSLIssues,
|
||||||
|
authentication: credentials.authentication ?? 'header',
|
||||||
});
|
});
|
||||||
|
|
||||||
let oauthTokenData = credentials.oauthTokenData as ClientOAuth2TokenData;
|
let oauthTokenData = credentials.oauthTokenData as ClientOAuth2TokenData;
|
||||||
// if it's the first time using the credentials, get the access token and save it into the DB.
|
// if it's the first time using the credentials, get the access token and save it into the DB.
|
||||||
if (
|
if (
|
||||||
credentials.grantType === OAuth2GrantType.clientCredentials &&
|
credentials.grantType === 'clientCredentials' &&
|
||||||
(oauthTokenData === undefined || Object.keys(oauthTokenData).length === 0)
|
(oauthTokenData === undefined || Object.keys(oauthTokenData).length === 0)
|
||||||
) {
|
) {
|
||||||
const { data } = await getClientCredentialsToken(oAuthClient, credentials);
|
const { data } = await oAuthClient.credentials.getToken();
|
||||||
// Find the credentials
|
// Find the credentials
|
||||||
if (!node.credentials?.[credentialsType]) {
|
if (!node.credentials?.[credentialsType]) {
|
||||||
throw new ApplicationError('Node does not have credential type', {
|
throw new ApplicationError('Node does not have credential type', {
|
||||||
|
@ -1249,12 +1248,13 @@ export async function requestOAuth2(
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeCredentials = node.credentials[credentialsType];
|
const nodeCredentials = node.credentials[credentialsType];
|
||||||
|
credentials.oauthTokenData = data;
|
||||||
|
|
||||||
// Save the refreshed token
|
// Save the refreshed token
|
||||||
await additionalData.credentialsHelper.updateCredentials(
|
await additionalData.credentialsHelper.updateCredentials(
|
||||||
nodeCredentials,
|
nodeCredentials,
|
||||||
credentialsType,
|
credentialsType,
|
||||||
Object.assign(credentials, { oauthTokenData: data }),
|
credentials as unknown as ICredentialDataDecryptedObject,
|
||||||
);
|
);
|
||||||
|
|
||||||
oauthTokenData = data;
|
oauthTokenData = data;
|
||||||
|
@ -1296,7 +1296,7 @@ export async function requestOAuth2(
|
||||||
const tokenRefreshOptions: IDataObject = {};
|
const tokenRefreshOptions: IDataObject = {};
|
||||||
if (oAuth2Options?.includeCredentialsOnRefreshOnBody) {
|
if (oAuth2Options?.includeCredentialsOnRefreshOnBody) {
|
||||||
const body: IDataObject = {
|
const body: IDataObject = {
|
||||||
client_id: credentials.clientId as string,
|
client_id: credentials.clientId,
|
||||||
...(credentials.grantType === 'authorizationCode' && {
|
...(credentials.grantType === 'authorizationCode' && {
|
||||||
client_secret: credentials.clientSecret as string,
|
client_secret: credentials.clientSecret as string,
|
||||||
}),
|
}),
|
||||||
|
@ -1314,8 +1314,8 @@ export async function requestOAuth2(
|
||||||
);
|
);
|
||||||
// if it's OAuth2 with client credentials grant type, get a new token
|
// if it's OAuth2 with client credentials grant type, get a new token
|
||||||
// instead of refreshing it.
|
// instead of refreshing it.
|
||||||
if (OAuth2GrantType.clientCredentials === credentials.grantType) {
|
if (credentials.grantType === 'clientCredentials') {
|
||||||
newToken = await getClientCredentialsToken(token.client, credentials);
|
newToken = await token.client.credentials.getToken();
|
||||||
} else {
|
} else {
|
||||||
newToken = await token.refresh(tokenRefreshOptions as unknown as ClientOAuth2Options);
|
newToken = await token.refresh(tokenRefreshOptions as unknown as ClientOAuth2Options);
|
||||||
}
|
}
|
||||||
|
@ -1335,7 +1335,7 @@ export async function requestOAuth2(
|
||||||
await additionalData.credentialsHelper.updateCredentials(
|
await additionalData.credentialsHelper.updateCredentials(
|
||||||
nodeCredentials,
|
nodeCredentials,
|
||||||
credentialsType,
|
credentialsType,
|
||||||
credentials,
|
credentials as unknown as ICredentialDataDecryptedObject,
|
||||||
);
|
);
|
||||||
const refreshedRequestOption = newToken.sign(requestOptions as ClientOAuth2RequestObject);
|
const refreshedRequestOption = newToken.sign(requestOptions as ClientOAuth2RequestObject);
|
||||||
|
|
||||||
|
@ -1391,8 +1391,8 @@ export async function requestOAuth2(
|
||||||
|
|
||||||
// if it's OAuth2 with client credentials grant type, get a new token
|
// if it's OAuth2 with client credentials grant type, get a new token
|
||||||
// instead of refreshing it.
|
// instead of refreshing it.
|
||||||
if (OAuth2GrantType.clientCredentials === credentials.grantType) {
|
if (credentials.grantType === 'clientCredentials') {
|
||||||
newToken = await getClientCredentialsToken(token.client, credentials);
|
newToken = await token.client.credentials.getToken();
|
||||||
} else {
|
} else {
|
||||||
newToken = await token.refresh(tokenRefreshOptions as unknown as ClientOAuth2Options);
|
newToken = await token.refresh(tokenRefreshOptions as unknown as ClientOAuth2Options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
|
||||||
import type { ClientOAuth2, ClientOAuth2Options, ClientOAuth2Token } from '@n8n/client-oauth2';
|
|
||||||
|
|
||||||
export const getClientCredentialsToken = async (
|
|
||||||
oAuth2Client: ClientOAuth2,
|
|
||||||
credentials: ICredentialDataDecryptedObject,
|
|
||||||
): Promise<ClientOAuth2Token> => {
|
|
||||||
const options = {};
|
|
||||||
if (credentials.authentication === 'body') {
|
|
||||||
Object.assign(options, {
|
|
||||||
headers: {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
Authorization: '',
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
client_id: credentials.clientId as string,
|
|
||||||
client_secret: credentials.clientSecret as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return oAuth2Client.credentials.getToken(options as ClientOAuth2Options);
|
|
||||||
};
|
|
|
@ -2124,23 +2124,6 @@ export interface IConnectedNode {
|
||||||
depth: number;
|
depth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum OAuth2GrantType {
|
|
||||||
pkce = 'pkce',
|
|
||||||
authorizationCode = 'authorizationCode',
|
|
||||||
clientCredentials = 'clientCredentials',
|
|
||||||
}
|
|
||||||
export interface IOAuth2Credentials {
|
|
||||||
grantType: 'authorizationCode' | 'clientCredentials' | 'pkce';
|
|
||||||
clientId: string;
|
|
||||||
clientSecret: string;
|
|
||||||
accessTokenUrl: string;
|
|
||||||
authUrl: string;
|
|
||||||
authQueryParameters: string;
|
|
||||||
authentication: 'body' | 'header';
|
|
||||||
scope: string;
|
|
||||||
oauthTokenData?: IDataObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PublicInstalledPackage = {
|
export type PublicInstalledPackage = {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
installedVersion: string;
|
installedVersion: string;
|
||||||
|
|
Loading…
Reference in a new issue