Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/providers/providers.scopes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions packages/providers/providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Comment thread
NathanHu725 marked this conversation as resolved.
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
Expand Down
9 changes: 6 additions & 3 deletions packages/shared/lib/services/connection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1202,17 +1202,20 @@ class ConnectionService {
client_certificate?: string | undefined;
client_private_key?: string | undefined;
}): Promise<ServiceResponse<OAuth2ClientCredentials>> {
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<string, any> = {};
if (provider.token_params) {
interpolatedParams = interpolateObjectValues(provider.token_params, connectionConfig);
}
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)}`;
}

Expand Down
5 changes: 4 additions & 1 deletion scripts/validation/providers/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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),
Expand Down