Skip to content

Commit

Permalink
feat(core): Add "Client Credentials" grant type to OAuth2 (#3489)
Browse files Browse the repository at this point in the history
* ⚡ Add OAuth2 client credentials grant type

* ⚡ Improvements

* 🐛 Fix linting issue

* 🐛 Fix typo

* 🐛 Fix small issue with type

* 🐛 When token expire get a new one instead of refreshing it

* ⚡ Fix issue that it did not display it correctly for OAuth1

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
  • Loading branch information
RicardoE105 and janober authored Jun 14, 2022
1 parent 51663c1 commit e29c597
Show file tree
Hide file tree
Showing 55 changed files with 417 additions and 13 deletions.
57 changes: 47 additions & 10 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ import {
WorkflowExecuteMode,
LoggerProxy as Logger,
IExecuteData,
OAuth2GrantType,
IOAuth2Credentials,
} from 'n8n-workflow';

import { Agent } from 'https';
Expand Down Expand Up @@ -881,19 +883,46 @@ export async function requestOAuth2(
oAuth2Options?: IOAuth2Options,
isN8nRequest = false,
) {
const credentials = await this.getCredentials(credentialsType);
const credentials = (await this.getCredentials(credentialsType)) as unknown as IOAuth2Credentials;

if (credentials.oauthTokenData === undefined) {
// Only the OAuth2 with authorization code grant needs connection
if (
credentials.grantType === OAuth2GrantType.authorizationCode &&
credentials.oauthTokenData === undefined
) {
throw new Error('OAuth credentials not connected!');
}

const oAuthClient = new clientOAuth2({
clientId: credentials.clientId as string,
clientSecret: credentials.clientSecret as string,
accessTokenUri: credentials.accessTokenUrl as string,
clientId: credentials.clientId,
clientSecret: credentials.clientSecret,
accessTokenUri: credentials.accessTokenUrl,
scopes: credentials.scope.split(' '),
});

const oauthTokenData = credentials.oauthTokenData as clientOAuth2.Data;
let oauthTokenData = credentials.oauthTokenData as clientOAuth2.Data;
// if it's the first time using the credentials, get the access token and save it into the DB.
if (credentials.grantType === OAuth2GrantType.clientCredentials && oauthTokenData === undefined) {
const { data } = await oAuthClient.credentials.getToken();

// Find the credentials
if (!node.credentials || !node.credentials[credentialsType]) {
throw new Error(
`The node "${node.name}" does not have credentials of type "${credentialsType}"!`,
);
}

const nodeCredentials = node.credentials[credentialsType];

// Save the refreshed token
await additionalData.credentialsHelper.updateCredentials(
nodeCredentials,
credentialsType,
credentials as unknown as ICredentialDataDecryptedObject,
);

oauthTokenData = data;
}

const token = oAuthClient.createToken(
get(oauthTokenData, oAuth2Options?.property as string) || oauthTokenData.accessToken,
Expand Down Expand Up @@ -926,8 +955,8 @@ export async function requestOAuth2(

if (oAuth2Options?.includeCredentialsOnRefreshOnBody) {
const body: IDataObject = {
client_id: credentials.clientId as string,
client_secret: credentials.clientSecret as string,
client_id: credentials.clientId,
client_secret: credentials.clientSecret,
};
tokenRefreshOptions.body = body;
// Override authorization property so the credentails are not included in it
Expand All @@ -940,7 +969,15 @@ export async function requestOAuth2(
`OAuth2 token for "${credentialsType}" used by node "${node.name}" expired. Should revalidate.`,
);

const newToken = await token.refresh(tokenRefreshOptions);
let newToken;

// 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.`,
Expand All @@ -960,7 +997,7 @@ export async function requestOAuth2(
await additionalData.credentialsHelper.updateCredentials(
nodeCredentials,
credentialsType,
credentials,
credentials as unknown as ICredentialDataDecryptedObject,
);

Logger.debug(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,17 @@ export default mixins(showMessage, nodeHelpers).extend({
},
isOAuthType(): boolean {
return !!this.credentialTypeName && (
['oAuth1Api', 'oAuth2Api'].includes(this.credentialTypeName) ||
this.parentTypes.includes('oAuth1Api') ||
this.parentTypes.includes('oAuth2Api')
(
(
this.credentialTypeName === 'oAuth2Api' ||
this.parentTypes.includes('oAuth2Api')
) && this.credentialData.grantType === 'authorizationCode'
)
||
(
this.credentialTypeName === 'oAuth1Api' ||
this.parentTypes.includes('oAuth1Api')
)
);
},
isOAuthConnected(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class AcuitySchedulingOAuth2Api implements ICredentialType {
displayName = 'AcuityScheduling OAuth2 API';
documentationUrl = 'acuityScheduling';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
6 changes: 6 additions & 0 deletions packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class AsanaOAuth2Api implements ICredentialType {
displayName = 'Asana OAuth2 API';
documentationUrl = 'asana';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
6 changes: 6 additions & 0 deletions packages/nodes-base/credentials/BitlyOAuth2Api.credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class BitlyOAuth2Api implements ICredentialType {
'oAuth2Api',
];
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
6 changes: 6 additions & 0 deletions packages/nodes-base/credentials/BoxOAuth2Api.credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class BoxOAuth2Api implements ICredentialType {
displayName = 'Box OAuth2 API';
documentationUrl = 'box';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export class CiscoWebexOAuth2Api implements ICredentialType {
];
displayName = 'Cisco Webex OAuth2 API';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class ClickUpOAuth2Api implements ICredentialType {
displayName = 'ClickUp OAuth2 API';
documentationUrl = 'clickUp';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
6 changes: 6 additions & 0 deletions packages/nodes-base/credentials/DriftOAuth2Api.credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class DriftOAuth2Api implements ICredentialType {
displayName = 'Drift OAuth2 API';
documentationUrl = 'drift';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export class DropboxOAuth2Api implements ICredentialType {
displayName = 'Dropbox OAuth2 API';
documentationUrl = 'dropbox';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class EventbriteOAuth2Api implements ICredentialType {
displayName = 'Eventbrite OAuth2 API';
documentationUrl = 'eventbrite';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export class FormstackOAuth2Api implements ICredentialType {
displayName = 'Formstack OAuth2 API';
documentationUrl = 'formstackTrigger';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export class GetResponseOAuth2Api implements ICredentialType {
];
displayName = 'GetResponse OAuth2 API';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class GithubOAuth2Api implements ICredentialType {
displayName = 'GitHub OAuth2 API';
documentationUrl = 'github';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Github Server',
name: 'server',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class GitlabOAuth2Api implements ICredentialType {
displayName = 'GitLab OAuth2 API';
documentationUrl = 'gitlab';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Gitlab Server',
name: 'server',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class GoToWebinarOAuth2Api implements ICredentialType {
displayName = 'GoToWebinar OAuth2 API';
documentationUrl = 'goToWebinar';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export class GoogleOAuth2Api implements ICredentialType {
documentationUrl = 'google';
icon = 'file:Google.svg';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class HarvestOAuth2Api implements ICredentialType {
];
displayName = 'Harvest OAuth2 API';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class HelpScoutOAuth2Api implements ICredentialType {
displayName = 'HelpScout OAuth2 API';
documentationUrl = 'helpScout';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export class HubspotDeveloperApi implements ICredentialType {
'oAuth2Api',
];
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export class HubspotOAuth2Api implements ICredentialType {
displayName = 'HubSpot OAuth2 API';
documentationUrl = 'hubspot';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
6 changes: 6 additions & 0 deletions packages/nodes-base/credentials/KeapOAuth2Api.credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export class KeapOAuth2Api implements ICredentialType {
displayName = 'Keap OAuth2 API';
documentationUrl = 'keap';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export class LineNotifyOAuth2Api implements ICredentialType {
displayName = 'Line Notify OAuth2 API';
documentationUrl = 'line';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
Expand Down
Loading

0 comments on commit e29c597

Please sign in to comment.