From 74f1d6a086b78321ec4993777b723ea666d49aa0 Mon Sep 17 00:00:00 2001 From: Danny Roosevelt Date: Thu, 24 Apr 2025 11:41:36 -0700 Subject: [PATCH 1/2] Adding polish and temporary logging --- docs-v2/components/AccountConnectionDemo.jsx | 2 +- docs-v2/components/ConnectLinkDemo.jsx | 4 ++-- docs-v2/components/api.js | 4 ++++ docs-v2/next.config.mjs | 8 ++++++++ docs-v2/pages/api/demo-connect/accounts/[id].js | 14 +++++++++++++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs-v2/components/AccountConnectionDemo.jsx b/docs-v2/components/AccountConnectionDemo.jsx index 83efac386e519..4f81f1807cf6a 100644 --- a/docs-v2/components/AccountConnectionDemo.jsx +++ b/docs-v2/components/AccountConnectionDemo.jsx @@ -63,7 +63,7 @@ export default function AccountConnectionDemo() { )} {connectedAccount && ( -
+
Successfully connected your {appSlug} account!
{connectedAccount.loading diff --git a/docs-v2/components/ConnectLinkDemo.jsx b/docs-v2/components/ConnectLinkDemo.jsx index 079fef294a175..6580434904fe4 100644 --- a/docs-v2/components/ConnectLinkDemo.jsx +++ b/docs-v2/components/ConnectLinkDemo.jsx @@ -82,7 +82,7 @@ export default function ConnectLinkDemo() { rel="noopener noreferrer" className={`${styles.primaryButton} inline-flex items-center`} > - Open Connect Link + Open @@ -94,7 +94,7 @@ export default function ConnectLinkDemo() { }} className={styles.secondaryButton} > - Copy URL + Copy diff --git a/docs-v2/components/api.js b/docs-v2/components/api.js index 8b356e83b2dbc..979528ec89633 100644 --- a/docs-v2/components/api.js +++ b/docs-v2/components/api.js @@ -5,10 +5,14 @@ /** * Generate a request token based on the browser environment * Creates a token that matches what the API will generate + * + * Note: Server-side uses the origin's hostname for token generation + * to handle domain mapping in production environments */ export function generateRequestToken() { if (typeof window === "undefined") return ""; + // Use the same origin's hostname that the server will use when generating the token const baseString = `${navigator.userAgent}:${window.location.host}:connect-demo`; return btoa(baseString); } diff --git a/docs-v2/next.config.mjs b/docs-v2/next.config.mjs index 9685cf4206800..56450106482e0 100644 --- a/docs-v2/next.config.mjs +++ b/docs-v2/next.config.mjs @@ -554,10 +554,18 @@ export default withNextra({ source: "/api-demo-connect/token", destination: "/api/demo-connect/token", }, + { + source: "/api-demo-connect/token/", + destination: "/api/demo-connect/token", + }, { source: "/api-demo-connect/accounts/:id", destination: "/api/demo-connect/accounts/:id", }, + { + source: "/api-demo-connect/accounts/:id/", + destination: "/api/demo-connect/accounts/:id", + }, ]; }, env: { diff --git a/docs-v2/pages/api/demo-connect/accounts/[id].js b/docs-v2/pages/api/demo-connect/accounts/[id].js index deaa287985fdb..d22a3aef81607 100644 --- a/docs-v2/pages/api/demo-connect/accounts/[id].js +++ b/docs-v2/pages/api/demo-connect/accounts/[id].js @@ -3,7 +3,9 @@ * Retrieves information about connected accounts for the interactive demo */ import { createBackendClient } from "@pipedream/sdk/server"; -import { createApiHandler } from "../utils"; +import { + createApiHandler, generateRequestToken, +} from "../utils"; /** * Handler for account details retrieval @@ -17,6 +19,15 @@ async function accountHandler(req, res) { }); } + // Debug logging for troubleshooting token validation + console.log("Account API request:", { + id, + host: req.headers.host, + origin: req.headers.origin, + requestToken: req.headers["x-request-token"], + expectedToken: generateRequestToken(req), + }); + try { // Initialize the Pipedream SDK client const pd = createBackendClient({ @@ -34,6 +45,7 @@ async function accountHandler(req, res) { // Return the account details return res.status(200).json(accountDetails); } catch (err) { + console.error("Error fetching account details:", err.message); return res.status(500).json({ error: "Failed to fetch account details", }); From fd517ccf4a528b068f6f054a4a1e292a02a61c0f Mon Sep 17 00:00:00 2001 From: Danny Roosevelt Date: Thu, 24 Apr 2025 12:41:17 -0700 Subject: [PATCH 2/2] Patching GET requests --- docs-v2/next.config.mjs | 15 ++++++++++ docs-v2/pages/api/demo-connect/utils.js | 40 +++++++++++++++++++++---- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/docs-v2/next.config.mjs b/docs-v2/next.config.mjs index 56450106482e0..e66bd72a498f7 100644 --- a/docs-v2/next.config.mjs +++ b/docs-v2/next.config.mjs @@ -17,6 +17,21 @@ export default withNextra({ }, ], }, + // Add custom headers for better cross-origin support + async headers() { + return [ + { + // Apply to all routes serving the Connect demo pages + source: "/connect/:path*", + headers: [ + { + key: "Cross-Origin-Opener-Policy", + value: "same-origin-allow-popups", + }, + ], + }, + ]; + }, async redirects() { return [ { diff --git a/docs-v2/pages/api/demo-connect/utils.js b/docs-v2/pages/api/demo-connect/utils.js index 1477e61e539d9..3d224c4640dbf 100644 --- a/docs-v2/pages/api/demo-connect/utils.js +++ b/docs-v2/pages/api/demo-connect/utils.js @@ -57,21 +57,47 @@ export const ALLOWED_ORIGINS = getAllowedOrigins(); * Used to verify requests are coming from our frontend */ export function generateRequestToken(req) { - // Try to use x-forwarded-host or origin's hostname instead of host to handle domain mapping - // This handles the case where the request goes through a reverse proxy or domain mapping + // Try to determine the effective host that matches what the client would use let effectiveHost = req.headers["host"]; - // If there's an origin header, extract its hostname - // as it will match the client's window.location.host + // First try to use origin header (best match for client's window.location.host) if (req.headers.origin) { try { const originUrl = new URL(req.headers.origin); effectiveHost = originUrl.host; } catch (e) { - // Fall back to host header if origin parsing fails console.log("Error parsing origin:", e.message); } } + // If no origin, try referer (can also contain the original hostname) + else if (req.headers.referer) { + try { + const refererUrl = new URL(req.headers.referer); + effectiveHost = refererUrl.host; + } catch (e) { + console.log("Error parsing referer:", e.message); + } + } + // Fall back to x-forwarded-host which might be set by proxies + else if (req.headers["x-forwarded-host"]) { + effectiveHost = req.headers["x-forwarded-host"]; + } + + // For account endpoints specifically, try to extract the host from the requestToken + // This is a special case for the accounts endpoint where the origin header might be missing + if (req.url?.includes("/accounts/") && req.headers["x-request-token"]) { + try { + const decodedToken = Buffer.from(req.headers["x-request-token"], "base64").toString(); + const parts = decodedToken.split(":"); + // If the token has the expected format with 3 parts, use the host from the token + if (parts.length === 3) { + // User-agent:host:connect-demo + effectiveHost = parts[1]; + } + } catch (e) { + console.log("Error extracting host from token:", e.message); + } + } const baseString = `${req.headers["user-agent"]}:${effectiveHost}:connect-demo`; return Buffer.from(baseString).toString("base64"); @@ -90,6 +116,10 @@ export function setCorsHeaders(req, res, methods = "GET, POST, OPTIONS") { ); res.setHeader("Access-Control-Allow-Methods", methods); res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Request-Token"); + + // Set COOP header to allow popups to communicate with the parent window + // This is important for OAuth flows in the Connect integration + res.setHeader("Cross-Origin-Opener-Policy", "same-origin-allow-popups"); } /**