diff --git a/packages/providers/providers.scopes.yaml b/packages/providers/providers.scopes.yaml index 4b733c9d53..80d5b61fd4 100644 --- a/packages/providers/providers.scopes.yaml +++ b/packages/providers/providers.scopes.yaml @@ -2722,6 +2722,13 @@ klaviyo-oauth: - web-feeds:read - web-feeds:write +# source: https://developers.knocommerce.com/ (read scopes for survey responses, questions, and webhook subscriptions) +kno-commerce: + - SURVEYS + - RESPONSES + - QUESTIONS + - WEBHOOKS + # source: https://hire.lever.co/developer/documentation lever: - applications:read:admin diff --git a/packages/providers/providers.yaml b/packages/providers/providers.yaml index 4fb197ff05..08d7a9d11d 100644 --- a/packages/providers/providers.yaml +++ b/packages/providers/providers.yaml @@ -10069,10 +10069,9 @@ kno-commerce: categories: - e-commerce auth_mode: OAUTH2_CC - token_url: https://app-api.knocommerce.com/api/oauth2/token + token_url: https://app-api.knocommerce.com/api/oauth2/token?grant_type=client_credentials&scope=${connectionConfig.oauth_scopes} token_request_auth_method: basic - token_params: - grant_type: client_credentials + scope_separator: ' ' proxy: base_url: https://app-api.knocommerce.com docs: https://nango.dev/docs/api-integrations/kno-commerce diff --git a/packages/shared/lib/services/connection.service.ts b/packages/shared/lib/services/connection.service.ts index 12df759292..de17794345 100644 --- a/packages/shared/lib/services/connection.service.ts +++ b/packages/shared/lib/services/connection.service.ts @@ -1202,8 +1202,12 @@ class ConnectionService { client_certificate?: string | undefined; client_private_key?: string | undefined; }): Promise> { + const scope = + connectionConfig['oauth_scopes'] && typeof connectionConfig['oauth_scopes'] === 'string' + ? connectionConfig['oauth_scopes'].split(',').join(provider.scope_separator || ' ') + : ''; const strippedTokenUrl = typeof provider.token_url === 'string' ? provider.token_url.replace(/connectionConfig\./g, '') : ''; - const url = new URL(interpolateString(strippedTokenUrl, connectionConfig)); + const url = new URL(interpolateString(strippedTokenUrl, { ...connectionConfig, oauth_scopes: scope })); let interpolatedParams: Record = {}; if (provider.token_params) { @@ -1211,8 +1215,7 @@ class ConnectionService { } let tokenParams = interpolatedParams && Object.keys(interpolatedParams).length > 0 ? new URLSearchParams(interpolatedParams).toString() : ''; - if (connectionConfig['oauth_scopes'] && typeof connectionConfig['oauth_scopes'] === 'string') { - const scope = connectionConfig['oauth_scopes'].split(',').join(provider.scope_separator || ' '); + if (scope) { tokenParams += (tokenParams ? '&' : '') + `scope=${encodeURIComponent(scope)}`; } diff --git a/scripts/validation/providers/validate.ts b/scripts/validation/providers/validate.ts index acd9dbb513..1cec84c349 100644 --- a/scripts/validation/providers/validate.ts +++ b/scripts/validation/providers/validate.ts @@ -150,6 +150,8 @@ function validateProvider(providerKey: string, provider: ExtendedProvider) { } } + const RUNTIME_DYNAMIC_KEYS = new Set(['oauth_scopes']); + // Find all connectionConfig references const connectionConfigReferences = findConnectionConfigReferences(provider); @@ -158,8 +160,9 @@ function validateProvider(providerKey: string, provider: ExtendedProvider) { for (const reference of connectionConfigReferences) { const defined = provider.connection_config && reference.key in provider.connection_config; const inTokenResponseMetadata = provider.token_response_metadata?.includes(reference.key); + const isRuntimeDynamic = RUNTIME_DYNAMIC_KEYS.has(reference.key); - if (!defined && !inTokenResponseMetadata) { + if (!defined && !inTokenResponseMetadata && !isRuntimeDynamic) { console.error( chalk.red('error'), chalk.blue(providerKey),