From b0e555c8b128d82025b7873bcf0f4921ad6baa32 Mon Sep 17 00:00:00 2001 From: Jason Endo Date: Wed, 25 Jun 2025 13:52:28 -1000 Subject: [PATCH 1/5] add scope as an optional parameter when creating the backend client --- packages/sdk/src/server/index.ts | 23 ++++++++++++++++++++++- pnpm-lock.yaml | 30 ++++++++++-------------------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/sdk/src/server/index.ts b/packages/sdk/src/server/index.ts index ff426614bd5a5..f9c22a1c7a513 100644 --- a/packages/sdk/src/server/index.ts +++ b/packages/sdk/src/server/index.ts @@ -56,6 +56,11 @@ export type BackendClientOpts = { * https://pipedream.com/docs/workflows/domains */ workflowDomain?: string; + + /** + * OAuth scope to request when obtaining access tokens + */ + scope?: string[]; }; /** @@ -197,6 +202,7 @@ export class BackendClient extends BaseClient { }; protected override projectId: string = ""; private staticAccessToken?: string; + private scope?: string[]; /** * Constructs a new ServerClient instance. @@ -209,6 +215,7 @@ export class BackendClient extends BaseClient { this.ensureValidEnvironment(opts.environment); this.projectId = opts.projectId; + this.scope = opts.scope; if ("accessToken" in opts.credentials) { this.staticAccessToken = opts.credentials.accessToken; } else { @@ -264,6 +271,16 @@ export class BackendClient extends BaseClient { return this.ensureValidOauthAccessToken(); } + /**s + * Returns true if the client is configured to use a static access token. + * + * @returns True if the client is configured to use a static access token. + * + */ + public isStaticAccessToken(): boolean { + return !!this.staticAccessToken; + } + protected authHeaders(): string | Promise { if (this.staticAccessToken) { return `Bearer ${this.staticAccessToken}`; @@ -294,6 +311,9 @@ export class BackendClient extends BaseClient { } const parameters = new URLSearchParams(); + if (this.scope && this.scope.length > 0) { + parameters.set("scope", this.scope.join(" ")); + } try { const response = await oauth.clientCredentialsGrantRequest(as, client, clientAuth, parameters); const oauthTokenResponse = await oauth.processClientCredentialsResponse(as, client, response); @@ -301,7 +321,8 @@ export class BackendClient extends BaseClient { token: oauthTokenResponse.access_token, expiresAt: Date.now() + (oauthTokenResponse.expires_in || 0) * 1000, }; - } catch { + } catch (e) { + console.log(e) // pass } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d22e25ad1c67..3aa2afc26a369 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2355,8 +2355,7 @@ importers: components/chatsonic: {} - components/chattermill: - specifiers: {} + components/chattermill: {} components/chatwork: dependencies: @@ -5399,8 +5398,7 @@ importers: components/godial: {} - components/goformz: - specifiers: {} + components/goformz: {} components/gohighlevel: dependencies: @@ -7291,8 +7289,7 @@ importers: specifier: ^3.0.0 version: 3.0.3 - components/lark: - specifiers: {} + components/lark: {} components/lastpass: dependencies: @@ -8497,8 +8494,7 @@ importers: components/mollie: {} - components/momentum_ams: - specifiers: {} + components/momentum_ams: {} components/monday: dependencies: @@ -9412,8 +9408,7 @@ importers: specifier: ^0.9.0 version: 0.9.0 - components/opinion_stage: - specifiers: {} + components/opinion_stage: {} components/opnform: dependencies: @@ -9484,8 +9479,7 @@ importers: components/order_sender: {} - components/orderspace: - specifiers: {} + components/orderspace: {} components/originality_ai: dependencies: @@ -11562,8 +11556,7 @@ importers: components/ryver: {} - components/sage_accounting: - specifiers: {} + components/sage_accounting: {} components/sage_intacct: {} @@ -12980,8 +12973,7 @@ importers: components/stealthseminar: {} - components/stiply: - specifiers: {} + components/stiply: {} components/storeganise: dependencies: @@ -13570,8 +13562,7 @@ importers: specifier: ^1.11.10 version: 1.11.13 - components/themarketer: - specifiers: {} + components/themarketer: {} components/thesportsdb: {} @@ -14464,8 +14455,7 @@ importers: specifier: ^0.8.1 version: 0.8.1 - components/vincario: - specifiers: {} + components/vincario: {} components/viral_loops: dependencies: From 7c978a884fab84ee474cfb8510e1505f67369ed6 Mon Sep 17 00:00:00 2001 From: Jason Endo Date: Thu, 26 Jun 2025 08:52:20 -1000 Subject: [PATCH 2/5] better error surfacing when getting an access token. --- packages/sdk/src/server/index.ts | 37 ++++++++++++++++++++++++++++++-- packages/sdk/src/shared/index.ts | 8 +++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/server/index.ts b/packages/sdk/src/server/index.ts index f9c22a1c7a513..91f58d43f8748 100644 --- a/packages/sdk/src/server/index.ts +++ b/packages/sdk/src/server/index.ts @@ -322,8 +322,41 @@ export class BackendClient extends BaseClient { expiresAt: Date.now() + (oauthTokenResponse.expires_in || 0) * 1000, }; } catch (e) { - console.log(e) - // pass + // Extract error details from OAuth response + let errorMessage = "OAuth token request failed"; + let wwwAuthenticate: string | undefined; + let statusCode: number | undefined; + + if (e instanceof Error) { + errorMessage = e.message; + } + + // Check if the error contains response information + if (e && typeof e === 'object' && 'response' in e) { + const errorResponse = (e as any).response; + if (errorResponse) { + statusCode = errorResponse.status; + wwwAuthenticate = errorResponse.headers?.get?.('www-authenticate') || + errorResponse.headers?.['www-authenticate']; + + // Create more specific error message based on status code + if (statusCode === 401) { + errorMessage = `OAuth authentication failed (401 Unauthorized)${wwwAuthenticate ? `: ${wwwAuthenticate}` : ''}`; + } else if (statusCode === 400) { + errorMessage = "OAuth request invalid (400 Bad Request) - check client credentials"; + } else if (statusCode) { + errorMessage = `OAuth request failed with status ${statusCode}`; + } + } + } + + // If this is the last attempt, throw a detailed error + if (attempts >= maxAttempts) { + const error = new Error(errorMessage); + (error as any).statusCode = statusCode; + (error as any).wwwAuthenticate = wwwAuthenticate; + throw error; + } } attempts++; diff --git a/packages/sdk/src/shared/index.ts b/packages/sdk/src/shared/index.ts index 0b4daa82d77f8..b2e934e578277 100644 --- a/packages/sdk/src/shared/index.ts +++ b/packages/sdk/src/shared/index.ts @@ -853,6 +853,14 @@ export type ErrorResponse = { * The error message returned by the API. */ error: string; + /** + * The www-authenticate header value, if present in the error response. + */ + wwwAuthenticate?: string; + /** + * Additional error headers that may be relevant for error handling. + */ + headers?: Record; }; /** From 745e3b49977934079173aa1090598f5892c6fd85 Mon Sep 17 00:00:00 2001 From: Jason Endo Date: Thu, 3 Jul 2025 10:09:48 -1000 Subject: [PATCH 3/5] add optional scope param to backendClient --- packages/sdk/CHANGELOG.md | 6 ++ packages/sdk/package.json | 2 +- packages/sdk/src/server/index.ts | 104 +++++++++++++++---------------- 3 files changed, 59 insertions(+), 53 deletions(-) diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 37dbdf294211b..31fbbb3a4d0be 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -2,6 +2,12 @@ # Changelog +## [1.7.0] - 2025-07-03 + +### Added + +- Added optional scope parameter to backendClient creation. + ## [1.6.9] - 2025-06-10 ### Added diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 0ace4a5b6e5b7..774c3089e24a4 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,7 +1,7 @@ { "name": "@pipedream/sdk", "type": "module", - "version": "1.6.9", + "version": "1.7.0", "description": "Pipedream SDK", "main": "./dist/server.js", "module": "./dist/server.js", diff --git a/packages/sdk/src/server/index.ts b/packages/sdk/src/server/index.ts index 91f58d43f8748..0db1eb0cd4b65 100644 --- a/packages/sdk/src/server/index.ts +++ b/packages/sdk/src/server/index.ts @@ -298,68 +298,68 @@ export class BackendClient extends BaseClient { as, } = this.oauthClient - let attempts = 0; const maxAttempts = 2; while (!this.oauthAccessToken || this.oauthAccessToken.expiresAt - Date.now() < 1000) { - if (attempts > maxAttempts) { - throw new Error("ran out of attempts trying to retrieve oauth access token"); - } - if (attempts > 0) { - // Wait for a short duration before retrying to avoid rapid retries - await new Promise((resolve) => setTimeout(resolve, 100)); - } + for (let attempts = 0; attempts <= maxAttempts; attempts++) { + if (attempts > 0) { + // Wait for a short duration before retrying to avoid rapid retries + await new Promise((resolve) => setTimeout(resolve, 100)); + } - const parameters = new URLSearchParams(); - if (this.scope && this.scope.length > 0) { - parameters.set("scope", this.scope.join(" ")); - } - try { - const response = await oauth.clientCredentialsGrantRequest(as, client, clientAuth, parameters); - const oauthTokenResponse = await oauth.processClientCredentialsResponse(as, client, response); - this.oauthAccessToken = { - token: oauthTokenResponse.access_token, - expiresAt: Date.now() + (oauthTokenResponse.expires_in || 0) * 1000, - }; - } catch (e) { - // Extract error details from OAuth response - let errorMessage = "OAuth token request failed"; - let wwwAuthenticate: string | undefined; - let statusCode: number | undefined; - - if (e instanceof Error) { - errorMessage = e.message; + const parameters = new URLSearchParams(); + if (this.scope && this.scope.length > 0) { + parameters.set("scope", this.scope.join(" ")); } + parameters.set("project_id", this.projectId); + parameters.set("environment", this.environment); - // Check if the error contains response information - if (e && typeof e === 'object' && 'response' in e) { - const errorResponse = (e as any).response; - if (errorResponse) { - statusCode = errorResponse.status; - wwwAuthenticate = errorResponse.headers?.get?.('www-authenticate') || - errorResponse.headers?.['www-authenticate']; - - // Create more specific error message based on status code - if (statusCode === 401) { - errorMessage = `OAuth authentication failed (401 Unauthorized)${wwwAuthenticate ? `: ${wwwAuthenticate}` : ''}`; - } else if (statusCode === 400) { - errorMessage = "OAuth request invalid (400 Bad Request) - check client credentials"; - } else if (statusCode) { - errorMessage = `OAuth request failed with status ${statusCode}`; + try { + const response = await oauth.clientCredentialsGrantRequest(as, client, clientAuth, parameters); + const oauthTokenResponse = await oauth.processClientCredentialsResponse(as, client, response); + this.oauthAccessToken = { + token: oauthTokenResponse.access_token, + expiresAt: Date.now() + (oauthTokenResponse.expires_in || 0) * 1000, + }; + break; // Successfully got token, exit retry loop + } catch (e) { + // Extract error details from OAuth response + let errorMessage = "OAuth token request failed"; + let wwwAuthenticate: string | undefined; + let statusCode: number | undefined; + + if (e instanceof Error) { + errorMessage = e.message; + } + + // Check if the error contains response information + if (e && typeof e === 'object' && 'response' in e) { + const errorResponse = (e as any).response; + if (errorResponse) { + statusCode = errorResponse.status; + wwwAuthenticate = errorResponse.headers?.get?.('www-authenticate') || + errorResponse.headers?.['www-authenticate']; + + // Create more specific error message based on status code + if (statusCode === 401) { + errorMessage = `OAuth authentication failed (401 Unauthorized)${wwwAuthenticate ? `: ${wwwAuthenticate}` : ''}`; + } else if (statusCode === 400) { + errorMessage = "OAuth request invalid (400 Bad Request) - check client credentials"; + } else if (statusCode) { + errorMessage = `OAuth request failed with status ${statusCode}`; + } } } - } - - // If this is the last attempt, throw a detailed error - if (attempts >= maxAttempts) { - const error = new Error(errorMessage); - (error as any).statusCode = statusCode; - (error as any).wwwAuthenticate = wwwAuthenticate; - throw error; + + // If this is the last attempt, throw a detailed error + if (attempts >= maxAttempts) { + const error = new Error(errorMessage); + (error as any).statusCode = statusCode; + (error as any).wwwAuthenticate = wwwAuthenticate; + throw error; + } } } - - attempts++; } return this.oauthAccessToken.token; From 90f4f8597498acd95b084bc83bc40e18e25ea6f7 Mon Sep 17 00:00:00 2001 From: Jason Endo Date: Thu, 3 Jul 2025 11:37:34 -1000 Subject: [PATCH 4/5] coderabbit --- packages/sdk/src/server/index.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/sdk/src/server/index.ts b/packages/sdk/src/server/index.ts index 0db1eb0cd4b65..35761508f7e1d 100644 --- a/packages/sdk/src/server/index.ts +++ b/packages/sdk/src/server/index.ts @@ -271,7 +271,7 @@ export class BackendClient extends BaseClient { return this.ensureValidOauthAccessToken(); } - /**s + /** * Returns true if the client is configured to use a static access token. * * @returns True if the client is configured to use a static access token. @@ -331,18 +331,17 @@ export class BackendClient extends BaseClient { if (e instanceof Error) { errorMessage = e.message; } - // Check if the error contains response information - if (e && typeof e === 'object' && 'response' in e) { + if (e && typeof e === "object" && "response" in e) { const errorResponse = (e as any).response; if (errorResponse) { statusCode = errorResponse.status; - wwwAuthenticate = errorResponse.headers?.get?.('www-authenticate') || - errorResponse.headers?.['www-authenticate']; + wwwAuthenticate = errorResponse.headers?.get?.("www-authenticate") || + errorResponse.headers?.["www-authenticate"]; // Create more specific error message based on status code if (statusCode === 401) { - errorMessage = `OAuth authentication failed (401 Unauthorized)${wwwAuthenticate ? `: ${wwwAuthenticate}` : ''}`; + errorMessage = `OAuth authentication failed (401 Unauthorized)${wwwAuthenticate ? `: ${wwwAuthenticate}` : ""}`; } else if (statusCode === 400) { errorMessage = "OAuth request invalid (400 Bad Request) - check client credentials"; } else if (statusCode) { @@ -351,11 +350,12 @@ export class BackendClient extends BaseClient { } } - // If this is the last attempt, throw a detailed error + const error = new Error(errorMessage); + (error as any).statusCode = statusCode; + (error as any).wwwAuthenticate = wwwAuthenticate; + + // If this is the last attempt, throw the error if (attempts >= maxAttempts) { - const error = new Error(errorMessage); - (error as any).statusCode = statusCode; - (error as any).wwwAuthenticate = wwwAuthenticate; throw error; } } From dcb774a8f09dfc9601e74238cd53c481d8ebb6e9 Mon Sep 17 00:00:00 2001 From: Jason Endo Date: Thu, 3 Jul 2025 11:48:56 -1000 Subject: [PATCH 5/5] more coderabbit --- packages/sdk/src/server/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/sdk/src/server/index.ts b/packages/sdk/src/server/index.ts index 35761508f7e1d..c66abfa18b5c1 100644 --- a/packages/sdk/src/server/index.ts +++ b/packages/sdk/src/server/index.ts @@ -313,7 +313,6 @@ export class BackendClient extends BaseClient { } parameters.set("project_id", this.projectId); parameters.set("environment", this.environment); - try { const response = await oauth.clientCredentialsGrantRequest(as, client, clientAuth, parameters); const oauthTokenResponse = await oauth.processClientCredentialsResponse(as, client, response); @@ -327,7 +326,6 @@ export class BackendClient extends BaseClient { let errorMessage = "OAuth token request failed"; let wwwAuthenticate: string | undefined; let statusCode: number | undefined; - if (e instanceof Error) { errorMessage = e.message; } @@ -338,7 +336,6 @@ export class BackendClient extends BaseClient { statusCode = errorResponse.status; wwwAuthenticate = errorResponse.headers?.get?.("www-authenticate") || errorResponse.headers?.["www-authenticate"]; - // Create more specific error message based on status code if (statusCode === 401) { errorMessage = `OAuth authentication failed (401 Unauthorized)${wwwAuthenticate ? `: ${wwwAuthenticate}` : ""}`; @@ -349,11 +346,9 @@ export class BackendClient extends BaseClient { } } } - const error = new Error(errorMessage); (error as any).statusCode = statusCode; (error as any).wwwAuthenticate = wwwAuthenticate; - // If this is the last attempt, throw the error if (attempts >= maxAttempts) { throw error;