feat(core) Simplify authentication type (#3578)

*  Add generic auth type

*  Remove queryAuth

*  Remove bearer

*  Remove headerAuth

*  Remove basicAuth

*  Adjust tests

*  Small improvements

* 👕 Fix lint issue
This commit is contained in:
Jan Oberhauser 2022-06-26 15:55:51 -07:00 committed by GitHub
parent 1e4fd9e4df
commit 86721fc496
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 207 additions and 281 deletions

View file

@ -98,73 +98,41 @@ export class CredentialsHelper extends ICredentialsHelper {
if (typeof credentialType.authenticate === 'object') { if (typeof credentialType.authenticate === 'object') {
// Predefined authentication method // Predefined authentication method
let keyResolved: string;
let valueResolved: string;
const { authenticate } = credentialType; const { authenticate } = credentialType;
if (requestOptions.headers === undefined) { if (requestOptions.headers === undefined) {
requestOptions.headers = {}; requestOptions.headers = {};
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (authenticate.type === 'generic') {
if (authenticate.type === 'bearer') { Object.entries(authenticate.properties).forEach(([outerKey, outerValue]) => {
const tokenPropertyName: string = Object.entries(outerValue).forEach(([key, value]) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access keyResolved = this.resolveValue(
authenticate.properties.tokenPropertyName ?? 'accessToken'; key,
requestOptions.headers.Authorization = `Bearer ${ { $credentials: credentials },
credentials[tokenPropertyName] as string workflow,
}`; node,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access defaultTimezone,
} else if (authenticate.type === 'basicAuth') { );
const userPropertyName: string =
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
authenticate.properties.userPropertyName ?? 'user';
const passwordPropertyName: string =
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
authenticate.properties.passwordPropertyName ?? 'password';
requestOptions.auth = { valueResolved = this.resolveValue(
username: credentials[userPropertyName] as string, value as string,
password: credentials[passwordPropertyName] as string, { $credentials: credentials },
}; workflow,
} else if (authenticate.type === 'headerAuth') { node,
const key = this.resolveValue( defaultTimezone,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access );
authenticate.properties.name,
{ $credentials: credentials },
workflow,
node,
defaultTimezone,
);
const value = this.resolveValue( // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!requestOptions[outerKey]) {
authenticate.properties.value, // @ts-ignore
{ $credentials: credentials }, requestOptions[outerKey] = {};
workflow, }
node, // @ts-ignore
defaultTimezone, requestOptions[outerKey][keyResolved] = valueResolved;
); });
requestOptions.headers[key] = value; });
} else if (authenticate.type === 'queryAuth') {
const key = this.resolveValue(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
authenticate.properties.key,
{ $credentials: credentials },
workflow,
node,
defaultTimezone,
);
const value = this.resolveValue(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
authenticate.properties.value,
{ $credentials: credentials },
workflow,
node,
defaultTimezone,
);
if (!requestOptions.qs) {
requestOptions.qs = {};
}
requestOptions.qs[key] = value;
} }
} }
} }

View file

@ -1,10 +1,7 @@
import { CredentialsHelper, CredentialTypes } from '../../src'; import { CredentialsHelper, CredentialTypes } from '../../src';
import * as Helpers from './Helpers'; import * as Helpers from './Helpers';
import { import {
IAuthenticateBasicAuth, IAuthenticateGeneric,
IAuthenticateBearer,
IAuthenticateHeaderAuth,
IAuthenticateQueryAuth,
ICredentialDataDecryptedObject, ICredentialDataDecryptedObject,
ICredentialType, ICredentialType,
ICredentialTypeData, ICredentialTypeData,
@ -27,7 +24,7 @@ describe('CredentialsHelper', () => {
output: IHttpRequestOptions; output: IHttpRequestOptions;
}> = [ }> = [
{ {
description: 'built-in basicAuth, default property names', description: 'basicAuth, default property names',
input: { input: {
credentials: { credentials: {
user: 'user1', user: 'user1',
@ -51,10 +48,15 @@ describe('CredentialsHelper', () => {
}, },
]; ];
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'basicAuth', type: 'generic',
properties: {}, properties: {
} as IAuthenticateBasicAuth; auth: {
username: '={{$credentials.user}}',
password: '={{$credentials.password}}',
},
},
};
})(), })(),
}, },
output: { output: {
@ -65,48 +67,67 @@ describe('CredentialsHelper', () => {
}, },
}, },
{ {
description: 'built-in basicAuth, custom property names', description: 'headerAuth',
input: { input: {
credentials: { credentials: {
customUser: 'user2', accessToken: 'test',
customPassword: 'password2',
}, },
credentialType: new (class TestApi implements ICredentialType { credentialType: new (class TestApi implements ICredentialType {
name = 'testApi'; name = 'testApi';
displayName = 'Test API'; displayName = 'Test API';
properties: INodeProperties[] = [ properties: INodeProperties[] = [
{ {
displayName: 'User', displayName: 'Access Token',
name: 'user', name: 'accessToken',
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string', type: 'string',
default: '', default: '',
}, },
]; ];
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'basicAuth', type: 'generic',
properties: { properties: {
userPropertyName: 'customUser', headers: {
passwordPropertyName: 'customPassword', Authorization: '=Bearer {{$credentials.accessToken}}',
},
}, },
} as IAuthenticateBasicAuth; };
})(), })(),
}, },
output: { output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} },
url: '',
headers: {},
auth: { username: 'user2', password: 'password2' },
qs: {},
},
}, },
{ {
description: 'built-in headerAuth', description: 'headerAuth, key and value expressions',
input: {
credentials: {
accessToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'={{$credentials.accessToken}}': '=Bearer {{$credentials.accessToken}}',
},
},
};
})(),
},
output: { url: '', headers: { test: 'Bearer test' }, qs: {} },
},
{
description: 'queryAuth',
input: { input: {
credentials: { credentials: {
accessToken: 'test', accessToken: 'test',
@ -124,95 +145,13 @@ describe('CredentialsHelper', () => {
]; ];
authenticate = { authenticate = {
type: 'headerAuth', type: 'generic',
properties: { properties: {
name: 'Authorization', qs: {
value: '=Bearer {{$credentials.accessToken}}', accessToken: '={{$credentials.accessToken}}',
},
}, },
} as IAuthenticateHeaderAuth; } as IAuthenticateGeneric;
})(),
},
output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} },
},
{
description: 'built-in bearer, default property name',
input: {
credentials: {
accessToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
default: '',
},
];
authenticate = {
type: 'bearer',
properties: {},
} as IAuthenticateBearer;
})(),
},
output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} },
},
{
description: 'built-in bearer, custom property name',
input: {
credentials: {
myToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'My Token',
name: 'myToken',
type: 'string',
default: '',
},
];
authenticate = {
type: 'bearer',
properties: {
tokenPropertyName: 'myToken',
},
} as IAuthenticateBearer;
})(),
},
output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} },
},
{
description: 'built-in queryAuth',
input: {
credentials: {
accessToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
default: '',
},
];
authenticate = {
type: 'queryAuth',
properties: {
key: 'accessToken',
value: '={{$credentials.accessToken}}',
},
} as IAuthenticateQueryAuth;
})(), })(),
}, },
output: { url: '', headers: {}, qs: { accessToken: 'test' } }, output: { url: '', headers: {}, qs: { accessToken: 'test' } },

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateBearer, IAuthenticateGeneric,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -17,9 +17,13 @@ export class AsanaApi implements ICredentialType {
}, },
]; ];
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'bearer', type: 'generic',
properties: {}, properties: {
} as IAuthenticateBearer; headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
} }

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateQueryAuth, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -24,13 +24,14 @@ export class CalApi implements ICredentialType {
}, },
]; ];
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'queryAuth', type: 'generic',
properties: { properties: {
key: 'apiKey', qs: {
value: '={{$credentials.apiKey}}', apiKey: '={{$credentials.apiKey}}',
},
}, },
} as IAuthenticateQueryAuth; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateHeaderAuth, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -30,13 +30,16 @@ export class GithubApi implements ICredentialType {
default: '', default: '',
}, },
]; ];
authenticate: IAuthenticateHeaderAuth = {
type: 'headerAuth', authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: { properties: {
name: 'Authorization', headers: {
value: '=token {{$credentials?.accessToken}}', Authorization: '=token {{$credentials?.accessToken}}',
},
}, },
}; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: '={{$credentials?.server}}', baseURL: '={{$credentials?.server}}',

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateHeaderAuth, IAuthenticateGeneric,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -26,11 +26,13 @@ export class HttpHeaderAuth implements ICredentialType {
default: '', default: '',
}, },
]; ];
authenticate = {
type: 'headerAuth', authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: { properties: {
name: '={{credentials.name}}', headers: {
value: '={{credentials.value}}', '={{$credentials.name}}': '={{$credentials.value}}',
},
}, },
} as IAuthenticateHeaderAuth; };
} }

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateBearer, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -30,8 +30,12 @@ export class Magento2Api implements ICredentialType {
}, },
}; };
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'bearer', type: 'generic',
properties: {}, properties: {
} as IAuthenticateBearer; headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
} }

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateBasicAuth, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -30,13 +30,17 @@ export class MailjetEmailApi implements ICredentialType {
description: 'Whether to allow to run the API call in a Sandbox mode, where all validations of the payload will be done without delivering the message', description: 'Whether to allow to run the API call in a Sandbox mode, where all validations of the payload will be done without delivering the message',
}, },
]; ];
authenticate: IAuthenticateBasicAuth = {
type: 'basicAuth', authenticate: IAuthenticateGeneric = {
properties: { type: 'generic',
userPropertyName: 'apiKey', properties: {
passwordPropertyName: 'secretKey', auth: {
}, username: '={{$credentials.apiKey}}',
}; password: '={{$credentials.secretKey}}',
},
},
};
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: `https://api.mailjet.com`, baseURL: `https://api.mailjet.com`,

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateHeaderAuth, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -17,13 +17,16 @@ export class MailjetSmsApi implements ICredentialType {
default: '', default: '',
}, },
]; ];
authenticate: IAuthenticateHeaderAuth = {
type: 'headerAuth', authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: { properties: {
name: 'Authorization', headers: {
value: '=Bearer {{$credentials.token}}', Authorization: '=Bearer {{$credentials.token}}',
},
}, },
}; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: `https://api.mailjet.com`, baseURL: `https://api.mailjet.com`,

View file

@ -1,6 +1,5 @@
import { import {
IAuthenticateQueryAuth, IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -19,11 +18,12 @@ export class PipedriveApi implements ICredentialType {
}, },
]; ];
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'queryAuth', type: 'generic',
properties: { properties: {
key: 'api_token', qs: {
value: '={{$credentials.apiToken}}', api_token: '={{$credentials.apiToken}}',
},
}, },
} as IAuthenticateQueryAuth; };
} }

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateBasicAuth, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -40,10 +40,17 @@ export class ServiceNowBasicApi implements ICredentialType {
required: true, required: true,
}, },
]; ];
authenticate: IAuthenticateBasicAuth = {
type: 'basicAuth', authenticate: IAuthenticateGeneric = {
properties: {}, type: 'generic',
properties: {
auth: {
username: '={{$credentials.user}}',
password: '={{$credentials.password}}',
},
},
}; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: '=https://{{$credentials?.subdomain}}.service-now.com', baseURL: '=https://{{$credentials?.subdomain}}.service-now.com',

View file

@ -1,6 +1,5 @@
import { import {
IAuthenticateBearer, IAuthenticateGeneric,
IAuthenticateQueryAuth,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -19,12 +18,16 @@ export class SlackApi implements ICredentialType {
required: true, required: true,
}, },
]; ];
authenticate: IAuthenticateBearer = {
type: 'bearer', authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: { properties: {
tokenPropertyName: 'accessToken', headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
}, },
}; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: 'https://slack.com', baseURL: 'https://slack.com',

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateBearer, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -18,12 +18,14 @@ export class TodoistApi implements ICredentialType {
]; ];
authenticate = { authenticate: IAuthenticateGeneric = {
type: 'bearer', type: 'generic',
properties: { properties: {
tokenPropertyName: 'apiKey', headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
}, },
} as IAuthenticateBearer; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {

View file

@ -1,5 +1,5 @@
import { import {
IAuthenticateBearer, IAuthenticateGeneric,
ICredentialTestRequest, ICredentialTestRequest,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
@ -17,12 +17,15 @@ export class TwakeCloudApi implements ICredentialType {
default: '', default: '',
}, },
]; ];
authenticate = {
type: 'bearer', authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: { properties: {
tokenPropertyName: 'workspaceKey', headers: {
Authorization: '=Bearer {{$credentials.workspaceKey}}',
},
}, },
} as IAuthenticateBearer; };
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {

View file

@ -150,6 +150,16 @@ export interface IRequestOptionsSimplified {
qs: IDataObject; qs: IDataObject;
} }
export interface IRequestOptionsSimplifiedAuth {
auth?: {
username: string;
password: string;
};
body?: IDataObject;
headers?: IDataObject;
qs?: IDataObject;
}
export abstract class ICredentialsHelper { export abstract class ICredentialsHelper {
encryptionKey: string; encryptionKey: string;
@ -191,40 +201,16 @@ export abstract class ICredentialsHelper {
export interface IAuthenticateBase { export interface IAuthenticateBase {
type: string; type: string;
properties: { properties:
[key: string]: string; | {
}; [key: string]: string;
}
| IRequestOptionsSimplifiedAuth;
} }
export interface IAuthenticateBasicAuth extends IAuthenticateBase { export interface IAuthenticateGeneric extends IAuthenticateBase {
type: 'basicAuth'; type: 'generic';
properties: { properties: IRequestOptionsSimplifiedAuth;
userPropertyName?: string;
passwordPropertyName?: string;
};
}
export interface IAuthenticateBearer extends IAuthenticateBase {
type: 'bearer';
properties: {
tokenPropertyName?: string;
};
}
export interface IAuthenticateHeaderAuth extends IAuthenticateBase {
type: 'headerAuth';
properties: {
name: string;
value: string;
};
}
export interface IAuthenticateQueryAuth extends IAuthenticateBase {
type: 'queryAuth';
properties: {
key: string;
value: string;
};
} }
export type IAuthenticate = export type IAuthenticate =
@ -232,10 +218,7 @@ export type IAuthenticate =
credentials: ICredentialDataDecryptedObject, credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions, requestOptions: IHttpRequestOptions,
) => Promise<IHttpRequestOptions>) ) => Promise<IHttpRequestOptions>)
| IAuthenticateBasicAuth | IAuthenticateGeneric;
| IAuthenticateBearer
| IAuthenticateHeaderAuth
| IAuthenticateQueryAuth;
export interface IAuthenticateRuleBase { export interface IAuthenticateRuleBase {
type: string; type: string;