@hubspot/app-connect-sdk 1.0.0-alpha.2 → 1.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-tsdown.log +41 -510
- package/build/tsconfig.browser.tsbuildinfo +1 -1
- package/build/tsconfig.server.tsbuildinfo +1 -1
- package/dist/browser/{HubSpotAppConnect-BW45gyDs.js → HubSpotAppConnect-COQgPrFn.js} +5 -3
- package/dist/browser/HubSpotAppConnect-COQgPrFn.js.map +1 -0
- package/dist/browser/{create-vctOhpX9.js → create-hzqjIhmO.js} +54 -25
- package/dist/browser/create-hzqjIhmO.js.map +1 -0
- package/dist/browser/index.js +1 -1
- package/dist/browser/react/lovable.js +2 -2
- package/dist/browser/react.js +1 -1
- package/dist/server/api-client.d.ts +60625 -197
- package/dist/server/api-client.js +5826 -100
- package/dist/server/api-client.js.map +1 -0
- package/dist/server/{api-client-core/client.js → binary-data-BOalJzKu.js} +58 -3
- package/dist/server/binary-data-BOalJzKu.js.map +1 -0
- package/dist/server/lovable.d.ts +117 -6
- package/dist/server/lovable.js +1458 -3
- package/dist/server/lovable.js.map +1 -0
- package/dist/server/oauth.d.ts +128 -6
- package/dist/server/oauth.js +1 -4
- package/dist/server/sha256-B7y8GBFB.js +228 -0
- package/dist/server/sha256-B7y8GBFB.js.map +1 -0
- package/dist/server/{types.d.ts → types-5gfN91Fq.d.ts} +2 -2
- package/dist/server/{api-client-core/types.d.ts → types-DEOUH4wE.d.ts} +2 -2
- package/package.json +4 -10
- package/src/browser/app-connect-controller/connect-start.ts +2 -1
- package/src/browser/app-connect-controller/init.test.ts +167 -0
- package/src/browser/app-connect-controller/init.ts +70 -19
- package/src/browser/react/components/AppConnectHeader/AppConnectHeader.tsx +3 -5
- package/src/browser/react/components/ConnectButton/ConnectButton.tsx +2 -1
- package/src/server/api-client-core/plugins/fetch-transport.ts +5 -1
- package/src/server/constants.ts +29 -4
- package/src/server/hono/hono-request-handler.ts +42 -15
- package/src/server/hono/hubspot-connect-routes/auth-complete.test.ts +285 -0
- package/src/server/hono/hubspot-connect-routes/{auth-callback.ts → auth-complete.ts} +73 -30
- package/src/server/hono/hubspot-connect-routes/auth-init-session.test.ts +114 -30
- package/src/server/hono/hubspot-connect-routes/auth-init-session.ts +33 -10
- package/src/server/hono/hubspot-connect-routes/auth-logout.test.ts +13 -0
- package/src/server/hono/hubspot-connect-routes/auth-logout.ts +18 -0
- package/src/server/hono/hubspot-connect-routes/auth-refresh.test.ts +6 -0
- package/src/server/hono/hubspot-connect-routes/auth-refresh.ts +6 -0
- package/src/server/hono/hubspot-connect-routes/hubspot-connect-routes.ts +9 -2
- package/src/server/hono/hubspot-connect-routes/utils.ts +57 -1
- package/src/server/hono/types.ts +15 -9
- package/src/server/hono/utils/cookie-utils.ts +27 -2
- package/src/server/hono/utils/cors-middleware.test.ts +79 -0
- package/src/server/hono/utils/cors-middleware.ts +95 -0
- package/src/server/sanitize-request.ts +25 -11
- package/src/server/types.ts +2 -2
- package/src/shared/constants.ts +31 -3
- package/src/shared/wire-types.ts +19 -0
- package/tsdown.config.ts +1 -1
- package/.turbo/turbo-format$colon$check.log +0 -4
- package/.turbo/turbo-lint.log +0 -2
- package/.turbo/turbo-test.log +0 -76
- package/dist/browser/HubSpotAppConnect-BW45gyDs.js.map +0 -1
- package/dist/browser/create-vctOhpX9.js.map +0 -1
- package/dist/server/api-client-core/apis/account/account-info-types.generated.d.ts +0 -111
- package/dist/server/api-client-core/apis/account/account-info.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/account/account-info.generated.js +0 -9
- package/dist/server/api-client-core/apis/account/account-info.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/account/audit-logs-types.generated.d.ts +0 -247
- package/dist/server/api-client-core/apis/account/audit-logs.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/account/audit-logs.generated.js +0 -28
- package/dist/server/api-client-core/apis/account/audit-logs.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/auth/oauth-types.generated.d.ts +0 -121
- package/dist/server/api-client-core/apis/auth/oauth.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/auth/oauth.generated.js +0 -19
- package/dist/server/api-client-core/apis/auth/oauth.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/automation/actions-types.generated.d.ts +0 -933
- package/dist/server/api-client-core/apis/automation/actions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/automation/actions.generated.js +0 -121
- package/dist/server/api-client-core/apis/automation/actions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/automation/sequences-types.generated.d.ts +0 -422
- package/dist/server/api-client-core/apis/automation/sequences.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/automation/sequences.generated.js +0 -22
- package/dist/server/api-client-core/apis/automation/sequences.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/business-units-types.generated.d.ts +0 -75
- package/dist/server/api-client-core/apis/business-units.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/business-units.generated.js +0 -12
- package/dist/server/api-client-core/apis/business-units.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/authors-types.generated.d.ts +0 -551
- package/dist/server/api-client-core/apis/cms/authors.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/authors.generated.js +0 -163
- package/dist/server/api-client-core/apis/cms/authors.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/blog-settings-types.generated.d.ts +0 -366
- package/dist/server/api-client-core/apis/cms/blog-settings.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/blog-settings.generated.js +0 -43
- package/dist/server/api-client-core/apis/cms/blog-settings.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/cms-content-audit-types.generated.d.ts +0 -157
- package/dist/server/api-client-core/apis/cms/cms-content-audit.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/cms-content-audit.generated.js +0 -18
- package/dist/server/api-client-core/apis/cms/cms-content-audit.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/domains-types.generated.d.ts +0 -193
- package/dist/server/api-client-core/apis/cms/domains.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/domains.generated.js +0 -20
- package/dist/server/api-client-core/apis/cms/domains.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/hubdb-types.generated.d.ts +0 -1097
- package/dist/server/api-client-core/apis/cms/hubdb.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/hubdb.generated.js +0 -192
- package/dist/server/api-client-core/apis/cms/hubdb.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/media-bridge-types.generated.d.ts +0 -1780
- package/dist/server/api-client-core/apis/cms/media-bridge.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/media-bridge.generated.js +0 -185
- package/dist/server/api-client-core/apis/cms/media-bridge.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/pages-types.generated.d.ts +0 -1768
- package/dist/server/api-client-core/apis/cms/pages.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/pages.generated.js +0 -331
- package/dist/server/api-client-core/apis/cms/pages.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/posts-types.generated.d.ts +0 -1090
- package/dist/server/api-client-core/apis/cms/posts.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/posts.generated.js +0 -201
- package/dist/server/api-client-core/apis/cms/posts.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/site-search-types.generated.d.ts +0 -200
- package/dist/server/api-client-core/apis/cms/site-search.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/site-search.generated.js +0 -32
- package/dist/server/api-client-core/apis/cms/site-search.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/source-code-types.generated.d.ts +0 -218
- package/dist/server/api-client-core/apis/cms/source-code.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/source-code.generated.js +0 -52
- package/dist/server/api-client-core/apis/cms/source-code.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/tags-types.generated.d.ts +0 -515
- package/dist/server/api-client-core/apis/cms/tags.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/tags.generated.js +0 -163
- package/dist/server/api-client-core/apis/cms/tags.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/url-mappings-types.generated.d.ts +0 -177
- package/dist/server/api-client-core/apis/cms/url-mappings.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/url-mappings.generated.js +0 -14
- package/dist/server/api-client-core/apis/cms/url-mappings.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/cms/url-redirects-types.generated.d.ts +0 -226
- package/dist/server/api-client-core/apis/cms/url-redirects.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/cms/url-redirects.generated.js +0 -26
- package/dist/server/api-client-core/apis/cms/url-redirects.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/communication-preferences/subscriptions-types.generated.d.ts +0 -802
- package/dist/server/api-client-core/apis/communication-preferences/subscriptions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/communication-preferences/subscriptions.generated.js +0 -74
- package/dist/server/api-client-core/apis/communication-preferences/subscriptions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/conversations/custom-channels-types.generated.d.ts +0 -551
- package/dist/server/api-client-core/apis/conversations/custom-channels.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/conversations/custom-channels.generated.js +0 -80
- package/dist/server/api-client-core/apis/conversations/custom-channels.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/conversations/visitor-identification-types.generated.d.ts +0 -60
- package/dist/server/api-client-core/apis/conversations/visitor-identification.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/conversations/visitor-identification.generated.js +0 -6
- package/dist/server/api-client-core/apis/conversations/visitor-identification.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/conversations-types.generated.d.ts +0 -908
- package/dist/server/api-client-core/apis/conversations.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/conversations.generated.js +0 -108
- package/dist/server/api-client-core/apis/conversations.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/app-uninstalls-types.generated.d.ts +0 -37
- package/dist/server/api-client-core/apis/crm/app-uninstalls.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/app-uninstalls.generated.js +0 -6
- package/dist/server/api-client-core/apis/crm/app-uninstalls.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/appointments-types.generated.d.ts +0 -989
- package/dist/server/api-client-core/apis/crm/appointments.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/appointments.generated.js +0 -118
- package/dist/server/api-client-core/apis/crm/appointments.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/associations-schema-types.generated.d.ts +0 -329
- package/dist/server/api-client-core/apis/crm/associations-schema.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/associations-schema.generated.js +0 -60
- package/dist/server/api-client-core/apis/crm/associations-schema.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/associations-types.generated.d.ts +0 -661
- package/dist/server/api-client-core/apis/crm/associations.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/associations.generated.js +0 -83
- package/dist/server/api-client-core/apis/crm/associations.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/calling-extensions-types.generated.d.ts +0 -466
- package/dist/server/api-client-core/apis/crm/calling-extensions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/calling-extensions.generated.js +0 -42
- package/dist/server/api-client-core/apis/crm/calling-extensions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/calls-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/calls.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/calls.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/calls.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/carts-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/carts.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/carts.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/carts.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/commerce-payments-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/commerce-payments.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/commerce-payments.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/commerce-payments.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/commerce-subscriptions-types.generated.d.ts +0 -847
- package/dist/server/api-client-core/apis/crm/commerce-subscriptions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/commerce-subscriptions.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/commerce-subscriptions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/communications-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/communications.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/communications.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/communications.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/companies-types.generated.d.ts +0 -884
- package/dist/server/api-client-core/apis/crm/companies.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/companies.generated.js +0 -67
- package/dist/server/api-client-core/apis/crm/companies.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/contacts-types.generated.d.ts +0 -899
- package/dist/server/api-client-core/apis/crm/contacts.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/contacts.generated.js +0 -70
- package/dist/server/api-client-core/apis/crm/contacts.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/contracts-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/contracts.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/contracts.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/contracts.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/courses-types.generated.d.ts +0 -853
- package/dist/server/api-client-core/apis/crm/courses.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/courses.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/courses.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/crm-owners-types.generated.d.ts +0 -140
- package/dist/server/api-client-core/apis/crm/crm-owners.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/crm-owners.generated.js +0 -20
- package/dist/server/api-client-core/apis/crm/crm-owners.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/custom-objects-types.generated.d.ts +0 -934
- package/dist/server/api-client-core/apis/crm/custom-objects.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/custom-objects.generated.js +0 -101
- package/dist/server/api-client-core/apis/crm/custom-objects.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/deal-splits-types.generated.d.ts +0 -196
- package/dist/server/api-client-core/apis/crm/deal-splits.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/deal-splits.generated.js +0 -9
- package/dist/server/api-client-core/apis/crm/deal-splits.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/deals-types.generated.d.ts +0 -872
- package/dist/server/api-client-core/apis/crm/deals.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/deals.generated.js +0 -67
- package/dist/server/api-client-core/apis/crm/deals.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/discounts-types.generated.d.ts +0 -846
- package/dist/server/api-client-core/apis/crm/discounts.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/discounts.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/discounts.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/emails-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/emails.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/emails.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/emails.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/exports-types.generated.d.ts +0 -281
- package/dist/server/api-client-core/apis/crm/exports.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/exports.generated.js +0 -12
- package/dist/server/api-client-core/apis/crm/exports.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/feedback-submissions-types.generated.d.ts +0 -616
- package/dist/server/api-client-core/apis/crm/feedback-submissions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/feedback-submissions.generated.js +0 -55
- package/dist/server/api-client-core/apis/crm/feedback-submissions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/fees-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/fees.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/fees.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/fees.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/goal-targets-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/goal-targets.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/goal-targets.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/goal-targets.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/imports-types.generated.d.ts +0 -371
- package/dist/server/api-client-core/apis/crm/imports.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/imports.generated.js +0 -30
- package/dist/server/api-client-core/apis/crm/imports.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/invoices-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/invoices.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/invoices.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/invoices.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/leads-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/leads.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/leads.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/leads.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/limits-tracking-types.generated.d.ts +0 -331
- package/dist/server/api-client-core/apis/crm/limits-tracking.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/limits-tracking.generated.js +0 -22
- package/dist/server/api-client-core/apis/crm/limits-tracking.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/line-items-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/line-items.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/line-items.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/line-items.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/listings-types.generated.d.ts +0 -853
- package/dist/server/api-client-core/apis/crm/listings.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/listings.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/listings.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/lists-types.generated.d.ts +0 -2265
- package/dist/server/api-client-core/apis/crm/lists.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/lists.generated.js +0 -105
- package/dist/server/api-client-core/apis/crm/lists.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/meetings-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/meetings.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/meetings.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/meetings.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/notes-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/notes.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/notes.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/notes.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/object-library-types.generated.d.ts +0 -60
- package/dist/server/api-client-core/apis/crm/object-library.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/object-library.generated.js +0 -9
- package/dist/server/api-client-core/apis/crm/object-library.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/objects-types.generated.d.ts +0 -712
- package/dist/server/api-client-core/apis/crm/objects.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/objects.generated.js +0 -76
- package/dist/server/api-client-core/apis/crm/objects.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/orders-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/orders.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/orders.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/orders.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/partner-clients-types.generated.d.ts +0 -725
- package/dist/server/api-client-core/apis/crm/partner-clients.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/partner-clients.generated.js +0 -71
- package/dist/server/api-client-core/apis/crm/partner-clients.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/partner-services-types.generated.d.ts +0 -725
- package/dist/server/api-client-core/apis/crm/partner-services.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/partner-services.generated.js +0 -71
- package/dist/server/api-client-core/apis/crm/partner-services.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/pipelines-types.generated.d.ts +0 -430
- package/dist/server/api-client-core/apis/crm/pipelines.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/pipelines.generated.js +0 -94
- package/dist/server/api-client-core/apis/crm/pipelines.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/postal-mail-types.generated.d.ts +0 -844
- package/dist/server/api-client-core/apis/crm/postal-mail.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/postal-mail.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/postal-mail.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/products-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/products.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/products.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/products.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/projects-types.generated.d.ts +0 -881
- package/dist/server/api-client-core/apis/crm/projects.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/projects.generated.js +0 -67
- package/dist/server/api-client-core/apis/crm/projects.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/properties-types.generated.d.ts +0 -603
- package/dist/server/api-client-core/apis/crm/properties.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/properties.generated.js +0 -86
- package/dist/server/api-client-core/apis/crm/properties.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/property-validations-types.generated.d.ts +0 -121
- package/dist/server/api-client-core/apis/crm/property-validations.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/property-validations.generated.js +0 -25
- package/dist/server/api-client-core/apis/crm/property-validations.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/public-app-crm-cards-types.generated.d.ts +0 -486
- package/dist/server/api-client-core/apis/crm/public-app-crm-cards.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/public-app-crm-cards.generated.js +0 -34
- package/dist/server/api-client-core/apis/crm/public-app-crm-cards.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/public-app-feature-flags-types.generated.d.ts +0 -247
- package/dist/server/api-client-core/apis/crm/public-app-feature-flags.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/public-app-feature-flags.generated.js +0 -69
- package/dist/server/api-client-core/apis/crm/public-app-feature-flags.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/quotes-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/quotes.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/quotes.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/quotes.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/schemas-types.generated.d.ts +0 -669
- package/dist/server/api-client-core/apis/crm/schemas.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/schemas.generated.js +0 -41
- package/dist/server/api-client-core/apis/crm/schemas.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/services-types.generated.d.ts +0 -853
- package/dist/server/api-client-core/apis/crm/services.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/services.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/services.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/tasks-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/tasks.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/tasks.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/tasks.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/taxes-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/taxes.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/taxes.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/taxes.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/tickets-types.generated.d.ts +0 -884
- package/dist/server/api-client-core/apis/crm/tickets.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/tickets.generated.js +0 -67
- package/dist/server/api-client-core/apis/crm/tickets.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/timeline-types.generated.d.ts +0 -187
- package/dist/server/api-client-core/apis/crm/timeline.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/timeline.generated.js +0 -12
- package/dist/server/api-client-core/apis/crm/timeline.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/transcriptions-types.generated.d.ts +0 -152
- package/dist/server/api-client-core/apis/crm/transcriptions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/transcriptions.generated.js +0 -15
- package/dist/server/api-client-core/apis/crm/transcriptions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/users-types.generated.d.ts +0 -850
- package/dist/server/api-client-core/apis/crm/users.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/users.generated.js +0 -66
- package/dist/server/api-client-core/apis/crm/users.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/crm/video-conferencing-extension-types.generated.d.ts +0 -72
- package/dist/server/api-client-core/apis/crm/video-conferencing-extension.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/crm/video-conferencing-extension.generated.js +0 -13
- package/dist/server/api-client-core/apis/crm/video-conferencing-extension.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/events/manage-event-definitions-types.generated.d.ts +0 -1005
- package/dist/server/api-client-core/apis/events/manage-event-definitions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/events/manage-event-definitions.generated.js +0 -39
- package/dist/server/api-client-core/apis/events/manage-event-definitions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/events/send-event-completions-types.generated.d.ts +0 -94
- package/dist/server/api-client-core/apis/events/send-event-completions.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/events/send-event-completions.generated.js +0 -9
- package/dist/server/api-client-core/apis/events/send-event-completions.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/events-types.generated.d.ts +0 -137
- package/dist/server/api-client-core/apis/events.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/events.generated.js +0 -23
- package/dist/server/api-client-core/apis/events.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/files-types.generated.d.ts +0 -791
- package/dist/server/api-client-core/apis/files.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/files.generated.js +0 -119
- package/dist/server/api-client-core/apis/files.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/marketing/campaigns-public-api-types.generated.d.ts +0 -989
- package/dist/server/api-client-core/apis/marketing/campaigns-public-api.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/marketing/campaigns-public-api.generated.js +0 -139
- package/dist/server/api-client-core/apis/marketing/campaigns-public-api.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/marketing/marketing-emails-types.generated.d.ts +0 -883
- package/dist/server/api-client-core/apis/marketing/marketing-emails.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/marketing/marketing-emails.generated.js +0 -108
- package/dist/server/api-client-core/apis/marketing/marketing-emails.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/marketing/marketing-events-types.generated.d.ts +0 -1788
- package/dist/server/api-client-core/apis/marketing/marketing-events.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/marketing/marketing-events.generated.js +0 -176
- package/dist/server/api-client-core/apis/marketing/marketing-events.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/marketing/single-send-types.generated.d.ts +0 -123
- package/dist/server/api-client-core/apis/marketing/single-send.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/marketing/single-send.generated.js +0 -6
- package/dist/server/api-client-core/apis/marketing/single-send.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/marketing/transactional-single-send-types.generated.d.ts +0 -257
- package/dist/server/api-client-core/apis/marketing/transactional-single-send.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/marketing/transactional-single-send.generated.js +0 -20
- package/dist/server/api-client-core/apis/marketing/transactional-single-send.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/meta/origins-types.generated.d.ts +0 -77
- package/dist/server/api-client-core/apis/meta/origins.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/meta/origins.generated.js +0 -15
- package/dist/server/api-client-core/apis/meta/origins.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/scheduler/meetings-types.generated.d.ts +0 -913
- package/dist/server/api-client-core/apis/scheduler/meetings.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/scheduler/meetings.generated.js +0 -34
- package/dist/server/api-client-core/apis/scheduler/meetings.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/settings/multicurrency-types.generated.d.ts +0 -404
- package/dist/server/api-client-core/apis/settings/multicurrency.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/settings/multicurrency.generated.js +0 -38
- package/dist/server/api-client-core/apis/settings/multicurrency.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/settings/tax-rates-types.generated.d.ts +0 -111
- package/dist/server/api-client-core/apis/settings/tax-rates.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/settings/tax-rates.generated.js +0 -13
- package/dist/server/api-client-core/apis/settings/tax-rates.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/settings/user-provisioning-types.generated.d.ts +0 -297
- package/dist/server/api-client-core/apis/settings/user-provisioning.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/settings/user-provisioning.generated.js +0 -31
- package/dist/server/api-client-core/apis/settings/user-provisioning.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/webhooks-journal-types.generated.d.ts +0 -643
- package/dist/server/api-client-core/apis/webhooks-journal.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/webhooks-journal.generated.js +0 -75
- package/dist/server/api-client-core/apis/webhooks-journal.generated.js.map +0 -1
- package/dist/server/api-client-core/apis/webhooks-types.generated.d.ts +0 -1016
- package/dist/server/api-client-core/apis/webhooks.generated.d.ts +0 -7
- package/dist/server/api-client-core/apis/webhooks.generated.js +0 -105
- package/dist/server/api-client-core/apis/webhooks.generated.js.map +0 -1
- package/dist/server/api-client-core/binary-data.d.ts +0 -33
- package/dist/server/api-client-core/binary-data.js +0 -29
- package/dist/server/api-client-core/binary-data.js.map +0 -1
- package/dist/server/api-client-core/client.d.ts +0 -14
- package/dist/server/api-client-core/client.js.map +0 -1
- package/dist/server/api-client-core/codegen-helpers/file-op-wrappers.js +0 -25
- package/dist/server/api-client-core/codegen-helpers/file-op-wrappers.js.map +0 -1
- package/dist/server/api-client-core/errors.d.ts +0 -27
- package/dist/server/api-client-core/errors.js +0 -33
- package/dist/server/api-client-core/errors.js.map +0 -1
- package/dist/server/api-client-core/op.d.ts +0 -37
- package/dist/server/api-client-core/op.js +0 -44
- package/dist/server/api-client-core/op.js.map +0 -1
- package/dist/server/api-client-core/pagination.d.ts +0 -60
- package/dist/server/api-client-core/pagination.js +0 -103
- package/dist/server/api-client-core/pagination.js.map +0 -1
- package/dist/server/api-client-core/plugins/fetch-transport.js +0 -72
- package/dist/server/api-client-core/plugins/fetch-transport.js.map +0 -1
- package/dist/server/constants.js +0 -46
- package/dist/server/constants.js.map +0 -1
- package/dist/server/deno/start.d.ts +0 -12
- package/dist/server/deno/start.js +0 -21
- package/dist/server/deno/start.js.map +0 -1
- package/dist/server/hono/hono-request-handler.js +0 -54
- package/dist/server/hono/hono-request-handler.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/auth-callback.js +0 -125
- package/dist/server/hono/hubspot-connect-routes/auth-callback.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/auth-init-session.js +0 -90
- package/dist/server/hono/hubspot-connect-routes/auth-init-session.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/auth-logout.js +0 -97
- package/dist/server/hono/hubspot-connect-routes/auth-logout.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/auth-refresh.js +0 -101
- package/dist/server/hono/hubspot-connect-routes/auth-refresh.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/cimd-client-metadata-types.d.ts +0 -16
- package/dist/server/hono/hubspot-connect-routes/cimd-client-metadata-types.js +0 -13
- package/dist/server/hono/hubspot-connect-routes/cimd-client-metadata-types.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/cimd-public-routes.js +0 -42
- package/dist/server/hono/hubspot-connect-routes/cimd-public-routes.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/constants.js +0 -8
- package/dist/server/hono/hubspot-connect-routes/constants.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/fetch-hubspot-client-metadata.js +0 -43
- package/dist/server/hono/hubspot-connect-routes/fetch-hubspot-client-metadata.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/hubspot-connect-routes.js +0 -35
- package/dist/server/hono/hubspot-connect-routes/hubspot-connect-routes.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/load-hubspot-connect-routes-env.js +0 -34
- package/dist/server/hono/hubspot-connect-routes/load-hubspot-connect-routes-env.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/oauth-client.js +0 -104
- package/dist/server/hono/hubspot-connect-routes/oauth-client.js.map +0 -1
- package/dist/server/hono/hubspot-connect-routes/utils.js +0 -73
- package/dist/server/hono/hubspot-connect-routes/utils.js.map +0 -1
- package/dist/server/hono/index.js +0 -4
- package/dist/server/hono/types.d.ts +0 -28
- package/dist/server/hono/utils/cookie-utils.js +0 -29
- package/dist/server/hono/utils/cookie-utils.js.map +0 -1
- package/dist/server/import-app-keys.js +0 -42
- package/dist/server/import-app-keys.js.map +0 -1
- package/dist/server/lovable/create-app-function-start.d.ts +0 -26
- package/dist/server/lovable/create-app-function-start.js +0 -28
- package/dist/server/lovable/create-app-function-start.js.map +0 -1
- package/dist/server/lovable/hubspot-connect/index.d.ts +0 -15
- package/dist/server/lovable/hubspot-connect/index.js +0 -20
- package/dist/server/lovable/hubspot-connect/index.js.map +0 -1
- package/dist/server/lovable/hubspot-connect/run-hubspot-connect-lovable-server.js +0 -29
- package/dist/server/lovable/hubspot-connect/run-hubspot-connect-lovable-server.js.map +0 -1
- package/dist/server/proxy.js +0 -68
- package/dist/server/proxy.js.map +0 -1
- package/dist/server/sanitize-request.js +0 -41
- package/dist/server/sanitize-request.js.map +0 -1
- package/dist/server/secure-start-core.d.ts +0 -23
- package/dist/server/secure-start-core.js +0 -28
- package/dist/server/secure-start-core.js.map +0 -1
- package/dist/server/shared/constants.js +0 -17
- package/dist/server/shared/constants.js.map +0 -1
- package/dist/server/shared/encoding/base64.js +0 -45
- package/dist/server/shared/encoding/base64.js.map +0 -1
- package/dist/server/shared/encoding/sha256.d.ts +0 -10
- package/dist/server/shared/encoding/sha256.js +0 -15
- package/dist/server/shared/encoding/sha256.js.map +0 -1
- package/dist/server/shared/logger.d.ts +0 -15
- package/dist/server/shared/logger.js +0 -16
- package/dist/server/shared/logger.js.map +0 -1
- package/dist/server/utils/cookie-utils.js +0 -21
- package/dist/server/utils/cookie-utils.js.map +0 -1
- package/dist/server/utils/dpop-utils.d.ts +0 -67
- package/dist/server/utils/dpop-utils.js +0 -75
- package/dist/server/utils/dpop-utils.js.map +0 -1
- package/dist/server/utils/env-utils.js +0 -54
- package/dist/server/utils/env-utils.js.map +0 -1
- package/dist/server/utils/jwk-utils.d.ts +0 -16
- package/dist/server/utils/jwk-utils.js +0 -24
- package/dist/server/utils/jwk-utils.js.map +0 -1
- package/dist/server/utils/jwt-utils.d.ts +0 -39
- package/dist/server/utils/jwt-utils.js +0 -87
- package/dist/server/utils/jwt-utils.js.map +0 -1
- package/src/server/hono/hubspot-connect-routes/auth-callback.test.ts +0 -225
package/dist/server/lovable.js
CHANGED
|
@@ -1,4 +1,1459 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { n as isBinaryData, r as createHubSpotClient } from "./binary-data-BOalJzKu.js";
|
|
2
|
+
import { c as base64url, i as signJwt, l as base64urlDecode, n as signDpopProof, o as getJwkThumbprint, s as base64StandardToArrayBuffer, t as sha256base64url } from "./sha256-B7y8GBFB.js";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
//#region src/server/import-app-keys.ts
|
|
5
|
+
/**
|
|
6
|
+
* Imports a base64-encoded PKCS8 ES256 private key into the
|
|
7
|
+
* `AppKeys` shape used throughout the SDK.
|
|
8
|
+
*
|
|
9
|
+
* The function imports the key twice: once as **extractable** to
|
|
10
|
+
* derive the public JWK (via `crypto.subtle.exportKey`), and once
|
|
11
|
+
* as **non-extractable** so the long-lived `appPrivateKey` can never
|
|
12
|
+
* be exfiltrated.
|
|
13
|
+
*
|
|
14
|
+
* @throws {Error} When `envKey` is empty/undefined or when the key
|
|
15
|
+
* isn't an EC P-256 keypair.
|
|
16
|
+
*/
|
|
17
|
+
async function importAppKeys(envKey) {
|
|
18
|
+
const b64 = envKey?.trim() ?? "";
|
|
19
|
+
if (!b64) throw new Error("HUBSPOT_APP_PRIVATE_KEY is not set");
|
|
20
|
+
const pkcs8 = base64StandardToArrayBuffer(b64);
|
|
21
|
+
const tempPrivateKey = await crypto.subtle.importKey("pkcs8", pkcs8, {
|
|
22
|
+
name: "ECDSA",
|
|
23
|
+
namedCurve: "P-256"
|
|
24
|
+
}, true, ["sign"]);
|
|
25
|
+
const privateJwk = await crypto.subtle.exportKey("jwk", tempPrivateKey);
|
|
26
|
+
if (privateJwk.kty !== "EC" || privateJwk.crv !== "P-256" || typeof privateJwk.x !== "string" || typeof privateJwk.y !== "string") throw new Error("Expected P-256 EC private key JWK with x and y");
|
|
27
|
+
const appPublicKeyJwk = {
|
|
28
|
+
kty: "EC",
|
|
29
|
+
crv: "P-256",
|
|
30
|
+
x: privateJwk.x,
|
|
31
|
+
y: privateJwk.y
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
appPrivateKey: await crypto.subtle.importKey("pkcs8", pkcs8, {
|
|
35
|
+
name: "ECDSA",
|
|
36
|
+
namedCurve: "P-256"
|
|
37
|
+
}, false, ["sign"]),
|
|
38
|
+
appPublicKeyJwk
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/server/utils/env-utils.ts
|
|
43
|
+
/**
|
|
44
|
+
* Reads an environment variable in a way that works under both Node
|
|
45
|
+
* (`process.env`) and Deno (`Deno.env.get`). Returns `undefined` when
|
|
46
|
+
* the variable is unset or when neither runtime is available.
|
|
47
|
+
*/
|
|
48
|
+
function getEnv(key) {
|
|
49
|
+
const g = globalThis;
|
|
50
|
+
const proc = g.process;
|
|
51
|
+
if (proc?.env) return proc.env[key];
|
|
52
|
+
const deno = g.Deno;
|
|
53
|
+
if (deno !== void 0) return deno.env.get(key);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reads an environment variable and throws when it is missing or empty.
|
|
57
|
+
* Use for values the SDK cannot fall back on (e.g. upstream service
|
|
58
|
+
* URLs).
|
|
59
|
+
*
|
|
60
|
+
* @throws {Error} When the environment variable is unset or an empty
|
|
61
|
+
* string.
|
|
62
|
+
*/
|
|
63
|
+
function requireEnv(key) {
|
|
64
|
+
const value = getEnv(key);
|
|
65
|
+
if (!value) throw new Error(`Missing required environment variable: ${key}`);
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Whether outbound HubSpot OAuth and API calls should attach DPoP on
|
|
70
|
+
* the wire. Disabled only when `HUBSPOT_DPOP_ENABLED` is exactly the
|
|
71
|
+
* string `"false"` (unset or any other value keeps DPoP enabled).
|
|
72
|
+
*/
|
|
73
|
+
function isHubspotDpopEnabled() {
|
|
74
|
+
return getEnv("HUBSPOT_DPOP_ENABLED") !== "false";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether the SDK should use CIMD-style OAuth (client ID URL + JWT client
|
|
78
|
+
* assertion). Disabled only when `HUBSPOT_CIMD_ENABLED` is exactly the
|
|
79
|
+
* string `"false"` (unset or any other value keeps CIMD enabled).
|
|
80
|
+
*/
|
|
81
|
+
function isHubspotCimdEnabled() {
|
|
82
|
+
return getEnv("HUBSPOT_CIMD_ENABLED") !== "false";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Whether `HUBSPOT_APP_PRIVATE_KEY` must be set for `secureStart`. False
|
|
86
|
+
* when both CIMD and DPoP are disabled — the SDK then uses
|
|
87
|
+
* `client_secret` for OAuth and Bearer tokens only for API calls.
|
|
88
|
+
*/
|
|
89
|
+
function isHubspotAppPrivateKeyRequired() {
|
|
90
|
+
return isHubspotCimdEnabled() || isHubspotDpopEnabled();
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/server/secure-start-core.ts
|
|
94
|
+
const APP_PRIVATE_KEY_ENV = "HUBSPOT_APP_PRIVATE_KEY";
|
|
95
|
+
/**
|
|
96
|
+
* Loads `HUBSPOT_APP_PRIVATE_KEY` when CIMD or DPoP is enabled, deletes
|
|
97
|
+
* it from the environment so later code cannot re-read it, imports the
|
|
98
|
+
* entrypoint module via `loader`, and invokes `start` with `AppKeys` or
|
|
99
|
+
* `null` when both features are off. On failure, logs the error and
|
|
100
|
+
* exits with code 1.
|
|
101
|
+
*/
|
|
102
|
+
async function runSecureStart(loader, adapter) {
|
|
103
|
+
let appKeys = null;
|
|
104
|
+
if (isHubspotAppPrivateKeyRequired()) {
|
|
105
|
+
const envKey = adapter.readEnv(APP_PRIVATE_KEY_ENV)?.trim();
|
|
106
|
+
if (!envKey) throw new Error(`${APP_PRIVATE_KEY_ENV} is not set`);
|
|
107
|
+
appKeys = await importAppKeys(envKey);
|
|
108
|
+
adapter.deleteEnv(APP_PRIVATE_KEY_ENV);
|
|
109
|
+
}
|
|
110
|
+
return (await loader()).start({ appKeys }).catch((error) => {
|
|
111
|
+
console.error(error);
|
|
112
|
+
return adapter.exit(1);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/server/deno/start.ts
|
|
117
|
+
const denoSecureStartAdapter = {
|
|
118
|
+
readEnv: (key) => Deno.env.get(key),
|
|
119
|
+
deleteEnv: (key) => {
|
|
120
|
+
Deno.env.delete(key);
|
|
121
|
+
},
|
|
122
|
+
exit: (code) => Deno.exit(code)
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Deno entrypoint helper. Reads `HUBSPOT_APP_PRIVATE_KEY` from `Deno.env`
|
|
126
|
+
* when CIMD or DPoP is enabled, imports the supplied loader, and invokes
|
|
127
|
+
* `start` with `AppKeys` or `null`.
|
|
128
|
+
*/
|
|
129
|
+
function secureStart(loader) {
|
|
130
|
+
return runSecureStart(loader, denoSecureStartAdapter);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/server/hono/hubspot-connect-routes/cimd-client-metadata-types.ts
|
|
134
|
+
function assertHubSpotConnectCimdClientMetadata(value) {
|
|
135
|
+
const required = value.scope?.required;
|
|
136
|
+
if (!(Array.isArray(required) && required.length > 0 && required.every((s) => typeof s === "string" && s.length > 0))) throw new Error("HubSpotConnectCimdClientMetadata.scope.required must be a non-empty array of non-empty strings");
|
|
137
|
+
const optional = value.scope.optional;
|
|
138
|
+
if (optional === void 0) return;
|
|
139
|
+
if (!Array.isArray(optional)) throw new Error("HubSpotConnectCimdClientMetadata.scope.optional must be an array when set");
|
|
140
|
+
if (optional.length > 0 && !optional.every((s) => typeof s === "string" && s.length > 0)) throw new Error("HubSpotConnectCimdClientMetadata.scope.optional entries must be non-empty strings");
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/shared/logger.ts
|
|
144
|
+
/**
|
|
145
|
+
* Logger that swallows every message. Convenient for tests and for
|
|
146
|
+
* the SDK's server-side handlers when no logger is provided by the
|
|
147
|
+
* host application.
|
|
148
|
+
*/
|
|
149
|
+
const noopLogger = {
|
|
150
|
+
debug: () => {},
|
|
151
|
+
info: () => {},
|
|
152
|
+
warn: () => {},
|
|
153
|
+
error: () => {}
|
|
154
|
+
};
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/server/api-client-core/plugins/fetch-transport.ts
|
|
157
|
+
const BASE_URL = "https://api.hubapi.com";
|
|
158
|
+
function appendRecordToUrlSearchParams(params, record) {
|
|
159
|
+
for (const [key, value] of Object.entries(record)) {
|
|
160
|
+
if (value == null) continue;
|
|
161
|
+
if (Array.isArray(value)) for (const item of value) params.append(key, String(item));
|
|
162
|
+
else params.append(key, String(value));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Transport plugin that executes HTTP requests using the Fetch API.
|
|
167
|
+
*
|
|
168
|
+
* This is the terminal middleware in the chain — it builds the actual HTTP
|
|
169
|
+
* request from the operation descriptor and returns a normalized response.
|
|
170
|
+
* It handles path-parameter interpolation, query-string serialization,
|
|
171
|
+
* Bearer-token auth, and content-type negotiation (JSON, form-urlencoded, multipart).
|
|
172
|
+
*/
|
|
173
|
+
function fetchTransportPlugin(options) {
|
|
174
|
+
return { activate(api) {
|
|
175
|
+
api.addMiddleware(async (ctx) => {
|
|
176
|
+
const { operation } = ctx;
|
|
177
|
+
let url = `${BASE_URL}${operation.path}`;
|
|
178
|
+
if (operation.pathParams) for (const [key, value] of Object.entries(operation.pathParams)) url = url.replace(`{${key}}`, encodeURIComponent(String(value)));
|
|
179
|
+
if (operation.queryParams) {
|
|
180
|
+
const params = new URLSearchParams();
|
|
181
|
+
appendRecordToUrlSearchParams(params, operation.queryParams);
|
|
182
|
+
const qs = params.toString();
|
|
183
|
+
if (qs) url += `?${qs}`;
|
|
184
|
+
}
|
|
185
|
+
const headers = {
|
|
186
|
+
...operation.headers ?? {},
|
|
187
|
+
Authorization: `Bearer ${options.getAccessToken()}`
|
|
188
|
+
};
|
|
189
|
+
const init = {
|
|
190
|
+
method: operation.method.toUpperCase(),
|
|
191
|
+
headers
|
|
192
|
+
};
|
|
193
|
+
if (operation.contentType === "multipart/form-data" && operation.body) {
|
|
194
|
+
const formData = new FormData();
|
|
195
|
+
for (const [key, value] of Object.entries(operation.body)) {
|
|
196
|
+
if (value == null) continue;
|
|
197
|
+
if (isBinaryData(value)) formData.append(key, value.source);
|
|
198
|
+
else formData.append(key, String(value));
|
|
199
|
+
}
|
|
200
|
+
init.body = formData;
|
|
201
|
+
} else if (operation.contentType === "application/x-www-form-urlencoded") {
|
|
202
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
203
|
+
if (operation.body) {
|
|
204
|
+
const params = new URLSearchParams();
|
|
205
|
+
appendRecordToUrlSearchParams(params, operation.body);
|
|
206
|
+
const encoded = params.toString();
|
|
207
|
+
if (encoded) init.body = encoded;
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
headers["Content-Type"] = "application/json";
|
|
211
|
+
if (operation.body) init.body = JSON.stringify(operation.body);
|
|
212
|
+
}
|
|
213
|
+
const response = await fetch(url, init);
|
|
214
|
+
const responseHeaders = Object.create(null);
|
|
215
|
+
response.headers.forEach((value, key) => {
|
|
216
|
+
responseHeaders[key] = value;
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
status: response.status,
|
|
220
|
+
statusText: response.statusText,
|
|
221
|
+
headers: responseHeaders,
|
|
222
|
+
bodyJson: response.status === 204 ? void 0 : await response.json()
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
} };
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/server/constants.ts
|
|
229
|
+
/**
|
|
230
|
+
* Cookie name carrying the DPoP-bound access token. Uses the
|
|
231
|
+
* `__Host-` prefix so the browser only accepts it from a `Secure`
|
|
232
|
+
* response with `Path=/` — a defense-in-depth against subdomain
|
|
233
|
+
* cookie injection.
|
|
234
|
+
*/
|
|
235
|
+
const HUBSPOT_ACCESS_TOKEN_COOKIE_NAME = "__Host-hs_access_token";
|
|
236
|
+
/**
|
|
237
|
+
* Cookie carrying the opaque app-session ID. Hashed before being put
|
|
238
|
+
* on the wire as a DPoP `sid` claim.
|
|
239
|
+
*/
|
|
240
|
+
const HUBSPOT_APP_SID_COOKIE_NAME = "__Host-hs_app_sid";
|
|
241
|
+
/**
|
|
242
|
+
* Cookie pinning the browser-facing app origin (e.g.
|
|
243
|
+
* `https://app.example.com`) for the lifetime of an app session.
|
|
244
|
+
* Set by `auth/init-session` from the request's `Origin` header and
|
|
245
|
+
* read by:
|
|
246
|
+
*
|
|
247
|
+
* - The CORS middleware to emit a credentialed
|
|
248
|
+
* `Access-Control-Allow-Origin` value (`*` is forbidden when
|
|
249
|
+
* `Access-Control-Allow-Credentials: true`).
|
|
250
|
+
* - `auth/complete` to rebuild the OAuth `redirect_uri` it sent to
|
|
251
|
+
* HubSpot during `init-session` (the token endpoint validates that
|
|
252
|
+
* the two values match).
|
|
253
|
+
*
|
|
254
|
+
* `__Host-` prefixed (Path=/, Secure, no Domain), so the same
|
|
255
|
+
* function host can serve both `hubspot-connect` and `api` routes
|
|
256
|
+
* and read the cookie from either.
|
|
257
|
+
*/
|
|
258
|
+
const HUBSPOT_APP_ORIGIN_COOKIE_NAME = "__Host-hs_app_origin";
|
|
259
|
+
/**
|
|
260
|
+
* Prefix used for refresh-token cookies. Each session gets its own
|
|
261
|
+
* cookie name (`hs_refresh_<sidHash>`) so multiple devices/tabs can
|
|
262
|
+
* coexist without overwriting each other's refresh tokens.
|
|
263
|
+
*/
|
|
264
|
+
const HUBSPOT_REFRESH_COOKIE_PREFIX = "hs_refresh_";
|
|
265
|
+
const PROTECTED_COOKIE_NAMES = new Set([
|
|
266
|
+
HUBSPOT_ACCESS_TOKEN_COOKIE_NAME,
|
|
267
|
+
HUBSPOT_APP_SID_COOKIE_NAME,
|
|
268
|
+
HUBSPOT_APP_ORIGIN_COOKIE_NAME
|
|
269
|
+
]);
|
|
270
|
+
/**
|
|
271
|
+
* Returns `true` for cookies the SDK manages internally
|
|
272
|
+
* (access token, session ID, any refresh-token cookie). The
|
|
273
|
+
* `sanitizeRequest` helper uses this to strip these cookies from the
|
|
274
|
+
* request before user code sees it, ensuring user route handlers
|
|
275
|
+
* cannot accidentally leak them in logs or proxy them upstream.
|
|
276
|
+
*/
|
|
277
|
+
function isProtectedCookieName(cookieName) {
|
|
278
|
+
return PROTECTED_COOKIE_NAMES.has(cookieName) || cookieName.startsWith("hs_refresh_");
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Cookie carrying the PKCE code verifier between `init-session` and
|
|
282
|
+
* `auth/complete`. Set with `SameSite=None; Secure; Partitioned` so the
|
|
283
|
+
* frontend's credentialed cross-site `POST /auth/complete` (made from
|
|
284
|
+
* the OAuth callback page on the app origin to the SDK's edge function
|
|
285
|
+
* origin) carries it through. `Lax` would silently drop it on that
|
|
286
|
+
* fetch, breaking every successful HubSpot redirect.
|
|
287
|
+
*/
|
|
288
|
+
const TEMP_COOKIE_PKCE_VERIFIER = "__hs_pkce_verifier";
|
|
289
|
+
/**
|
|
290
|
+
* Cookie carrying the OAuth `state` value between `init-session` and
|
|
291
|
+
* `auth/complete`. Compared against the `state` query parameter as the
|
|
292
|
+
* primary CSRF defense. Same `SameSite=None; Secure; Partitioned`
|
|
293
|
+
* attributes as `TEMP_COOKIE_PKCE_VERIFIER` for the same cross-site
|
|
294
|
+
* `POST /auth/complete` reason.
|
|
295
|
+
*/
|
|
296
|
+
const TEMP_COOKIE_OAUTH_STATE = "__hs_oauth_state";
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/server/proxy.ts
|
|
299
|
+
const HUBSPOT_API_ORIGIN = requireEnv("HUBSPOT_API_ORIGIN");
|
|
300
|
+
const notAuthenticated = {
|
|
301
|
+
authenticated: false,
|
|
302
|
+
fetch: async () => {
|
|
303
|
+
return new Response(null, {
|
|
304
|
+
status: 401,
|
|
305
|
+
statusText: "Unauthorized",
|
|
306
|
+
headers: { "Content-Type": "application/json" }
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const EMPTY_HEADERS = {};
|
|
311
|
+
function createHubSpotProxy(options) {
|
|
312
|
+
const { userCredentials, appKeys, logger = noopLogger } = options;
|
|
313
|
+
if (isHubspotDpopEnabled() && appKeys === null) throw new Error("createHubSpotProxy: appKeys is required when HUBSPOT_DPOP_ENABLED is not false");
|
|
314
|
+
const { accessToken, sessionId } = userCredentials;
|
|
315
|
+
if (!accessToken || !sessionId) {
|
|
316
|
+
logger.debug("createHubSpotProxy: returning unauthenticated proxy (missing cookies)");
|
|
317
|
+
return notAuthenticated;
|
|
318
|
+
}
|
|
319
|
+
async function proxyFetch(request) {
|
|
320
|
+
const { path, method = "GET", headers = EMPTY_HEADERS, body } = request;
|
|
321
|
+
const targetUrl = `${HUBSPOT_API_ORIGIN}${path}`;
|
|
322
|
+
const useDpop = isHubspotDpopEnabled();
|
|
323
|
+
let authorizationHeaders;
|
|
324
|
+
if (useDpop) {
|
|
325
|
+
const ath = await sha256base64url(accessToken);
|
|
326
|
+
const sid = await sha256base64url(sessionId);
|
|
327
|
+
const dpopProof = await signDpopProof({
|
|
328
|
+
appKeys,
|
|
329
|
+
claims: {
|
|
330
|
+
htm: method,
|
|
331
|
+
htu: targetUrl,
|
|
332
|
+
jti: crypto.randomUUID(),
|
|
333
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
334
|
+
ath,
|
|
335
|
+
sid
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
authorizationHeaders = {
|
|
339
|
+
Authorization: `DPoP ${accessToken}`,
|
|
340
|
+
DPoP: dpopProof
|
|
341
|
+
};
|
|
342
|
+
} else authorizationHeaders = { Authorization: `Bearer ${accessToken}` };
|
|
343
|
+
const requestInit = {
|
|
344
|
+
headers: {
|
|
345
|
+
...headers,
|
|
346
|
+
...authorizationHeaders
|
|
347
|
+
},
|
|
348
|
+
method
|
|
349
|
+
};
|
|
350
|
+
if (body != null) requestInit.body = body;
|
|
351
|
+
return fetch(targetUrl, requestInit);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
authenticated: true,
|
|
355
|
+
fetch: proxyFetch
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/server/utils/cookie-utils.ts
|
|
360
|
+
/**
|
|
361
|
+
* Parses an HTTP `Cookie` request header into a `name -> value` map.
|
|
362
|
+
* Tolerates leading/trailing whitespace, missing `=` (treats the
|
|
363
|
+
* cookie value as empty), and duplicate names (last write wins).
|
|
364
|
+
*
|
|
365
|
+
* Returns an empty object when `cookieHeader` is `null`/`undefined`
|
|
366
|
+
* /empty so callers don't have to null-check the input.
|
|
367
|
+
*/
|
|
368
|
+
function parseCookies(cookieHeader) {
|
|
369
|
+
if (!cookieHeader) return {};
|
|
370
|
+
return Object.fromEntries(cookieHeader.split(";").map((pair) => {
|
|
371
|
+
const eqIdx = pair.indexOf("=");
|
|
372
|
+
if (eqIdx === -1) return [pair.trim(), ""];
|
|
373
|
+
return [pair.slice(0, eqIdx).trim(), pair.slice(eqIdx + 1).trim()];
|
|
374
|
+
}).filter(([name]) => name.length > 0));
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/server/sanitize-request.ts
|
|
378
|
+
function serializeCookies(cookies) {
|
|
379
|
+
const parts = [];
|
|
380
|
+
for (const [name, value] of cookies) parts.push(`${name}=${value}`);
|
|
381
|
+
return parts.join("; ");
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Mutates `headers` in place: parses the `Cookie` header, drops every
|
|
385
|
+
* protected cookie (see {@link isProtectedCookieName}), and rewrites
|
|
386
|
+
* the header. Deletes the header entirely when nothing survives.
|
|
387
|
+
*
|
|
388
|
+
* Used by the SDK's auth middleware after it has read the access
|
|
389
|
+
* token / session ID, so user route handlers never see — and
|
|
390
|
+
* therefore cannot leak — those cookies, while CORS / auth
|
|
391
|
+
* middleware that ran first still got the raw values.
|
|
392
|
+
*/
|
|
393
|
+
function stripProtectedCookies(headers) {
|
|
394
|
+
const cookies = parseCookies(headers.get("Cookie"));
|
|
395
|
+
const surviving = /* @__PURE__ */ new Map();
|
|
396
|
+
for (const [name, value] of Object.entries(cookies)) if (!isProtectedCookieName(name)) surviving.set(name, value);
|
|
397
|
+
if (surviving.size === 0) headers.delete("cookie");
|
|
398
|
+
else headers.set("cookie", serializeCookies(surviving));
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Returns a clone of `original` whose `Cookie` header has every
|
|
402
|
+
* protected cookie removed (see {@link isProtectedCookieName}). When
|
|
403
|
+
* no other cookies remain, the header is dropped entirely.
|
|
404
|
+
*
|
|
405
|
+
* Standalone helper retained for callers that want a new Request
|
|
406
|
+
* (e.g. tests). Inside the SDK's per-request handler, the auth
|
|
407
|
+
* middleware uses {@link stripProtectedCookies} directly on
|
|
408
|
+
* `c.req.raw.headers` so the auth check itself can still read the
|
|
409
|
+
* protected cookies before they are stripped.
|
|
410
|
+
*/
|
|
411
|
+
function sanitizeRequest(original) {
|
|
412
|
+
const headers = new Headers(original.headers);
|
|
413
|
+
stripProtectedCookies(headers);
|
|
414
|
+
const init = {
|
|
415
|
+
method: original.method,
|
|
416
|
+
headers,
|
|
417
|
+
redirect: original.redirect,
|
|
418
|
+
signal: original.signal
|
|
419
|
+
};
|
|
420
|
+
if (original.body !== null) {
|
|
421
|
+
init.body = original.body;
|
|
422
|
+
init.duplex = "half";
|
|
423
|
+
}
|
|
424
|
+
return new Request(original.url, init);
|
|
425
|
+
}
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/server/hono/utils/cors-middleware.ts
|
|
428
|
+
/**
|
|
429
|
+
* Comma-separated list of request headers the SDK accepts on
|
|
430
|
+
* cross-site fetches. Mirrors the Supabase Edge Functions defaults
|
|
431
|
+
* the Lovable AI agent emits today, plus `content-type` for the
|
|
432
|
+
* `auth/complete` POST body and `accept` so JSON content negotiation
|
|
433
|
+
* works.
|
|
434
|
+
*/
|
|
435
|
+
const ALLOWED_HEADERS = [
|
|
436
|
+
"authorization",
|
|
437
|
+
"x-client-info",
|
|
438
|
+
"apikey",
|
|
439
|
+
"content-type",
|
|
440
|
+
"accept",
|
|
441
|
+
"x-supabase-client-platform",
|
|
442
|
+
"x-supabase-client-platform-version",
|
|
443
|
+
"x-supabase-client-runtime",
|
|
444
|
+
"x-supabase-client-runtime-version"
|
|
445
|
+
].join(", ");
|
|
446
|
+
const ALLOWED_METHODS = "GET, POST, OPTIONS";
|
|
447
|
+
const PREFLIGHT_MAX_AGE_SECONDS = "600";
|
|
448
|
+
/**
|
|
449
|
+
* Reads the persisted app-origin cookie from the request, falling
|
|
450
|
+
* back to the literal `Origin` request header. The cookie is the
|
|
451
|
+
* authoritative pin once `auth/init-session` has run; on the very
|
|
452
|
+
* first init-session call (no cookie yet) we just echo whatever
|
|
453
|
+
* `Origin` the caller sent — the actual access decision is enforced
|
|
454
|
+
* by cookie-based authentication on every other route, not by CORS.
|
|
455
|
+
*/
|
|
456
|
+
function resolveAllowedOrigin(c) {
|
|
457
|
+
const pinned = parseCookies(c.req.header("Cookie"))[HUBSPOT_APP_ORIGIN_COOKIE_NAME];
|
|
458
|
+
if (pinned) return pinned;
|
|
459
|
+
return c.req.header("Origin") ?? null;
|
|
460
|
+
}
|
|
461
|
+
function setSharedCorsHeaders(c, allowOrigin) {
|
|
462
|
+
c.res.headers.set("Access-Control-Allow-Origin", allowOrigin);
|
|
463
|
+
c.res.headers.set("Access-Control-Allow-Credentials", "true");
|
|
464
|
+
c.res.headers.set("Vary", "Origin, Cookie");
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Hono middleware that emits credentialed CORS response headers for
|
|
468
|
+
* the cross-origin Lovable / Supabase deployment shape.
|
|
469
|
+
*
|
|
470
|
+
* - On `OPTIONS` preflight: short-circuits with a 204 carrying
|
|
471
|
+
* `Access-Control-Allow-*` headers. The browser will then send the
|
|
472
|
+
* real request with cookies attached.
|
|
473
|
+
* - On every other method: echoes the pinned `__Host-hs_app_origin`
|
|
474
|
+
* cookie value (or, before init-session has run, the request
|
|
475
|
+
* `Origin` header) as `Access-Control-Allow-Origin`, with
|
|
476
|
+
* `Access-Control-Allow-Credentials: true`. The wildcard `*` is
|
|
477
|
+
* forbidden by browsers when credentials are included, so the
|
|
478
|
+
* middleware always echoes a concrete origin.
|
|
479
|
+
*
|
|
480
|
+
* Skips header emission entirely when the request has no `Origin`
|
|
481
|
+
* (server-to-server calls, curl, etc.) so non-browser callers are
|
|
482
|
+
* left untouched.
|
|
483
|
+
*/
|
|
484
|
+
function corsMiddleware() {
|
|
485
|
+
return async (c, next) => {
|
|
486
|
+
const allowOrigin = resolveAllowedOrigin(c);
|
|
487
|
+
if (c.req.method === "OPTIONS") {
|
|
488
|
+
const headers = new Headers();
|
|
489
|
+
if (allowOrigin) {
|
|
490
|
+
headers.set("Access-Control-Allow-Origin", allowOrigin);
|
|
491
|
+
headers.set("Access-Control-Allow-Credentials", "true");
|
|
492
|
+
headers.set("Vary", "Origin, Cookie");
|
|
493
|
+
}
|
|
494
|
+
headers.set("Access-Control-Allow-Methods", ALLOWED_METHODS);
|
|
495
|
+
headers.set("Access-Control-Allow-Headers", ALLOWED_HEADERS);
|
|
496
|
+
headers.set("Access-Control-Max-Age", PREFLIGHT_MAX_AGE_SECONDS);
|
|
497
|
+
return new Response(null, {
|
|
498
|
+
status: 204,
|
|
499
|
+
headers
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
await next();
|
|
503
|
+
if (allowOrigin) setSharedCorsHeaders(c, allowOrigin);
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/server/hono/hono-request-handler.ts
|
|
508
|
+
/**
|
|
509
|
+
* Wraps a Hono app so its `fetch` handler additionally:
|
|
510
|
+
*
|
|
511
|
+
* - Strips SDK-managed cookies (access token, refresh, sid) from the
|
|
512
|
+
* request before the app sees them, via `sanitizeRequest`.
|
|
513
|
+
* - Exposes a `hubSpotProxy` on the Hono context so route handlers
|
|
514
|
+
* can issue authenticated calls to HubSpot's API on behalf of the
|
|
515
|
+
* browser session.
|
|
516
|
+
*/
|
|
517
|
+
function createAppConnectRequestHandler(options) {
|
|
518
|
+
const { registerRoutes, appKeys, logger = noopLogger } = options;
|
|
519
|
+
const app = new Hono();
|
|
520
|
+
app.use("*", corsMiddleware());
|
|
521
|
+
app.use("*", async (c, next) => {
|
|
522
|
+
const { authenticated } = c.env.hubSpot;
|
|
523
|
+
if (!authenticated) return c.json({ error: "Unauthorized" }, 401);
|
|
524
|
+
await next();
|
|
525
|
+
});
|
|
526
|
+
registerRoutes(app);
|
|
527
|
+
return (request) => {
|
|
528
|
+
const cookie = request.headers.get("Cookie");
|
|
529
|
+
if (!cookie) throw new Error("Missing auth cookies");
|
|
530
|
+
const cookies = parseCookies(cookie);
|
|
531
|
+
const accessToken = cookies[HUBSPOT_ACCESS_TOKEN_COOKIE_NAME];
|
|
532
|
+
const proxy = createHubSpotProxy({
|
|
533
|
+
userCredentials: {
|
|
534
|
+
accessToken,
|
|
535
|
+
sessionId: cookies[HUBSPOT_APP_SID_COOKIE_NAME]
|
|
536
|
+
},
|
|
537
|
+
appKeys,
|
|
538
|
+
logger
|
|
539
|
+
});
|
|
540
|
+
const client = createHubSpotClient({ plugins: [fetchTransportPlugin({ getAccessToken: () => {
|
|
541
|
+
if (!accessToken) throw new Error("Missing access token");
|
|
542
|
+
return accessToken;
|
|
543
|
+
} })] });
|
|
544
|
+
const sanitizedRequest = sanitizeRequest(request);
|
|
545
|
+
const honoBindings = { hubSpot: {
|
|
546
|
+
proxy,
|
|
547
|
+
client,
|
|
548
|
+
authenticated: proxy.authenticated
|
|
549
|
+
} };
|
|
550
|
+
return app.fetch(sanitizedRequest, honoBindings);
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/shared/constants.ts
|
|
555
|
+
/**
|
|
556
|
+
* Path the browser visits after HubSpot's authorize endpoint
|
|
557
|
+
* redirects back to the app. Mounted on the **frontend** origin (not
|
|
558
|
+
* the SDK's edge function host) so all OAuth-related cookies live in
|
|
559
|
+
* the `(frontend, edge)` CHIPS partition.
|
|
560
|
+
*
|
|
561
|
+
* The SDK's `auth/init-session` builds the OAuth `redirect_uri` as
|
|
562
|
+
* `${requestOrigin}${HUBSPOT_FRONTEND_CALLBACK_PATH}`. The browser
|
|
563
|
+
* controller, on `start()`, recognizes this path on `window.location`
|
|
564
|
+
* and forwards `?code` + `?state` to the SDK's `auth/complete`
|
|
565
|
+
* endpoint via a credentialed cross-site fetch. The host app must
|
|
566
|
+
* register `${app_origin}${HUBSPOT_FRONTEND_CALLBACK_PATH}` as a
|
|
567
|
+
* redirect URI in its HubSpot app settings.
|
|
568
|
+
*/
|
|
569
|
+
const HUBSPOT_FRONTEND_CALLBACK_PATH = "/__hubspot_oauth_callback";
|
|
570
|
+
/**
|
|
571
|
+
* Query parameter on the `auth/complete` POST request carrying the
|
|
572
|
+
* authorization `code` HubSpot returned to the frontend callback.
|
|
573
|
+
*/
|
|
574
|
+
const AUTH_COMPLETE_CODE_PARAM = "code";
|
|
575
|
+
/**
|
|
576
|
+
* Query parameter on the `auth/complete` POST request carrying the
|
|
577
|
+
* OAuth `state` HubSpot echoed back to the frontend callback.
|
|
578
|
+
*/
|
|
579
|
+
const AUTH_COMPLETE_STATE_PARAM = "state";
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/server/hono/utils/cookie-utils.ts
|
|
582
|
+
/**
|
|
583
|
+
* Appends a `Set-Cookie` header to the response. Hono replaces single
|
|
584
|
+
* headers by default, so this uses `{ append: true }` to emit multiple
|
|
585
|
+
* cookies on the same response.
|
|
586
|
+
*/
|
|
587
|
+
function setResponseCookie(options) {
|
|
588
|
+
const { c, value } = options;
|
|
589
|
+
c.header("Set-Cookie", value, { append: true });
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Builds a `Set-Cookie` header value with HubSpot's default attributes
|
|
593
|
+
* (HttpOnly, Secure, SameSite). Centralizes the serialization so all
|
|
594
|
+
* cookies share the same policy.
|
|
595
|
+
*/
|
|
596
|
+
function serializeCookie(options) {
|
|
597
|
+
const { name, value, path, sameSite = "Strict", maxAge, secure = true, httpOnly = true, partitioned = false } = options;
|
|
598
|
+
const parts = [`${name}=${value}`];
|
|
599
|
+
if (httpOnly) parts.push("HttpOnly");
|
|
600
|
+
if (secure) parts.push("Secure");
|
|
601
|
+
parts.push(`SameSite=${sameSite}`);
|
|
602
|
+
parts.push(`Path=${path}`);
|
|
603
|
+
parts.push(`Max-Age=${maxAge}`);
|
|
604
|
+
if (partitioned) parts.push("Partitioned");
|
|
605
|
+
return parts.join("; ");
|
|
606
|
+
}
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region src/server/hono/hubspot-connect-routes/constants.ts
|
|
609
|
+
const SESSION_MAX_AGE_SEC = 2592e3;
|
|
610
|
+
const OAUTH_TEMP_MAX_AGE_SEC = 3600;
|
|
611
|
+
const REFRESH_COOKIE_MAX_AGE_SEC = 2592e3;
|
|
612
|
+
//#endregion
|
|
613
|
+
//#region src/server/hono/hubspot-connect-routes/oauth-client.ts
|
|
614
|
+
/**
|
|
615
|
+
* Lifetime of the OAuth `client_assertion` JWT in seconds. Short by
|
|
616
|
+
* design — RFC 7521 recommends short-lived assertions, and HubSpot's
|
|
617
|
+
* authorization server rejects assertions older than ~5 minutes.
|
|
618
|
+
*/
|
|
619
|
+
const CLIENT_ASSERTION_TTL_SEC = 60;
|
|
620
|
+
/**
|
|
621
|
+
* The `client_assertion_type` value mandated by RFC 7523 §2.2 for
|
|
622
|
+
* JWT-bearer client authentication.
|
|
623
|
+
*/
|
|
624
|
+
const JWT_BEARER_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
625
|
+
/**
|
|
626
|
+
* Mints a short-lived ES256 JWT used as the `client_assertion`
|
|
627
|
+
* parameter when calling HubSpot's token endpoint, per RFC 7523.
|
|
628
|
+
*/
|
|
629
|
+
async function buildClientAssertion(options) {
|
|
630
|
+
const { appKeys, clientId, audience } = options;
|
|
631
|
+
return signJwt({
|
|
632
|
+
privateKey: appKeys.appPrivateKey,
|
|
633
|
+
payload: {
|
|
634
|
+
iss: clientId,
|
|
635
|
+
sub: clientId,
|
|
636
|
+
aud: audience,
|
|
637
|
+
jti: crypto.randomUUID()
|
|
638
|
+
},
|
|
639
|
+
ttlSeconds: CLIENT_ASSERTION_TTL_SEC
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Signs a DPoP proof for a `POST` to HubSpot's token endpoint. RFC 9449
|
|
644
|
+
* binds the resulting access token to the proving public key so that
|
|
645
|
+
* later API calls must come from the same key holder.
|
|
646
|
+
*/
|
|
647
|
+
async function buildTokenEndpointDpopProof(options) {
|
|
648
|
+
const { appKeys, tokenEndpointUrl, sessionIdHash } = options;
|
|
649
|
+
return signDpopProof({
|
|
650
|
+
appKeys,
|
|
651
|
+
claims: {
|
|
652
|
+
htm: "POST",
|
|
653
|
+
htu: tokenEndpointUrl,
|
|
654
|
+
jti: crypto.randomUUID(),
|
|
655
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
656
|
+
sid: sessionIdHash
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* POSTs an `application/x-www-form-urlencoded` body to a HubSpot token
|
|
662
|
+
* endpoint. Attaches a `DPoP` header when `isDpopEnabled` is true and
|
|
663
|
+
* `dpopProof` is provided. Reads the response once — either
|
|
664
|
+
* as JSON on success or as text on failure — so callers never have to
|
|
665
|
+
* consume the body twice.
|
|
666
|
+
*/
|
|
667
|
+
async function requestOAuthToken(options) {
|
|
668
|
+
const { tokenEndpointUrl, formParams, dpopProof, isDpopEnabled } = options;
|
|
669
|
+
const headers = { "Content-Type": "application/x-www-form-urlencoded" };
|
|
670
|
+
if (isDpopEnabled) {
|
|
671
|
+
if (!dpopProof) throw new Error("DPoP proof is required when DPoP is enabled");
|
|
672
|
+
headers.DPoP = dpopProof;
|
|
673
|
+
}
|
|
674
|
+
const tokenResponse = await fetch(tokenEndpointUrl, {
|
|
675
|
+
method: "POST",
|
|
676
|
+
headers,
|
|
677
|
+
body: new URLSearchParams(formParams)
|
|
678
|
+
});
|
|
679
|
+
if (!tokenResponse.ok) {
|
|
680
|
+
const errorText = await tokenResponse.text();
|
|
681
|
+
return {
|
|
682
|
+
ok: false,
|
|
683
|
+
status: tokenResponse.status,
|
|
684
|
+
errorText
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
ok: true,
|
|
689
|
+
body: await tokenResponse.json()
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Form parameters always present on the OAuth `client_assertion` flow.
|
|
694
|
+
* Spread into the call site's `formParams`.
|
|
695
|
+
*/
|
|
696
|
+
function buildClientAssertionFormParams(input) {
|
|
697
|
+
const { clientId, clientAssertion } = input;
|
|
698
|
+
return {
|
|
699
|
+
client_id: clientId,
|
|
700
|
+
client_assertion_type: JWT_BEARER_ASSERTION_TYPE,
|
|
701
|
+
client_assertion: clientAssertion
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function buildClientSecretFormParams(options) {
|
|
705
|
+
const { clientId, clientSecret } = options;
|
|
706
|
+
return {
|
|
707
|
+
client_id: clientId,
|
|
708
|
+
client_secret: clientSecret
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region src/server/hono/hubspot-connect-routes/utils.ts
|
|
713
|
+
function clearTempCookie(name) {
|
|
714
|
+
return serializeCookie({
|
|
715
|
+
name,
|
|
716
|
+
value: "",
|
|
717
|
+
path: "/",
|
|
718
|
+
sameSite: "None",
|
|
719
|
+
maxAge: 0,
|
|
720
|
+
partitioned: true
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Parses the request `Origin` header into the canonical origin
|
|
725
|
+
* string (`URL.origin`) or returns `null` when the header is
|
|
726
|
+
* missing, malformed, or carries a scheme/host the SDK does not
|
|
727
|
+
* accept.
|
|
728
|
+
*
|
|
729
|
+
* Accepted shapes:
|
|
730
|
+
*
|
|
731
|
+
* - `https://<host>` for production deployments.
|
|
732
|
+
* - `http://localhost[:<port>]` and `http://127.0.0.1[:<port>]`
|
|
733
|
+
* for local development; browsers exempt these from the `Secure`
|
|
734
|
+
* cookie restriction.
|
|
735
|
+
*
|
|
736
|
+
* Rejects values with a path/query/hash component (the request
|
|
737
|
+
* `Origin` header is by spec a bare origin, so anything else
|
|
738
|
+
* indicates a malformed or hostile request).
|
|
739
|
+
*/
|
|
740
|
+
function parseAppOriginHeader(originHeader) {
|
|
741
|
+
if (!originHeader) return null;
|
|
742
|
+
let parsed;
|
|
743
|
+
try {
|
|
744
|
+
parsed = new URL(originHeader);
|
|
745
|
+
} catch {
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
if (parsed.pathname !== "/" && parsed.pathname !== "") return null;
|
|
749
|
+
if (parsed.search !== "" || parsed.hash !== "") return null;
|
|
750
|
+
if (parsed.protocol === "https:") return parsed.origin;
|
|
751
|
+
if (parsed.protocol === "http:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1")) return parsed.origin;
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* OAuth `redirect_uri` for the cross-origin app shape: the OAuth
|
|
756
|
+
* callback lands on the **frontend** origin (not the SDK's edge
|
|
757
|
+
* function host), so all cookies set by `init-session` and read by
|
|
758
|
+
* `auth/complete` live in the same `(frontend, edge)` CHIPS
|
|
759
|
+
* partition.
|
|
760
|
+
*
|
|
761
|
+
* Used by `auth/init-session` (when building `authorization_url`)
|
|
762
|
+
* and `auth/complete` (which must rebuild the same value to satisfy
|
|
763
|
+
* the OAuth token endpoint's `redirect_uri` check).
|
|
764
|
+
*/
|
|
765
|
+
function buildFrontendOAuthRedirectUri(appOrigin) {
|
|
766
|
+
return `${appOrigin}${HUBSPOT_FRONTEND_CALLBACK_PATH}`;
|
|
767
|
+
}
|
|
768
|
+
function isSafeReturnPath(rawPath) {
|
|
769
|
+
if (!rawPath.startsWith("/")) return false;
|
|
770
|
+
if (rawPath.includes("\0")) return false;
|
|
771
|
+
let decoded;
|
|
772
|
+
try {
|
|
773
|
+
decoded = decodeURIComponent(rawPath);
|
|
774
|
+
} catch {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
if (!decoded.startsWith("/")) return false;
|
|
778
|
+
const second = decoded.charAt(1);
|
|
779
|
+
if (second === "/" || second === "\\") return false;
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
function normalizeHubSpotConnectBasePath(basePath) {
|
|
783
|
+
return basePath.endsWith("/") && basePath.length > 1 ? basePath.slice(0, -1) : basePath;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Public origin for hubspot-connect URLs (`redirect_uri`, CIMD `client_id`,
|
|
787
|
+
* `jwks_uri`). Matches the host/proto rules used for the OAuth callback.
|
|
788
|
+
*/
|
|
789
|
+
function buildHubSpotConnectRequestOrigin(options) {
|
|
790
|
+
const { requestUrl, xForwardedProto, xForwardedHost, requestHostHeader } = options;
|
|
791
|
+
const proto = xForwardedProto?.split(",")[0]?.trim();
|
|
792
|
+
if (proto && (proto === "http" || proto === "https")) {
|
|
793
|
+
const forwardedHost = xForwardedHost?.split(",")[0]?.trim();
|
|
794
|
+
const hostHeader = requestHostHeader?.split(",")[0]?.trim();
|
|
795
|
+
return `${proto}://${forwardedHost || hostHeader || new URL(requestUrl).host}`;
|
|
796
|
+
}
|
|
797
|
+
return new URL(requestUrl).origin;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* OAuth `redirect_uri` for the hubspot-connect callback. Uses
|
|
801
|
+
* `X-Forwarded-Proto` with `X-Forwarded-Host`, then `Host`, then the request URL
|
|
802
|
+
* host when the proto is forwarded (reverse proxy); otherwise the request URL
|
|
803
|
+
* origin.
|
|
804
|
+
*/
|
|
805
|
+
function buildOAuthRedirectUriFromRequest(options) {
|
|
806
|
+
const trimmed = normalizeHubSpotConnectBasePath(options.basePath);
|
|
807
|
+
return `${buildHubSpotConnectRequestOrigin(options)}${trimmed}/auth/callback`;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* CIMD `client_id` URL: `{origin}{basePath}/client.json`.
|
|
811
|
+
*/
|
|
812
|
+
function buildCimdClientIdUrlFromRequest(options) {
|
|
813
|
+
const trimmed = normalizeHubSpotConnectBasePath(options.basePath);
|
|
814
|
+
return `${buildHubSpotConnectRequestOrigin(options)}${trimmed}/client.json`;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* App JWKS URL published in CIMD: `{origin}{basePath}/jwks.json`.
|
|
818
|
+
*/
|
|
819
|
+
function buildHubSpotAppJwksUrlFromRequest(options) {
|
|
820
|
+
const trimmed = normalizeHubSpotConnectBasePath(options.basePath);
|
|
821
|
+
return `${buildHubSpotConnectRequestOrigin(options)}${trimmed}/jwks.json`;
|
|
822
|
+
}
|
|
823
|
+
function isPositiveFiniteNumber(value) {
|
|
824
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
825
|
+
}
|
|
826
|
+
//#endregion
|
|
827
|
+
//#region src/server/hono/hubspot-connect-routes/auth-complete.ts
|
|
828
|
+
/**
|
|
829
|
+
* Cross-origin OAuth completion endpoint.
|
|
830
|
+
*
|
|
831
|
+
* Called from the React app on the frontend OAuth callback path
|
|
832
|
+
* (`HUBSPOT_FRONTEND_CALLBACK_PATH`) once HubSpot has redirected the
|
|
833
|
+
* browser back with `?code` + `?state`. The browser POSTs both
|
|
834
|
+
* values here as a credentialed cross-site fetch — same partition as
|
|
835
|
+
* `init-session`, so the temp PKCE/state cookies are visible — and
|
|
836
|
+
* the SDK:
|
|
837
|
+
*
|
|
838
|
+
* 1. Validates `state` against the temp `__hs_oauth_state` cookie.
|
|
839
|
+
* 2. Pulls the PKCE verifier from `__hs_pkce_verifier`.
|
|
840
|
+
* 3. Rebuilds the same `redirect_uri` it sent to HubSpot during
|
|
841
|
+
* `init-session` (frontend origin + the fixed callback path);
|
|
842
|
+
* the OAuth token endpoint requires the two values to match.
|
|
843
|
+
* 4. Exchanges `code` for an access + refresh token (with DPoP /
|
|
844
|
+
* CIMD client-assertion when enabled).
|
|
845
|
+
* 5. Sets the durable session cookies (access token, refresh) with
|
|
846
|
+
* `SameSite=None; Secure; Partitioned` so they live in the
|
|
847
|
+
* `(frontend, edge)` partition where subsequent API fetches will
|
|
848
|
+
* read them.
|
|
849
|
+
* 6. Clears the temp cookies.
|
|
850
|
+
* 7. Returns `{ expires_at, return_path }` so the controller can
|
|
851
|
+
* update its session-storage expiry tracking and navigate back to
|
|
852
|
+
* the page the user started the connect flow from.
|
|
853
|
+
*/
|
|
854
|
+
async function handleAuthComplete(c, options) {
|
|
855
|
+
const { appKeys, refreshCookiePath, hubspotConnectEnv } = options;
|
|
856
|
+
const xForwardedProto = c.req.header("x-forwarded-proto") ?? void 0;
|
|
857
|
+
const xForwardedHost = c.req.header("x-forwarded-host") ?? void 0;
|
|
858
|
+
const requestHostHeader = c.req.header("host") ?? void 0;
|
|
859
|
+
const code = c.req.query(AUTH_COMPLETE_CODE_PARAM);
|
|
860
|
+
const state = c.req.query(AUTH_COMPLETE_STATE_PARAM);
|
|
861
|
+
if (!code || !state) return c.json({ error: "Missing code or state" }, 400);
|
|
862
|
+
if (hubspotConnectEnv.isAppPrivateKeyRequired && !appKeys) return c.json({ error: "Server misconfiguration: HUBSPOT_APP_PRIVATE_KEY is required when CIMD or DPoP is enabled" }, 500);
|
|
863
|
+
const cookies = parseCookies(c.req.header("Cookie"));
|
|
864
|
+
const expectedState = cookies[TEMP_COOKIE_OAUTH_STATE];
|
|
865
|
+
const codeVerifier = cookies[TEMP_COOKIE_PKCE_VERIFIER];
|
|
866
|
+
const appOriginCookie = cookies[HUBSPOT_APP_ORIGIN_COOKIE_NAME];
|
|
867
|
+
if (!expectedState || state !== decodeURIComponent(expectedState)) return c.json({ error: "State mismatch" }, 403);
|
|
868
|
+
if (!codeVerifier) return c.json({ error: "Missing PKCE verifier" }, 400);
|
|
869
|
+
const appOrigin = parseAppOriginHeader(appOriginCookie);
|
|
870
|
+
if (!appOrigin) return c.json({ error: "Missing app origin cookie" }, 400);
|
|
871
|
+
let statePayload;
|
|
872
|
+
try {
|
|
873
|
+
statePayload = JSON.parse(new TextDecoder().decode(base64urlDecode(decodeURIComponent(state))));
|
|
874
|
+
} catch {
|
|
875
|
+
return c.json({ error: "Malformed state value" }, 400);
|
|
876
|
+
}
|
|
877
|
+
const returnPath = statePayload.return_path;
|
|
878
|
+
if (!returnPath || !isSafeReturnPath(returnPath)) return c.json({ error: "Invalid return path in state" }, 400);
|
|
879
|
+
const sessionId = statePayload.sid;
|
|
880
|
+
if (!sessionId) return c.json({ error: "Missing app session cookie" }, 400);
|
|
881
|
+
const decodedCodeVerifier = decodeURIComponent(codeVerifier);
|
|
882
|
+
const clientId = hubspotConnectEnv.isCimdEnabled ? buildCimdClientIdUrlFromRequest({
|
|
883
|
+
requestUrl: c.req.url,
|
|
884
|
+
basePath: options.basePath,
|
|
885
|
+
xForwardedProto,
|
|
886
|
+
xForwardedHost,
|
|
887
|
+
requestHostHeader
|
|
888
|
+
}) : hubspotConnectEnv.hubspotClientId;
|
|
889
|
+
const redirectUri = buildFrontendOAuthRedirectUri(appOrigin);
|
|
890
|
+
const tokenEndpointUrl = new URL("/oauth/v1/token", hubspotConnectEnv.hubspotOAuthApiOrigin).href;
|
|
891
|
+
let dpopProof;
|
|
892
|
+
if (hubspotConnectEnv.isDpopEnabled) dpopProof = await buildTokenEndpointDpopProof({
|
|
893
|
+
appKeys,
|
|
894
|
+
tokenEndpointUrl,
|
|
895
|
+
sessionIdHash: sessionId
|
|
896
|
+
});
|
|
897
|
+
let formParams;
|
|
898
|
+
if (hubspotConnectEnv.isCimdEnabled) formParams = {
|
|
899
|
+
grant_type: "authorization_code",
|
|
900
|
+
code,
|
|
901
|
+
code_verifier: decodedCodeVerifier,
|
|
902
|
+
redirect_uri: redirectUri,
|
|
903
|
+
...buildClientAssertionFormParams({
|
|
904
|
+
clientId,
|
|
905
|
+
clientAssertion: await buildClientAssertion({
|
|
906
|
+
appKeys,
|
|
907
|
+
clientId,
|
|
908
|
+
audience: tokenEndpointUrl
|
|
909
|
+
})
|
|
910
|
+
})
|
|
911
|
+
};
|
|
912
|
+
else formParams = {
|
|
913
|
+
grant_type: "authorization_code",
|
|
914
|
+
code,
|
|
915
|
+
code_verifier: decodedCodeVerifier,
|
|
916
|
+
redirect_uri: redirectUri,
|
|
917
|
+
...buildClientSecretFormParams({
|
|
918
|
+
clientId,
|
|
919
|
+
clientSecret: hubspotConnectEnv.hubspotClientSecret
|
|
920
|
+
})
|
|
921
|
+
};
|
|
922
|
+
const tokenResult = await requestOAuthToken({
|
|
923
|
+
tokenEndpointUrl,
|
|
924
|
+
isDpopEnabled: hubspotConnectEnv.isDpopEnabled,
|
|
925
|
+
...dpopProof !== void 0 ? { dpopProof } : {},
|
|
926
|
+
formParams
|
|
927
|
+
});
|
|
928
|
+
if (!tokenResult.ok) return c.json({ error: `Token exchange failed: ${tokenResult.errorText}` }, 502);
|
|
929
|
+
const { access_token: accessToken, refresh_token: refreshToken, expires_in } = tokenResult.body;
|
|
930
|
+
if (!refreshToken) return c.json({ error: "Token response missing refresh_token" }, 502);
|
|
931
|
+
if (!isPositiveFiniteNumber(expires_in)) return c.json({ error: "Token response missing or invalid expires_in" }, 502);
|
|
932
|
+
const expiresAt = Date.now() + expires_in * 1e3;
|
|
933
|
+
const refreshCookieName = `${HUBSPOT_REFRESH_COOKIE_PREFIX}${sessionId}`;
|
|
934
|
+
setResponseCookie({
|
|
935
|
+
c,
|
|
936
|
+
value: serializeCookie({
|
|
937
|
+
name: HUBSPOT_ACCESS_TOKEN_COOKIE_NAME,
|
|
938
|
+
value: accessToken,
|
|
939
|
+
path: "/",
|
|
940
|
+
sameSite: "None",
|
|
941
|
+
partitioned: true,
|
|
942
|
+
maxAge: expires_in
|
|
943
|
+
})
|
|
944
|
+
});
|
|
945
|
+
setResponseCookie({
|
|
946
|
+
c,
|
|
947
|
+
value: serializeCookie({
|
|
948
|
+
name: refreshCookieName,
|
|
949
|
+
value: refreshToken,
|
|
950
|
+
path: refreshCookiePath,
|
|
951
|
+
sameSite: "None",
|
|
952
|
+
partitioned: true,
|
|
953
|
+
maxAge: REFRESH_COOKIE_MAX_AGE_SEC
|
|
954
|
+
})
|
|
955
|
+
});
|
|
956
|
+
setResponseCookie({
|
|
957
|
+
c,
|
|
958
|
+
value: clearTempCookie(TEMP_COOKIE_PKCE_VERIFIER)
|
|
959
|
+
});
|
|
960
|
+
setResponseCookie({
|
|
961
|
+
c,
|
|
962
|
+
value: clearTempCookie(TEMP_COOKIE_OAUTH_STATE)
|
|
963
|
+
});
|
|
964
|
+
return c.json({
|
|
965
|
+
expires_at: expiresAt,
|
|
966
|
+
return_path: returnPath
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
//#endregion
|
|
970
|
+
//#region src/server/hono/hubspot-connect-routes/fetch-hubspot-client-metadata.ts
|
|
971
|
+
/**
|
|
972
|
+
* Builds `scope` and `optional_scope` query values for HubSpot
|
|
973
|
+
* `/oauth/authorize` from in-memory client metadata (same shape as
|
|
974
|
+
* `HubSpotConnectCimdClientMetadata` / `startHubSpotConnectFunction({ client })`).
|
|
975
|
+
*/
|
|
976
|
+
function deriveHubSpotAuthorizeScopesFromClientMetadata(metadata) {
|
|
977
|
+
const required = metadata.scope?.required;
|
|
978
|
+
if (!(Array.isArray(required) && required.length > 0 && required.every((s) => typeof s === "string" && s.length > 0))) return {
|
|
979
|
+
ok: false,
|
|
980
|
+
status: 500,
|
|
981
|
+
message: "Invalid or empty scope.required in client metadata"
|
|
982
|
+
};
|
|
983
|
+
const scope = required.join(" ");
|
|
984
|
+
const optionalRaw = metadata.scope.optional;
|
|
985
|
+
if (optionalRaw == null) return {
|
|
986
|
+
ok: true,
|
|
987
|
+
scope
|
|
988
|
+
};
|
|
989
|
+
if (!Array.isArray(optionalRaw)) return {
|
|
990
|
+
ok: false,
|
|
991
|
+
status: 500,
|
|
992
|
+
message: "Invalid scope.optional in client metadata"
|
|
993
|
+
};
|
|
994
|
+
if (optionalRaw.length === 0) return {
|
|
995
|
+
ok: true,
|
|
996
|
+
scope
|
|
997
|
+
};
|
|
998
|
+
if (!optionalRaw.every((s) => typeof s === "string" && s.length > 0)) return {
|
|
999
|
+
ok: false,
|
|
1000
|
+
status: 500,
|
|
1001
|
+
message: "Invalid scope.optional in client metadata"
|
|
1002
|
+
};
|
|
1003
|
+
return {
|
|
1004
|
+
ok: true,
|
|
1005
|
+
scope,
|
|
1006
|
+
optionalScope: optionalRaw.join(" ")
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
//#endregion
|
|
1010
|
+
//#region src/server/hono/hubspot-connect-routes/auth-init-session.ts
|
|
1011
|
+
async function handleAuthInitSession(c, options) {
|
|
1012
|
+
const { hubspotConnectEnv, cimdClientMetadata } = options;
|
|
1013
|
+
const xForwardedProto = c.req.header("x-forwarded-proto") ?? void 0;
|
|
1014
|
+
const xForwardedHost = c.req.header("x-forwarded-host") ?? void 0;
|
|
1015
|
+
const requestHostHeader = c.req.header("host") ?? void 0;
|
|
1016
|
+
const returnPath = new URL(c.req.url).searchParams.get("return_path") ?? "/";
|
|
1017
|
+
if (!isSafeReturnPath(returnPath)) return c.text("Invalid return_path", 400);
|
|
1018
|
+
const appOrigin = parseAppOriginHeader(c.req.header("Origin"));
|
|
1019
|
+
if (!appOrigin) return c.text("Missing or invalid Origin header; init-session must be called from a browser", 400);
|
|
1020
|
+
const sessionIdBytes = new Uint8Array(32);
|
|
1021
|
+
crypto.getRandomValues(sessionIdBytes);
|
|
1022
|
+
const sessionId = base64url(sessionIdBytes);
|
|
1023
|
+
const sessionIdHash = await sha256base64url(sessionId);
|
|
1024
|
+
const codeVerifierBytes = new Uint8Array(32);
|
|
1025
|
+
crypto.getRandomValues(codeVerifierBytes);
|
|
1026
|
+
const codeVerifier = base64url(codeVerifierBytes);
|
|
1027
|
+
const codeChallenge = await sha256base64url(codeVerifier);
|
|
1028
|
+
const stateValue = base64url(new TextEncoder().encode(JSON.stringify({
|
|
1029
|
+
return_path: returnPath,
|
|
1030
|
+
sid: sessionIdHash
|
|
1031
|
+
})));
|
|
1032
|
+
const clientId = hubspotConnectEnv.isCimdEnabled ? buildCimdClientIdUrlFromRequest({
|
|
1033
|
+
requestUrl: c.req.url,
|
|
1034
|
+
basePath: options.basePath,
|
|
1035
|
+
xForwardedProto,
|
|
1036
|
+
xForwardedHost,
|
|
1037
|
+
requestHostHeader
|
|
1038
|
+
}) : hubspotConnectEnv.hubspotClientId;
|
|
1039
|
+
const redirectUri = buildFrontendOAuthRedirectUri(appOrigin);
|
|
1040
|
+
const authorizeUrl = new URL(hubspotConnectEnv.hubspotAuthorizationEndpoint);
|
|
1041
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
1042
|
+
authorizeUrl.searchParams.set("client_id", clientId);
|
|
1043
|
+
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
|
|
1044
|
+
authorizeUrl.searchParams.set("code_challenge", codeChallenge);
|
|
1045
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
1046
|
+
authorizeUrl.searchParams.set("state", stateValue);
|
|
1047
|
+
authorizeUrl.searchParams.set("sid", sessionIdHash);
|
|
1048
|
+
if (!hubspotConnectEnv.isCimdEnabled) {
|
|
1049
|
+
const scopesResult = deriveHubSpotAuthorizeScopesFromClientMetadata(cimdClientMetadata);
|
|
1050
|
+
if (!scopesResult.ok) return c.text(scopesResult.message, scopesResult.status);
|
|
1051
|
+
authorizeUrl.searchParams.set("scope", scopesResult.scope);
|
|
1052
|
+
if (scopesResult.optionalScope !== void 0) authorizeUrl.searchParams.set("optional_scope", scopesResult.optionalScope);
|
|
1053
|
+
}
|
|
1054
|
+
setResponseCookie({
|
|
1055
|
+
c,
|
|
1056
|
+
value: serializeCookie({
|
|
1057
|
+
name: HUBSPOT_APP_ORIGIN_COOKIE_NAME,
|
|
1058
|
+
value: appOrigin,
|
|
1059
|
+
path: "/",
|
|
1060
|
+
sameSite: "None",
|
|
1061
|
+
partitioned: true,
|
|
1062
|
+
maxAge: SESSION_MAX_AGE_SEC
|
|
1063
|
+
})
|
|
1064
|
+
});
|
|
1065
|
+
setResponseCookie({
|
|
1066
|
+
c,
|
|
1067
|
+
value: serializeCookie({
|
|
1068
|
+
name: HUBSPOT_APP_SID_COOKIE_NAME,
|
|
1069
|
+
value: sessionId,
|
|
1070
|
+
path: "/",
|
|
1071
|
+
sameSite: "None",
|
|
1072
|
+
partitioned: true,
|
|
1073
|
+
maxAge: SESSION_MAX_AGE_SEC
|
|
1074
|
+
})
|
|
1075
|
+
});
|
|
1076
|
+
setResponseCookie({
|
|
1077
|
+
c,
|
|
1078
|
+
value: serializeCookie({
|
|
1079
|
+
name: TEMP_COOKIE_PKCE_VERIFIER,
|
|
1080
|
+
value: encodeURIComponent(codeVerifier),
|
|
1081
|
+
path: "/",
|
|
1082
|
+
sameSite: "None",
|
|
1083
|
+
partitioned: true,
|
|
1084
|
+
maxAge: OAUTH_TEMP_MAX_AGE_SEC
|
|
1085
|
+
})
|
|
1086
|
+
});
|
|
1087
|
+
setResponseCookie({
|
|
1088
|
+
c,
|
|
1089
|
+
value: serializeCookie({
|
|
1090
|
+
name: TEMP_COOKIE_OAUTH_STATE,
|
|
1091
|
+
value: encodeURIComponent(stateValue),
|
|
1092
|
+
path: "/",
|
|
1093
|
+
sameSite: "None",
|
|
1094
|
+
partitioned: true,
|
|
1095
|
+
maxAge: OAUTH_TEMP_MAX_AGE_SEC
|
|
1096
|
+
})
|
|
1097
|
+
});
|
|
1098
|
+
return c.json({ authorization_url: authorizeUrl.toString() });
|
|
1099
|
+
}
|
|
1100
|
+
//#endregion
|
|
1101
|
+
//#region src/server/hono/hubspot-connect-routes/auth-logout.ts
|
|
1102
|
+
async function revokeToken(options) {
|
|
1103
|
+
const { revokeEndpointUrl, body, logger } = options;
|
|
1104
|
+
try {
|
|
1105
|
+
const response = await fetch(revokeEndpointUrl, {
|
|
1106
|
+
method: "POST",
|
|
1107
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1108
|
+
body
|
|
1109
|
+
});
|
|
1110
|
+
if (!response.ok) logger.warn(`HubSpot token revoke returned HTTP ${response.status} ${response.statusText}`);
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
logger.warn("HubSpot token revoke request failed", error);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async function handleAuthLogout(c, options) {
|
|
1116
|
+
const { appKeys, refreshCookiePath, basePath, hubspotConnectEnv, logger } = options;
|
|
1117
|
+
const xForwardedProto = c.req.header("x-forwarded-proto") ?? void 0;
|
|
1118
|
+
const xForwardedHost = c.req.header("x-forwarded-host") ?? void 0;
|
|
1119
|
+
const requestHostHeader = c.req.header("host") ?? void 0;
|
|
1120
|
+
const cookies = parseCookies(c.req.header("Cookie"));
|
|
1121
|
+
const accessToken = cookies[HUBSPOT_ACCESS_TOKEN_COOKIE_NAME];
|
|
1122
|
+
const clientId = hubspotConnectEnv.isCimdEnabled ? buildCimdClientIdUrlFromRequest({
|
|
1123
|
+
requestUrl: c.req.url,
|
|
1124
|
+
basePath,
|
|
1125
|
+
xForwardedProto,
|
|
1126
|
+
xForwardedHost,
|
|
1127
|
+
requestHostHeader
|
|
1128
|
+
}) : hubspotConnectEnv.hubspotClientId;
|
|
1129
|
+
const revokeEndpointUrl = new URL("/oauth/v1/revoke", hubspotConnectEnv.hubspotOAuthApiOrigin).href;
|
|
1130
|
+
if (accessToken) if (hubspotConnectEnv.isCimdEnabled) {
|
|
1131
|
+
if (!appKeys) return c.json({ error: "Server misconfiguration: HUBSPOT_APP_PRIVATE_KEY is required when CIMD is enabled" }, 500);
|
|
1132
|
+
const clientAssertion = await buildClientAssertion({
|
|
1133
|
+
appKeys,
|
|
1134
|
+
clientId,
|
|
1135
|
+
audience: revokeEndpointUrl
|
|
1136
|
+
});
|
|
1137
|
+
await revokeToken({
|
|
1138
|
+
revokeEndpointUrl,
|
|
1139
|
+
body: new URLSearchParams({
|
|
1140
|
+
token: accessToken,
|
|
1141
|
+
token_type_hint: "access_token",
|
|
1142
|
+
client_id: clientId,
|
|
1143
|
+
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
|
1144
|
+
client_assertion: clientAssertion
|
|
1145
|
+
}),
|
|
1146
|
+
logger
|
|
1147
|
+
});
|
|
1148
|
+
} else await revokeToken({
|
|
1149
|
+
revokeEndpointUrl,
|
|
1150
|
+
body: new URLSearchParams({
|
|
1151
|
+
token: accessToken,
|
|
1152
|
+
token_type_hint: "access_token",
|
|
1153
|
+
client_id: clientId,
|
|
1154
|
+
client_secret: hubspotConnectEnv.hubspotClientSecret
|
|
1155
|
+
}),
|
|
1156
|
+
logger
|
|
1157
|
+
});
|
|
1158
|
+
setResponseCookie({
|
|
1159
|
+
c,
|
|
1160
|
+
value: serializeCookie({
|
|
1161
|
+
name: HUBSPOT_ACCESS_TOKEN_COOKIE_NAME,
|
|
1162
|
+
value: "",
|
|
1163
|
+
path: "/",
|
|
1164
|
+
sameSite: "None",
|
|
1165
|
+
partitioned: true,
|
|
1166
|
+
maxAge: 0
|
|
1167
|
+
})
|
|
1168
|
+
});
|
|
1169
|
+
setResponseCookie({
|
|
1170
|
+
c,
|
|
1171
|
+
value: serializeCookie({
|
|
1172
|
+
name: HUBSPOT_APP_SID_COOKIE_NAME,
|
|
1173
|
+
value: "",
|
|
1174
|
+
path: "/",
|
|
1175
|
+
sameSite: "None",
|
|
1176
|
+
partitioned: true,
|
|
1177
|
+
maxAge: 0
|
|
1178
|
+
})
|
|
1179
|
+
});
|
|
1180
|
+
setResponseCookie({
|
|
1181
|
+
c,
|
|
1182
|
+
value: serializeCookie({
|
|
1183
|
+
name: HUBSPOT_APP_ORIGIN_COOKIE_NAME,
|
|
1184
|
+
value: "",
|
|
1185
|
+
path: "/",
|
|
1186
|
+
sameSite: "None",
|
|
1187
|
+
partitioned: true,
|
|
1188
|
+
maxAge: 0
|
|
1189
|
+
})
|
|
1190
|
+
});
|
|
1191
|
+
Object.keys(cookies).forEach((cookieName) => {
|
|
1192
|
+
if (cookieName.startsWith("hs_refresh_")) setResponseCookie({
|
|
1193
|
+
c,
|
|
1194
|
+
value: serializeCookie({
|
|
1195
|
+
name: cookieName,
|
|
1196
|
+
value: "",
|
|
1197
|
+
path: refreshCookiePath,
|
|
1198
|
+
sameSite: "None",
|
|
1199
|
+
partitioned: true,
|
|
1200
|
+
maxAge: 0
|
|
1201
|
+
})
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1204
|
+
return c.json({ redirect_to: "/" });
|
|
1205
|
+
}
|
|
1206
|
+
//#endregion
|
|
1207
|
+
//#region src/server/hono/hubspot-connect-routes/auth-refresh.ts
|
|
1208
|
+
async function handleAuthRefresh(c, options) {
|
|
1209
|
+
const { appKeys, refreshCookiePath, basePath, hubspotConnectEnv } = options;
|
|
1210
|
+
const xForwardedProto = c.req.header("x-forwarded-proto") ?? void 0;
|
|
1211
|
+
const xForwardedHost = c.req.header("x-forwarded-host") ?? void 0;
|
|
1212
|
+
const requestHostHeader = c.req.header("host") ?? void 0;
|
|
1213
|
+
const cookies = parseCookies(c.req.header("Cookie"));
|
|
1214
|
+
const sessionId = cookies[HUBSPOT_APP_SID_COOKIE_NAME];
|
|
1215
|
+
if (!sessionId) return c.json({ error: "Missing session cookie" }, 401);
|
|
1216
|
+
if (hubspotConnectEnv.isAppPrivateKeyRequired && !appKeys) return c.json({ error: "Server misconfiguration: HUBSPOT_APP_PRIVATE_KEY is required when CIMD or DPoP is enabled" }, 500);
|
|
1217
|
+
const sidHash = await sha256base64url(sessionId);
|
|
1218
|
+
const refreshCookieName = `${HUBSPOT_REFRESH_COOKIE_PREFIX}${sidHash}`;
|
|
1219
|
+
const refreshToken = cookies[refreshCookieName];
|
|
1220
|
+
if (!refreshToken) return c.json({ error: "Missing refresh token" }, 401);
|
|
1221
|
+
const clientId = hubspotConnectEnv.isCimdEnabled ? buildCimdClientIdUrlFromRequest({
|
|
1222
|
+
requestUrl: c.req.url,
|
|
1223
|
+
basePath,
|
|
1224
|
+
xForwardedProto,
|
|
1225
|
+
xForwardedHost,
|
|
1226
|
+
requestHostHeader
|
|
1227
|
+
}) : hubspotConnectEnv.hubspotClientId;
|
|
1228
|
+
const tokenEndpointUrl = new URL("/oauth/v1/token", hubspotConnectEnv.hubspotOAuthApiOrigin).href;
|
|
1229
|
+
let dpopProof;
|
|
1230
|
+
if (hubspotConnectEnv.isDpopEnabled) dpopProof = await buildTokenEndpointDpopProof({
|
|
1231
|
+
appKeys,
|
|
1232
|
+
tokenEndpointUrl,
|
|
1233
|
+
sessionIdHash: sidHash
|
|
1234
|
+
});
|
|
1235
|
+
let formParams;
|
|
1236
|
+
if (hubspotConnectEnv.isCimdEnabled) formParams = {
|
|
1237
|
+
grant_type: "refresh_token",
|
|
1238
|
+
refresh_token: refreshToken,
|
|
1239
|
+
...buildClientAssertionFormParams({
|
|
1240
|
+
clientId,
|
|
1241
|
+
clientAssertion: await buildClientAssertion({
|
|
1242
|
+
appKeys,
|
|
1243
|
+
clientId,
|
|
1244
|
+
audience: tokenEndpointUrl
|
|
1245
|
+
})
|
|
1246
|
+
})
|
|
1247
|
+
};
|
|
1248
|
+
else formParams = {
|
|
1249
|
+
grant_type: "refresh_token",
|
|
1250
|
+
refresh_token: refreshToken,
|
|
1251
|
+
...buildClientSecretFormParams({
|
|
1252
|
+
clientId,
|
|
1253
|
+
clientSecret: hubspotConnectEnv.hubspotClientSecret
|
|
1254
|
+
})
|
|
1255
|
+
};
|
|
1256
|
+
const tokenResult = await requestOAuthToken({
|
|
1257
|
+
tokenEndpointUrl,
|
|
1258
|
+
isDpopEnabled: hubspotConnectEnv.isDpopEnabled,
|
|
1259
|
+
...dpopProof !== void 0 ? { dpopProof } : {},
|
|
1260
|
+
formParams
|
|
1261
|
+
});
|
|
1262
|
+
if (!tokenResult.ok) return c.json({ error: `Token refresh failed: ${tokenResult.errorText}` }, 502);
|
|
1263
|
+
const { access_token: newAccessToken, refresh_token: newRefreshToken, expires_in } = tokenResult.body;
|
|
1264
|
+
if (!newRefreshToken) return c.json({ error: "Token response missing refresh_token" }, 502);
|
|
1265
|
+
if (!isPositiveFiniteNumber(expires_in)) return c.json({ error: "Token response missing or invalid expires_in" }, 502);
|
|
1266
|
+
setResponseCookie({
|
|
1267
|
+
c,
|
|
1268
|
+
value: serializeCookie({
|
|
1269
|
+
name: HUBSPOT_ACCESS_TOKEN_COOKIE_NAME,
|
|
1270
|
+
value: newAccessToken,
|
|
1271
|
+
path: "/",
|
|
1272
|
+
sameSite: "None",
|
|
1273
|
+
partitioned: true,
|
|
1274
|
+
maxAge: expires_in
|
|
1275
|
+
})
|
|
1276
|
+
});
|
|
1277
|
+
setResponseCookie({
|
|
1278
|
+
c,
|
|
1279
|
+
value: serializeCookie({
|
|
1280
|
+
name: refreshCookieName,
|
|
1281
|
+
value: newRefreshToken,
|
|
1282
|
+
path: refreshCookiePath,
|
|
1283
|
+
sameSite: "None",
|
|
1284
|
+
partitioned: true,
|
|
1285
|
+
maxAge: REFRESH_COOKIE_MAX_AGE_SEC
|
|
1286
|
+
})
|
|
1287
|
+
});
|
|
1288
|
+
Object.keys(cookies).forEach((cookieName) => {
|
|
1289
|
+
if (cookieName.startsWith("hs_refresh_") && cookieName !== refreshCookieName) setResponseCookie({
|
|
1290
|
+
c,
|
|
1291
|
+
value: serializeCookie({
|
|
1292
|
+
name: cookieName,
|
|
1293
|
+
value: "",
|
|
1294
|
+
path: refreshCookiePath,
|
|
1295
|
+
sameSite: "None",
|
|
1296
|
+
partitioned: true,
|
|
1297
|
+
maxAge: 0
|
|
1298
|
+
})
|
|
1299
|
+
});
|
|
1300
|
+
});
|
|
1301
|
+
return c.json({ expires_in });
|
|
1302
|
+
}
|
|
1303
|
+
//#endregion
|
|
1304
|
+
//#region src/server/hono/hubspot-connect-routes/cimd-public-routes.ts
|
|
1305
|
+
async function handleCimdClientJson(c, options) {
|
|
1306
|
+
const { cimdClientMetadata, basePath } = options;
|
|
1307
|
+
if (!cimdClientMetadata) return c.text("CIMD client metadata is not configured", 500);
|
|
1308
|
+
const xForwardedProto = c.req.header("x-forwarded-proto") ?? void 0;
|
|
1309
|
+
const xForwardedHost = c.req.header("x-forwarded-host") ?? void 0;
|
|
1310
|
+
const requestHostHeader = c.req.header("host") ?? void 0;
|
|
1311
|
+
const forwarded = {
|
|
1312
|
+
requestUrl: c.req.url,
|
|
1313
|
+
basePath,
|
|
1314
|
+
xForwardedProto,
|
|
1315
|
+
xForwardedHost,
|
|
1316
|
+
requestHostHeader
|
|
1317
|
+
};
|
|
1318
|
+
const body = {
|
|
1319
|
+
redirect_uri: buildOAuthRedirectUriFromRequest(forwarded),
|
|
1320
|
+
jwks_uri: buildHubSpotAppJwksUrlFromRequest(forwarded),
|
|
1321
|
+
scope: cimdClientMetadata.scope
|
|
1322
|
+
};
|
|
1323
|
+
return c.text(JSON.stringify(body, null, 2), 200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1324
|
+
}
|
|
1325
|
+
async function handleCimdAppJwks(c, options) {
|
|
1326
|
+
const { appKeys, hubspotConnectEnv } = options;
|
|
1327
|
+
if (!hubspotConnectEnv.isCimdEnabled) return c.text("Not found", 404);
|
|
1328
|
+
if (!appKeys) return c.text("Missing app keys", 503);
|
|
1329
|
+
const kid = await getJwkThumbprint({ publicKeyJwk: appKeys.appPublicKeyJwk });
|
|
1330
|
+
const jwks = { keys: [{
|
|
1331
|
+
...appKeys.appPublicKeyJwk,
|
|
1332
|
+
kid,
|
|
1333
|
+
use: "sig",
|
|
1334
|
+
alg: "ES256",
|
|
1335
|
+
key_ops: ["verify"],
|
|
1336
|
+
ext: true
|
|
1337
|
+
}] };
|
|
1338
|
+
return c.json(jwks);
|
|
1339
|
+
}
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region src/server/hono/hubspot-connect-routes/hubspot-connect-routes.ts
|
|
1342
|
+
/**
|
|
1343
|
+
* Mounts hubspot-connect routes: OAuth (`/auth/...`); `GET /client.json`
|
|
1344
|
+
* from `cimdClientMetadata`; `GET /jwks.json` when CIMD is enabled.
|
|
1345
|
+
*/
|
|
1346
|
+
function registerHubSpotConnectRoutes(options) {
|
|
1347
|
+
const { app, appKeys, basePath, hubspotConnectEnv, cimdClientMetadata, logger = noopLogger } = options;
|
|
1348
|
+
if (!cimdClientMetadata) throw new Error("registerHubSpotConnectRoutes: cimdClientMetadata is required");
|
|
1349
|
+
assertHubSpotConnectCimdClientMetadata(cimdClientMetadata);
|
|
1350
|
+
const oauthRouteOptions = {
|
|
1351
|
+
appKeys,
|
|
1352
|
+
refreshCookiePath: `${basePath}/auth`,
|
|
1353
|
+
logger,
|
|
1354
|
+
basePath,
|
|
1355
|
+
hubspotConnectEnv,
|
|
1356
|
+
cimdClientMetadata
|
|
1357
|
+
};
|
|
1358
|
+
app.use("*", corsMiddleware());
|
|
1359
|
+
app.get("/client.json", (c) => handleCimdClientJson(c, oauthRouteOptions));
|
|
1360
|
+
if (hubspotConnectEnv.isCimdEnabled) app.get("/jwks.json", (c) => handleCimdAppJwks(c, oauthRouteOptions));
|
|
1361
|
+
app.get("/auth/init-session", (c) => handleAuthInitSession(c, oauthRouteOptions));
|
|
1362
|
+
app.post("/auth/complete", (c) => handleAuthComplete(c, oauthRouteOptions));
|
|
1363
|
+
app.post("/auth/refresh", (c) => handleAuthRefresh(c, oauthRouteOptions));
|
|
1364
|
+
app.post("/auth/logout", (c) => handleAuthLogout(c, oauthRouteOptions));
|
|
1365
|
+
}
|
|
1366
|
+
//#endregion
|
|
1367
|
+
//#region src/server/hono/hubspot-connect-routes/load-hubspot-connect-routes-env.ts
|
|
1368
|
+
/**
|
|
1369
|
+
* Reads hubspot-connect environment variables from `process.env` or
|
|
1370
|
+
* `Deno.env` and returns a typed object for
|
|
1371
|
+
* {@link registerHubSpotConnectRoutes}.
|
|
1372
|
+
*/
|
|
1373
|
+
function loadHubSpotConnectRoutesEnv() {
|
|
1374
|
+
const hubspotAuthorizationEndpoint = requireEnv("HUBSPOT_AUTHORIZATION_ENDPOINT");
|
|
1375
|
+
const hubspotOAuthApiOrigin = new URL(requireEnv("HUBSPOT_OAUTH_API_ORIGIN")).origin;
|
|
1376
|
+
const isCimdEnabled = isHubspotCimdEnabled();
|
|
1377
|
+
const isDpopEnabled = isHubspotDpopEnabled();
|
|
1378
|
+
const isAppPrivateKeyRequired = isCimdEnabled || isDpopEnabled;
|
|
1379
|
+
if (isCimdEnabled) return {
|
|
1380
|
+
hubspotAuthorizationEndpoint,
|
|
1381
|
+
hubspotOAuthApiOrigin,
|
|
1382
|
+
isCimdEnabled: true,
|
|
1383
|
+
isDpopEnabled,
|
|
1384
|
+
isAppPrivateKeyRequired
|
|
1385
|
+
};
|
|
1386
|
+
return {
|
|
1387
|
+
hubspotAuthorizationEndpoint,
|
|
1388
|
+
hubspotOAuthApiOrigin,
|
|
1389
|
+
isCimdEnabled: false,
|
|
1390
|
+
isDpopEnabled,
|
|
1391
|
+
isAppPrivateKeyRequired,
|
|
1392
|
+
hubspotClientId: requireEnv("HUBSPOT_CLIENT_ID"),
|
|
1393
|
+
hubspotClientSecret: requireEnv("HUBSPOT_CLIENT_SECRET")
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
//#endregion
|
|
1397
|
+
//#region src/server/lovable/hubspot-connect/run-hubspot-connect-lovable-server.ts
|
|
1398
|
+
const PORT$1 = Deno.env.get("PORT");
|
|
1399
|
+
const serveOptions$1 = typeof PORT$1 === "string" ? { port: parseInt(PORT$1, 10) } : {};
|
|
1400
|
+
const HUBSPOT_CONNECT_BASE_PATH = "/functions/v1/hubspot-connect";
|
|
1401
|
+
function runHubSpotConnectLovableServer(options) {
|
|
1402
|
+
const { appKeys, cimdClientMetadata } = options;
|
|
1403
|
+
const hubspotConnectEnv = loadHubSpotConnectRoutesEnv();
|
|
1404
|
+
const result = Deno.serve(serveOptions$1, (request) => {
|
|
1405
|
+
const app = new Hono();
|
|
1406
|
+
registerHubSpotConnectRoutes({
|
|
1407
|
+
app: app.basePath(HUBSPOT_CONNECT_BASE_PATH),
|
|
1408
|
+
appKeys,
|
|
1409
|
+
basePath: HUBSPOT_CONNECT_BASE_PATH,
|
|
1410
|
+
hubspotConnectEnv,
|
|
1411
|
+
cimdClientMetadata
|
|
1412
|
+
});
|
|
1413
|
+
return app.fetch(request);
|
|
1414
|
+
});
|
|
1415
|
+
console.log(`[hubspot-connect] Listening on ${result.addr.port}`);
|
|
1416
|
+
return Promise.resolve();
|
|
1417
|
+
}
|
|
1418
|
+
//#endregion
|
|
1419
|
+
//#region src/server/lovable/hubspot-connect/index.ts
|
|
1420
|
+
/**
|
|
1421
|
+
* Lovable-style entry point for the hubspot-connect Deno function.
|
|
1422
|
+
* Loads `HUBSPOT_APP_PRIVATE_KEY` via `secureStart`, then serves OAuth
|
|
1423
|
+
* and CIMD routes under `/functions/v1/hubspot-connect`.
|
|
1424
|
+
*/
|
|
1425
|
+
async function startHubSpotConnectFunction(options) {
|
|
1426
|
+
assertHubSpotConnectCimdClientMetadata(options.client);
|
|
1427
|
+
await secureStart(async () => ({ start: (context) => runHubSpotConnectLovableServer({
|
|
1428
|
+
...context,
|
|
1429
|
+
cimdClientMetadata: options.client
|
|
1430
|
+
}) }));
|
|
1431
|
+
}
|
|
1432
|
+
//#endregion
|
|
1433
|
+
//#region src/server/lovable/create-app-function-start.ts
|
|
1434
|
+
const PORT = Deno.env.get("PORT");
|
|
1435
|
+
const serveOptions = typeof PORT === "string" ? { port: parseInt(PORT, 10) } : {};
|
|
1436
|
+
/**
|
|
1437
|
+
* Builds a Deno-style `start({ appKeys })` entry point that boots a
|
|
1438
|
+
* Hono app under `basePath`, wires the SDK's per-request HubSpot
|
|
1439
|
+
* proxy via `createAppConnectRequestHandler`, and serves it with
|
|
1440
|
+
* `Deno.serve` on `PORT`.
|
|
1441
|
+
*/
|
|
1442
|
+
function createAppFunctionStart(options) {
|
|
1443
|
+
const { basePath, registerRoutes, logger } = options;
|
|
1444
|
+
return ({ appKeys }) => {
|
|
1445
|
+
Deno.serve(serveOptions, createAppConnectRequestHandler({
|
|
1446
|
+
appKeys,
|
|
1447
|
+
...logger !== void 0 ? { logger } : {},
|
|
1448
|
+
registerRoutes: (app) => {
|
|
1449
|
+
registerRoutes(app.basePath(basePath));
|
|
1450
|
+
}
|
|
1451
|
+
}));
|
|
1452
|
+
console.log(`[app-function ${basePath}] Listening on http://localhost:${PORT}`);
|
|
1453
|
+
return Promise.resolve();
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
//#endregion
|
|
4
1457
|
export { createAppFunctionStart, secureStart, startHubSpotConnectFunction };
|
|
1458
|
+
|
|
1459
|
+
//# sourceMappingURL=lovable.js.map
|