@teleporthq/teleport-plugin-next-workflows 0.44.11 → 0.45.0
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/__tests__/_helpers/fake-fetch.ts +75 -0
- package/__tests__/_helpers/gui-place-order-statements.ts +17 -0
- package/__tests__/_helpers/inserted-lines.ts +22 -0
- package/__tests__/_helpers/invoice-store.ts +10 -1
- package/__tests__/_helpers/load-security-runtime.ts +45 -0
- package/__tests__/_helpers/load-server-runtime.ts +26 -3
- package/__tests__/_helpers/run-account-delete-route.ts +3 -1
- package/__tests__/_helpers/run-generated-middleware.ts +7 -2
- package/__tests__/account-delete-owned-rows.test.ts +3 -1
- package/__tests__/adopted-element-trigger-code.test.ts +128 -0
- package/__tests__/analytics-track-outcome.test.ts +145 -0
- package/__tests__/auth-password-change-ends-sessions.test.ts +8 -1
- package/__tests__/auth-password-security.test.ts +2275 -0
- package/__tests__/auth-session-cost.test.ts +31 -0
- package/__tests__/auth-signup-route.test.ts +960 -0
- package/__tests__/authored-code-every-path.test.ts +247 -0
- package/__tests__/automations-bus.test.ts +3820 -0
- package/__tests__/automations-catalog-drift.test.ts +97 -0
- package/__tests__/booking-calendar-feed-route.test.ts +20 -3
- package/__tests__/cart-price-order.test.ts +336 -1
- package/__tests__/code-lookup-rate-limit-route.test.ts +189 -0
- package/__tests__/cost-bearing-node-types.test.ts +119 -0
- package/__tests__/credential-columns-reply.test.ts +9 -0
- package/__tests__/credential-result-reply.test.ts +19 -5
- package/__tests__/cron-route-loop-templateparams.test.ts +6 -3
- package/__tests__/custom-js-authored-code.test.ts +304 -0
- package/__tests__/custom-node-custom-code-gate.test.ts +136 -0
- package/__tests__/custom-node-step-handlers.test.ts +251 -0
- package/__tests__/custom-node-step-reach.test.ts +63 -0
- package/__tests__/data-api-anon-user-id-fallback.test.ts +7 -2
- package/__tests__/data-api-browser-reads.test.ts +7 -0
- package/__tests__/data-api-business-events.test.ts +403 -0
- package/__tests__/data-api-invalid-body.test.ts +7 -0
- package/__tests__/data-api-low-stock-autofire.test.ts +30 -4
- package/__tests__/data-api-optional-feature-columns.test.ts +44 -0
- package/__tests__/data-api-owner-scope-credentials.test.ts +1324 -0
- package/__tests__/data-api-protected-tables.test.ts +48 -15
- package/__tests__/data-api-retired-table-and-account-fields.test.ts +208 -0
- package/__tests__/email-locale-senders.test.ts +5 -3
- package/__tests__/expression-injection.test.ts +343 -0
- package/__tests__/fixtures/admin-file-workflows.json +242 -0
- package/__tests__/form-confirmation-email-template.test.ts +44 -0
- package/__tests__/internal-request-headers.test.ts +25 -0
- package/__tests__/invoice-pdf-service-project-header.test.ts +120 -0
- package/__tests__/invoice-self-call-origin.test.ts +57 -1
- package/__tests__/legacy-webhook-self-calls.test.ts +86 -1
- package/__tests__/middleware-home-route-protection.test.ts +3 -1
- package/__tests__/middleware-subscriber-access.test.ts +1 -1
- package/__tests__/newsletter-admin-route.test.ts +649 -0
- package/__tests__/newsletter-plugin.test.ts +1248 -0
- package/__tests__/outbound-nodes-security.test.ts +1929 -0
- package/__tests__/page-access-guard.test.ts +217 -0
- package/__tests__/page-access-route-table.test.ts +231 -0
- package/__tests__/payment-drivers-module.test.ts +23 -0
- package/__tests__/payment-legacy-webhook-routes.test.ts +100 -5
- package/__tests__/payment-webhook-route.test.ts +4 -1
- package/__tests__/paypal-webhook-signature-verification.test.ts +44 -0
- package/__tests__/place-order-guards.test.ts +506 -0
- package/__tests__/public-step-guards.test.ts +310 -0
- package/__tests__/publish-refusals.test.ts +505 -0
- package/__tests__/realtime-identity-routes.test.ts +492 -0
- package/__tests__/record-event-writer-server-only.test.ts +975 -0
- package/__tests__/resolve-config-template-params.test.ts +3 -3
- package/__tests__/runtime-storage-upload-access.test.ts +280 -17
- package/__tests__/security-integration-env-names.test.ts +74 -0
- package/__tests__/security-lists-parity.test.ts +245 -0
- package/__tests__/security-runtime-shadow.test.ts +224 -1
- package/__tests__/security-runtime.test.ts +893 -0
- package/__tests__/segment-rate-limit-error-branch.test.ts +284 -0
- package/__tests__/segment-route-abuse-gate.test.ts +309 -0
- package/__tests__/server-job-hops-real-data-api.test.ts +669 -0
- package/__tests__/server-session-identity.test.ts +522 -0
- package/__tests__/server-step-chain.test.ts +222 -0
- package/__tests__/server-step-signing.test.ts +107 -2
- package/__tests__/session-cookie-resolver.test.ts +62 -106
- package/__tests__/signup-event.test.ts +649 -0
- package/__tests__/signup-reply-identical.test.ts +144 -0
- package/__tests__/slack-message-escaping.test.ts +123 -0
- package/__tests__/visitor-step-limits.test.ts +318 -0
- package/__tests__/webhook-delivery-lease.test.ts +4 -1
- package/__tests__/webpack-safe-handlers.test.ts +57 -8
- package/__tests__/workflow-auth-guard.test.ts +9 -1
- package/dist/cjs/account-delete-route-generator.d.ts +1 -0
- package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
- package/dist/cjs/account-delete-route-generator.js +18 -3
- package/dist/cjs/account-delete-route-generator.js.map +1 -1
- package/dist/cjs/api-route-generator.d.ts +52 -3
- package/dist/cjs/api-route-generator.d.ts.map +1 -1
- package/dist/cjs/api-route-generator.js +600 -145
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/auth-generator.d.ts +126 -3
- package/dist/cjs/auth-generator.d.ts.map +1 -1
- package/dist/cjs/auth-generator.js +1353 -139
- package/dist/cjs/auth-generator.js.map +1 -1
- package/dist/cjs/automations/activity-kicks.d.ts +12 -0
- package/dist/cjs/automations/activity-kicks.d.ts.map +1 -0
- package/dist/cjs/automations/activity-kicks.js +123 -0
- package/dist/cjs/automations/activity-kicks.js.map +1 -0
- package/dist/cjs/automations/app-events-source.d.ts +33 -0
- package/dist/cjs/automations/app-events-source.d.ts.map +1 -0
- package/dist/cjs/automations/app-events-source.js +109 -0
- package/dist/cjs/automations/app-events-source.js.map +1 -0
- package/dist/cjs/automations/automations-project-plugin.d.ts +21 -0
- package/dist/cjs/automations/automations-project-plugin.d.ts.map +1 -0
- package/dist/cjs/automations/automations-project-plugin.js +102 -0
- package/dist/cjs/automations/automations-project-plugin.js.map +1 -0
- package/dist/cjs/automations/automations-scope.d.ts +104 -0
- package/dist/cjs/automations/automations-scope.d.ts.map +1 -0
- package/dist/cjs/automations/automations-scope.js +176 -0
- package/dist/cjs/automations/automations-scope.js.map +1 -0
- package/dist/cjs/automations/dispatcher-source.d.ts +72 -0
- package/dist/cjs/automations/dispatcher-source.d.ts.map +1 -0
- package/dist/cjs/automations/dispatcher-source.js +1008 -0
- package/dist/cjs/automations/dispatcher-source.js.map +1 -0
- package/dist/cjs/automations/event-bus-source.d.ts +44 -0
- package/dist/cjs/automations/event-bus-source.d.ts.map +1 -0
- package/dist/cjs/automations/event-bus-source.js +427 -0
- package/dist/cjs/automations/event-bus-source.js.map +1 -0
- package/dist/cjs/automations/handler-module-generator.d.ts +25 -0
- package/dist/cjs/automations/handler-module-generator.d.ts.map +1 -0
- package/dist/cjs/automations/handler-module-generator.js +78 -0
- package/dist/cjs/automations/handler-module-generator.js.map +1 -0
- package/dist/cjs/automations/hydrators-source.d.ts +33 -0
- package/dist/cjs/automations/hydrators-source.d.ts.map +1 -0
- package/dist/cjs/automations/hydrators-source.js +571 -0
- package/dist/cjs/automations/hydrators-source.js.map +1 -0
- package/dist/cjs/booking-calendar-feed-route-generator.d.ts.map +1 -1
- package/dist/cjs/booking-calendar-feed-route-generator.js +8 -0
- package/dist/cjs/booking-calendar-feed-route-generator.js.map +1 -1
- package/dist/cjs/configured-nextauth-url.d.ts +17 -0
- package/dist/cjs/configured-nextauth-url.d.ts.map +1 -0
- package/dist/cjs/configured-nextauth-url.js +20 -0
- package/dist/cjs/configured-nextauth-url.js.map +1 -0
- package/dist/cjs/cron-request-verification.d.ts +22 -1
- package/dist/cjs/cron-request-verification.d.ts.map +1 -1
- package/dist/cjs/cron-request-verification.js +50 -6
- package/dist/cjs/cron-request-verification.js.map +1 -1
- package/dist/cjs/data-api-route-generator.d.ts +19 -0
- package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
- package/dist/cjs/data-api-route-generator.js +711 -43
- package/dist/cjs/data-api-route-generator.js.map +1 -1
- package/dist/cjs/ecommerce/parameterized-stock-chain.d.ts +3 -1
- package/dist/cjs/ecommerce/parameterized-stock-chain.d.ts.map +1 -1
- package/dist/cjs/ecommerce/parameterized-stock-chain.js +45 -5
- package/dist/cjs/ecommerce/parameterized-stock-chain.js.map +1 -1
- package/dist/cjs/ecommerce/place-order-guards.d.ts +126 -0
- package/dist/cjs/ecommerce/place-order-guards.d.ts.map +1 -0
- package/dist/cjs/ecommerce/place-order-guards.js +670 -0
- package/dist/cjs/ecommerce/place-order-guards.js.map +1 -0
- package/dist/cjs/ecommerce-customhandler-rewriter.d.ts +2 -0
- package/dist/cjs/ecommerce-customhandler-rewriter.d.ts.map +1 -1
- package/dist/cjs/ecommerce-customhandler-rewriter.js +38 -2
- package/dist/cjs/ecommerce-customhandler-rewriter.js.map +1 -1
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +368 -60
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/graph-utils.d.ts +9 -0
- package/dist/cjs/graph-utils.d.ts.map +1 -1
- package/dist/cjs/graph-utils.js +32 -1
- package/dist/cjs/graph-utils.js.map +1 -1
- package/dist/cjs/index.d.ts +20 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +72 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/invoice/api-routes-code.d.ts +9 -3
- package/dist/cjs/invoice/api-routes-code.d.ts.map +1 -1
- package/dist/cjs/invoice/api-routes-code.js +9 -7
- package/dist/cjs/invoice/api-routes-code.js.map +1 -1
- package/dist/cjs/invoice/index.d.ts.map +1 -1
- package/dist/cjs/invoice/index.js +12 -7
- package/dist/cjs/invoice/index.js.map +1 -1
- package/dist/cjs/invoice/invoice-assembly-code.d.ts.map +1 -1
- package/dist/cjs/invoice/invoice-assembly-code.js +8 -3
- package/dist/cjs/invoice/invoice-assembly-code.js.map +1 -1
- package/dist/cjs/invoice/pdf-generator-code.d.ts.map +1 -1
- package/dist/cjs/invoice/pdf-generator-code.js +2 -1
- package/dist/cjs/invoice/pdf-generator-code.js.map +1 -1
- package/dist/cjs/invoice/pdf-service-client-code.d.ts.map +1 -1
- package/dist/cjs/invoice/pdf-service-client-code.js +15 -7
- package/dist/cjs/invoice/pdf-service-client-code.js.map +1 -1
- package/dist/cjs/newsletter/admin-route-code.d.ts +60 -0
- package/dist/cjs/newsletter/admin-route-code.d.ts.map +1 -0
- package/dist/cjs/newsletter/admin-route-code.js +300 -0
- package/dist/cjs/newsletter/admin-route-code.js.map +1 -0
- package/dist/cjs/newsletter/helper-code.d.ts +109 -0
- package/dist/cjs/newsletter/helper-code.d.ts.map +1 -0
- package/dist/cjs/newsletter/helper-code.js +662 -0
- package/dist/cjs/newsletter/helper-code.js.map +1 -0
- package/dist/cjs/newsletter/newsletter-scope.d.ts +65 -0
- package/dist/cjs/newsletter/newsletter-scope.d.ts.map +1 -0
- package/dist/cjs/newsletter/newsletter-scope.js +103 -0
- package/dist/cjs/newsletter/newsletter-scope.js.map +1 -0
- package/dist/cjs/newsletter/project-plugin.d.ts +18 -0
- package/dist/cjs/newsletter/project-plugin.d.ts.map +1 -0
- package/dist/cjs/newsletter/project-plugin.js +97 -0
- package/dist/cjs/newsletter/project-plugin.js.map +1 -0
- package/dist/cjs/newsletter/routes-code.d.ts +6 -0
- package/dist/cjs/newsletter/routes-code.d.ts.map +1 -0
- package/dist/cjs/newsletter/routes-code.js +181 -0
- package/dist/cjs/newsletter/routes-code.js.map +1 -0
- package/dist/cjs/nodes/account/account-compare-passwords.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-compare-passwords.js +16 -6
- package/dist/cjs/nodes/account/account-compare-passwords.js.map +1 -1
- package/dist/cjs/nodes/account/account-delete-current.js +1 -1
- package/dist/cjs/nodes/account/account-get-current.js +1 -1
- package/dist/cjs/nodes/account/account-hash-password.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-hash-password.js +15 -3
- package/dist/cjs/nodes/account/account-hash-password.js.map +1 -1
- package/dist/cjs/nodes/account/account-login.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-login.js +23 -6
- package/dist/cjs/nodes/account/account-login.js.map +1 -1
- package/dist/cjs/nodes/account/account-logout.js +1 -1
- package/dist/cjs/nodes/account/account-refresh-session.js +1 -1
- package/dist/cjs/nodes/account/account-request-password-reset.d.ts +3 -0
- package/dist/cjs/nodes/account/account-request-password-reset.d.ts.map +1 -0
- package/dist/cjs/nodes/account/account-request-password-reset.js +46 -0
- package/dist/cjs/nodes/account/account-request-password-reset.js.map +1 -0
- package/dist/cjs/nodes/account/account-reset-password.d.ts +3 -0
- package/dist/cjs/nodes/account/account-reset-password.d.ts.map +1 -0
- package/dist/cjs/nodes/account/account-reset-password.js +44 -0
- package/dist/cjs/nodes/account/account-reset-password.js.map +1 -0
- package/dist/cjs/nodes/account/account-signup.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-signup.js +82 -19
- package/dist/cjs/nodes/account/account-signup.js.map +1 -1
- package/dist/cjs/nodes/account/account-social-login.js +1 -1
- package/dist/cjs/nodes/ai/ai-custom-prompt.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-custom-prompt.js +20 -5
- package/dist/cjs/nodes/ai/ai-custom-prompt.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-detect-language.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-detect-language.js +20 -5
- package/dist/cjs/nodes/ai/ai-detect-language.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-generate-text-embedding.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-generate-text-embedding.js +10 -2
- package/dist/cjs/nodes/ai/ai-generate-text-embedding.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-provider-utils-sources.generated.js +2 -2
- package/dist/cjs/nodes/ai/ai-provider-utils-sources.generated.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-provider-utils.d.ts +31 -2
- package/dist/cjs/nodes/ai/ai-provider-utils.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-provider-utils.js +125 -11
- package/dist/cjs/nodes/ai/ai-provider-utils.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-select-database-data.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-select-database-data.js +6 -2
- package/dist/cjs/nodes/ai/ai-select-database-data.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-sentiment-analysis.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-sentiment-analysis.js +20 -5
- package/dist/cjs/nodes/ai/ai-sentiment-analysis.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-summarization.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-summarization.js +20 -5
- package/dist/cjs/nodes/ai/ai-summarization.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-text-classifier.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-text-classifier.js +20 -5
- package/dist/cjs/nodes/ai/ai-text-classifier.js.map +1 -1
- package/dist/cjs/nodes/ai/ai-text-transform.d.ts.map +1 -1
- package/dist/cjs/nodes/ai/ai-text-transform.js +20 -5
- package/dist/cjs/nodes/ai/ai-text-transform.js.map +1 -1
- package/dist/cjs/nodes/analytics/analytics-track-outcome.d.ts +3 -0
- package/dist/cjs/nodes/analytics/analytics-track-outcome.d.ts.map +1 -0
- package/dist/cjs/nodes/analytics/analytics-track-outcome.js +110 -0
- package/dist/cjs/nodes/analytics/analytics-track-outcome.js.map +1 -0
- package/dist/cjs/nodes/cart/cart-price-order.d.ts.map +1 -1
- package/dist/cjs/nodes/cart/cart-price-order.js +161 -2
- package/dist/cjs/nodes/cart/cart-price-order.js.map +1 -1
- package/dist/cjs/nodes/cost-bearing-node-types.d.ts +4 -0
- package/dist/cjs/nodes/cost-bearing-node-types.d.ts.map +1 -0
- package/dist/cjs/nodes/cost-bearing-node-types.js +43 -0
- package/dist/cjs/nodes/cost-bearing-node-types.js.map +1 -0
- package/dist/cjs/nodes/data/data-select.d.ts.map +1 -1
- package/dist/cjs/nodes/data/data-select.js +12 -2
- package/dist/cjs/nodes/data/data-select.js.map +1 -1
- package/dist/cjs/nodes/email/email-mailersend.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-mailersend.js +15 -11
- package/dist/cjs/nodes/email/email-mailersend.js.map +1 -1
- package/dist/cjs/nodes/email/email-mailgun.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-mailgun.js +15 -15
- package/dist/cjs/nodes/email/email-mailgun.js.map +1 -1
- package/dist/cjs/nodes/email/email-postmark.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-postmark.js +15 -15
- package/dist/cjs/nodes/email/email-postmark.js.map +1 -1
- package/dist/cjs/nodes/email/email-resend.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-resend.js +15 -15
- package/dist/cjs/nodes/email/email-resend.js.map +1 -1
- package/dist/cjs/nodes/email/email-sendgrid.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-sendgrid.js +15 -15
- package/dist/cjs/nodes/email/email-sendgrid.js.map +1 -1
- package/dist/cjs/nodes/email/html-escape-code.d.ts +3 -0
- package/dist/cjs/nodes/email/html-escape-code.d.ts.map +1 -0
- package/dist/cjs/nodes/email/html-escape-code.js +23 -0
- package/dist/cjs/nodes/email/html-escape-code.js.map +1 -0
- package/dist/cjs/nodes/email/message-trust.d.ts +40 -0
- package/dist/cjs/nodes/email/message-trust.d.ts.map +1 -0
- package/dist/cjs/nodes/email/message-trust.js +167 -0
- package/dist/cjs/nodes/email/message-trust.js.map +1 -0
- package/dist/cjs/nodes/email/outbound-message-guard.d.ts +113 -0
- package/dist/cjs/nodes/email/outbound-message-guard.d.ts.map +1 -0
- package/dist/cjs/nodes/email/outbound-message-guard.js +460 -0
- package/dist/cjs/nodes/email/outbound-message-guard.js.map +1 -0
- package/dist/cjs/nodes/email/trusted-html-code.d.ts +22 -0
- package/dist/cjs/nodes/email/trusted-html-code.d.ts.map +1 -0
- package/dist/cjs/nodes/email/trusted-html-code.js +52 -0
- package/dist/cjs/nodes/email/trusted-html-code.js.map +1 -0
- package/dist/cjs/nodes/general/general-custom-js.d.ts.map +1 -1
- package/dist/cjs/nodes/general/general-custom-js.js +44 -7
- package/dist/cjs/nodes/general/general-custom-js.js.map +1 -1
- package/dist/cjs/nodes/general/general-http-request.d.ts.map +1 -1
- package/dist/cjs/nodes/general/general-http-request.js +431 -45
- package/dist/cjs/nodes/general/general-http-request.js.map +1 -1
- package/dist/cjs/nodes/general/general-rate-limiter.d.ts.map +1 -1
- package/dist/cjs/nodes/general/general-rate-limiter.js +46 -78
- package/dist/cjs/nodes/general/general-rate-limiter.js.map +1 -1
- package/dist/cjs/nodes/general/general-wait.d.ts +3 -0
- package/dist/cjs/nodes/general/general-wait.d.ts.map +1 -0
- package/dist/cjs/nodes/general/general-wait.js +81 -0
- package/dist/cjs/nodes/general/general-wait.js.map +1 -0
- package/dist/cjs/nodes/general/visitor-request.d.ts +44 -0
- package/dist/cjs/nodes/general/visitor-request.d.ts.map +1 -0
- package/dist/cjs/nodes/general/visitor-request.js +72 -0
- package/dist/cjs/nodes/general/visitor-request.js.map +1 -0
- package/dist/cjs/nodes/index.d.ts.map +1 -1
- package/dist/cjs/nodes/index.js +7 -1
- package/dist/cjs/nodes/index.js.map +1 -1
- package/dist/cjs/nodes/integrations/integration-slack.d.ts.map +1 -1
- package/dist/cjs/nodes/integrations/integration-slack.js +17 -6
- package/dist/cjs/nodes/integrations/integration-slack.js.map +1 -1
- package/dist/cjs/nodes/newsletter/newsletter-confirm.d.ts +3 -0
- package/dist/cjs/nodes/newsletter/newsletter-confirm.d.ts.map +1 -0
- package/dist/cjs/nodes/newsletter/newsletter-confirm.js +41 -0
- package/dist/cjs/nodes/newsletter/newsletter-confirm.js.map +1 -0
- package/dist/cjs/nodes/newsletter/newsletter-subscribe.d.ts +7 -0
- package/dist/cjs/nodes/newsletter/newsletter-subscribe.d.ts.map +1 -0
- package/dist/cjs/nodes/newsletter/newsletter-subscribe.js +69 -0
- package/dist/cjs/nodes/newsletter/newsletter-subscribe.js.map +1 -0
- package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.d.ts +3 -0
- package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.d.ts.map +1 -0
- package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.js +42 -0
- package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.js.map +1 -0
- package/dist/cjs/nodes/realtime/realtime-join-channel.d.ts.map +1 -1
- package/dist/cjs/nodes/realtime/realtime-join-channel.js +5 -4
- package/dist/cjs/nodes/realtime/realtime-join-channel.js.map +1 -1
- package/dist/cjs/nodes/realtime/realtime-send-channel-event.d.ts.map +1 -1
- package/dist/cjs/nodes/realtime/realtime-send-channel-event.js +11 -2
- package/dist/cjs/nodes/realtime/realtime-send-channel-event.js.map +1 -1
- package/dist/cjs/nodes/realtime/realtime-send-channel-message.d.ts.map +1 -1
- package/dist/cjs/nodes/realtime/realtime-send-channel-message.js +11 -2
- package/dist/cjs/nodes/realtime/realtime-send-channel-message.js.map +1 -1
- package/dist/cjs/nodes/sms/sms-infobip.d.ts.map +1 -1
- package/dist/cjs/nodes/sms/sms-infobip.js +11 -8
- package/dist/cjs/nodes/sms/sms-infobip.js.map +1 -1
- package/dist/cjs/nodes/sms/sms-smsapi.d.ts.map +1 -1
- package/dist/cjs/nodes/sms/sms-smsapi.js +11 -8
- package/dist/cjs/nodes/sms/sms-smsapi.js.map +1 -1
- package/dist/cjs/nodes/sms/sms-textmagic.d.ts.map +1 -1
- package/dist/cjs/nodes/sms/sms-textmagic.js +11 -8
- package/dist/cjs/nodes/sms/sms-textmagic.js.map +1 -1
- package/dist/cjs/nodes/sms/sms-twilio.d.ts.map +1 -1
- package/dist/cjs/nodes/sms/sms-twilio.js +12 -10
- package/dist/cjs/nodes/sms/sms-twilio.js.map +1 -1
- package/dist/cjs/nodes/types.d.ts +8 -1
- package/dist/cjs/nodes/types.d.ts.map +1 -1
- package/dist/cjs/nodes/utility/utility-similarity-scoring.d.ts.map +1 -1
- package/dist/cjs/nodes/utility/utility-similarity-scoring.js +29 -10
- package/dist/cjs/nodes/utility/utility-similarity-scoring.js.map +1 -1
- package/dist/cjs/page-access-guard-generator.d.ts +23 -0
- package/dist/cjs/page-access-guard-generator.d.ts.map +1 -0
- package/dist/cjs/page-access-guard-generator.js +135 -0
- package/dist/cjs/page-access-guard-generator.js.map +1 -0
- package/dist/cjs/password-reset-route-generator.d.ts +119 -0
- package/dist/cjs/password-reset-route-generator.d.ts.map +1 -0
- package/dist/cjs/password-reset-route-generator.js +460 -0
- package/dist/cjs/password-reset-route-generator.js.map +1 -0
- package/dist/cjs/payments/payment-core-code.d.ts.map +1 -1
- package/dist/cjs/payments/payment-core-code.js +12 -7
- package/dist/cjs/payments/payment-core-code.js.map +1 -1
- package/dist/cjs/project-code-scanner.d.ts +11 -0
- package/dist/cjs/project-code-scanner.d.ts.map +1 -0
- package/dist/cjs/project-code-scanner.js +80 -0
- package/dist/cjs/project-code-scanner.js.map +1 -0
- package/dist/cjs/public-step-guards.d.ts +122 -0
- package/dist/cjs/public-step-guards.d.ts.map +1 -0
- package/dist/cjs/public-step-guards.js +288 -0
- package/dist/cjs/public-step-guards.js.map +1 -0
- package/dist/cjs/publish-refusals.d.ts +66 -0
- package/dist/cjs/publish-refusals.d.ts.map +1 -0
- package/dist/cjs/publish-refusals.js +278 -0
- package/dist/cjs/publish-refusals.js.map +1 -0
- package/dist/cjs/realtime-generator.d.ts +63 -1
- package/dist/cjs/realtime-generator.d.ts.map +1 -1
- package/dist/cjs/realtime-generator.js +404 -230
- package/dist/cjs/realtime-generator.js.map +1 -1
- package/dist/cjs/runtime-storage-generator.d.ts +32 -1
- package/dist/cjs/runtime-storage-generator.d.ts.map +1 -1
- package/dist/cjs/runtime-storage-generator.js +265 -12
- package/dist/cjs/runtime-storage-generator.js.map +1 -1
- package/dist/cjs/security-runtime/index.d.ts +14 -0
- package/dist/cjs/security-runtime/index.d.ts.map +1 -0
- package/dist/cjs/security-runtime/index.js +48 -0
- package/dist/cjs/security-runtime/index.js.map +1 -0
- package/dist/cjs/security-runtime/security-runtime-code.d.ts +53 -0
- package/dist/cjs/security-runtime/security-runtime-code.d.ts.map +1 -0
- package/dist/cjs/security-runtime/security-runtime-code.js +580 -0
- package/dist/cjs/security-runtime/security-runtime-code.js.map +1 -0
- package/dist/cjs/security-scanner.d.ts +72 -3
- package/dist/cjs/security-scanner.d.ts.map +1 -1
- package/dist/cjs/security-scanner.js +220 -8
- package/dist/cjs/security-scanner.js.map +1 -1
- package/dist/cjs/segment-splitter.d.ts.map +1 -1
- package/dist/cjs/segment-splitter.js +10 -2
- package/dist/cjs/segment-splitter.js.map +1 -1
- package/dist/cjs/segment-trust.d.ts +36 -0
- package/dist/cjs/segment-trust.d.ts.map +1 -1
- package/dist/cjs/segment-trust.js +147 -3
- package/dist/cjs/segment-trust.js.map +1 -1
- package/dist/cjs/self-call-headers.d.ts +31 -0
- package/dist/cjs/self-call-headers.d.ts.map +1 -0
- package/dist/cjs/self-call-headers.js +48 -0
- package/dist/cjs/self-call-headers.js.map +1 -0
- package/dist/cjs/sent-email-log/index.d.ts +7 -0
- package/dist/cjs/sent-email-log/index.d.ts.map +1 -1
- package/dist/cjs/sent-email-log/index.js +17 -1
- package/dist/cjs/sent-email-log/index.js.map +1 -1
- package/dist/cjs/sent-email-log/sent-email-log-code.d.ts +7 -0
- package/dist/cjs/sent-email-log/sent-email-log-code.d.ts.map +1 -1
- package/dist/cjs/sent-email-log/sent-email-log-code.js +17 -3
- package/dist/cjs/sent-email-log/sent-email-log-code.js.map +1 -1
- package/dist/cjs/server-custom-node-call.d.ts +79 -0
- package/dist/cjs/server-custom-node-call.d.ts.map +1 -0
- package/dist/cjs/server-custom-node-call.js +285 -0
- package/dist/cjs/server-custom-node-call.js.map +1 -0
- package/dist/cjs/server-runtime-code.d.ts +39 -12
- package/dist/cjs/server-runtime-code.d.ts.map +1 -1
- package/dist/cjs/server-runtime-code.js +484 -41
- package/dist/cjs/server-runtime-code.js.map +1 -1
- package/dist/cjs/session-cookie-resolver.d.ts +3 -1
- package/dist/cjs/session-cookie-resolver.d.ts.map +1 -1
- package/dist/cjs/session-revocation-generator.d.ts +39 -0
- package/dist/cjs/session-revocation-generator.d.ts.map +1 -0
- package/dist/cjs/session-revocation-generator.js +177 -0
- package/dist/cjs/session-revocation-generator.js.map +1 -0
- package/dist/cjs/slack-message-values.d.ts +6 -0
- package/dist/cjs/slack-message-values.d.ts.map +1 -0
- package/dist/cjs/slack-message-values.js +73 -0
- package/dist/cjs/slack-message-values.js.map +1 -0
- package/dist/cjs/transactional-email-code.d.ts +11 -0
- package/dist/cjs/transactional-email-code.d.ts.map +1 -1
- package/dist/cjs/transactional-email-code.js +29 -5
- package/dist/cjs/transactional-email-code.js.map +1 -1
- package/dist/cjs/trigger-generator.d.ts +2 -0
- package/dist/cjs/trigger-generator.d.ts.map +1 -1
- package/dist/cjs/trigger-generator.js +17 -4
- package/dist/cjs/trigger-generator.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/visitor-step-limits.d.ts +66 -0
- package/dist/cjs/visitor-step-limits.d.ts.map +1 -0
- package/dist/cjs/visitor-step-limits.js +187 -0
- package/dist/cjs/visitor-step-limits.js.map +1 -0
- package/dist/cjs/webhook-generator.d.ts.map +1 -1
- package/dist/cjs/webhook-generator.js +93 -15
- package/dist/cjs/webhook-generator.js.map +1 -1
- package/dist/cjs/webhook-signature-verification.d.ts.map +1 -1
- package/dist/cjs/webhook-signature-verification.js +13 -3
- package/dist/cjs/webhook-signature-verification.js.map +1 -1
- package/dist/cjs/workflow-auth-generator.d.ts +4 -1
- package/dist/cjs/workflow-auth-generator.d.ts.map +1 -1
- package/dist/cjs/workflow-auth-generator.js +8 -3
- package/dist/cjs/workflow-auth-generator.js.map +1 -1
- package/dist/cjs/workflow-component-plugin.js +11 -3
- package/dist/cjs/workflow-component-plugin.js.map +1 -1
- package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-project-plugin.js +302 -56
- package/dist/cjs/workflow-project-plugin.js.map +1 -1
- package/dist/cjs/workflow-scope.d.ts.map +1 -1
- package/dist/cjs/workflow-scope.js +5 -0
- package/dist/cjs/workflow-scope.js.map +1 -1
- package/dist/esm/account-delete-route-generator.d.ts +1 -0
- package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
- package/dist/esm/account-delete-route-generator.js +18 -3
- package/dist/esm/account-delete-route-generator.js.map +1 -1
- package/dist/esm/api-route-generator.d.ts +52 -3
- package/dist/esm/api-route-generator.d.ts.map +1 -1
- package/dist/esm/api-route-generator.js +595 -142
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/auth-generator.d.ts +126 -3
- package/dist/esm/auth-generator.d.ts.map +1 -1
- package/dist/esm/auth-generator.js +1350 -140
- package/dist/esm/auth-generator.js.map +1 -1
- package/dist/esm/automations/activity-kicks.d.ts +12 -0
- package/dist/esm/automations/activity-kicks.d.ts.map +1 -0
- package/dist/esm/automations/activity-kicks.js +118 -0
- package/dist/esm/automations/activity-kicks.js.map +1 -0
- package/dist/esm/automations/app-events-source.d.ts +33 -0
- package/dist/esm/automations/app-events-source.d.ts.map +1 -0
- package/dist/esm/automations/app-events-source.js +104 -0
- package/dist/esm/automations/app-events-source.js.map +1 -0
- package/dist/esm/automations/automations-project-plugin.d.ts +21 -0
- package/dist/esm/automations/automations-project-plugin.d.ts.map +1 -0
- package/dist/esm/automations/automations-project-plugin.js +98 -0
- package/dist/esm/automations/automations-project-plugin.js.map +1 -0
- package/dist/esm/automations/automations-scope.d.ts +104 -0
- package/dist/esm/automations/automations-scope.d.ts.map +1 -0
- package/dist/esm/automations/automations-scope.js +165 -0
- package/dist/esm/automations/automations-scope.js.map +1 -0
- package/dist/esm/automations/dispatcher-source.d.ts +72 -0
- package/dist/esm/automations/dispatcher-source.d.ts.map +1 -0
- package/dist/esm/automations/dispatcher-source.js +999 -0
- package/dist/esm/automations/dispatcher-source.js.map +1 -0
- package/dist/esm/automations/event-bus-source.d.ts +44 -0
- package/dist/esm/automations/event-bus-source.d.ts.map +1 -0
- package/dist/esm/automations/event-bus-source.js +421 -0
- package/dist/esm/automations/event-bus-source.js.map +1 -0
- package/dist/esm/automations/handler-module-generator.d.ts +25 -0
- package/dist/esm/automations/handler-module-generator.d.ts.map +1 -0
- package/dist/esm/automations/handler-module-generator.js +71 -0
- package/dist/esm/automations/handler-module-generator.js.map +1 -0
- package/dist/esm/automations/hydrators-source.d.ts +33 -0
- package/dist/esm/automations/hydrators-source.d.ts.map +1 -0
- package/dist/esm/automations/hydrators-source.js +567 -0
- package/dist/esm/automations/hydrators-source.js.map +1 -0
- package/dist/esm/booking-calendar-feed-route-generator.d.ts.map +1 -1
- package/dist/esm/booking-calendar-feed-route-generator.js +8 -0
- package/dist/esm/booking-calendar-feed-route-generator.js.map +1 -1
- package/dist/esm/configured-nextauth-url.d.ts +17 -0
- package/dist/esm/configured-nextauth-url.d.ts.map +1 -0
- package/dist/esm/configured-nextauth-url.js +17 -0
- package/dist/esm/configured-nextauth-url.js.map +1 -0
- package/dist/esm/cron-request-verification.d.ts +22 -1
- package/dist/esm/cron-request-verification.d.ts.map +1 -1
- package/dist/esm/cron-request-verification.js +48 -5
- package/dist/esm/cron-request-verification.js.map +1 -1
- package/dist/esm/data-api-route-generator.d.ts +19 -0
- package/dist/esm/data-api-route-generator.d.ts.map +1 -1
- package/dist/esm/data-api-route-generator.js +710 -43
- package/dist/esm/data-api-route-generator.js.map +1 -1
- package/dist/esm/ecommerce/parameterized-stock-chain.d.ts +3 -1
- package/dist/esm/ecommerce/parameterized-stock-chain.d.ts.map +1 -1
- package/dist/esm/ecommerce/parameterized-stock-chain.js +43 -4
- package/dist/esm/ecommerce/parameterized-stock-chain.js.map +1 -1
- package/dist/esm/ecommerce/place-order-guards.d.ts +126 -0
- package/dist/esm/ecommerce/place-order-guards.d.ts.map +1 -0
- package/dist/esm/ecommerce/place-order-guards.js +655 -0
- package/dist/esm/ecommerce/place-order-guards.js.map +1 -0
- package/dist/esm/ecommerce-customhandler-rewriter.d.ts +2 -0
- package/dist/esm/ecommerce-customhandler-rewriter.d.ts.map +1 -1
- package/dist/esm/ecommerce-customhandler-rewriter.js +38 -2
- package/dist/esm/ecommerce-customhandler-rewriter.js.map +1 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +368 -60
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/graph-utils.d.ts +9 -0
- package/dist/esm/graph-utils.d.ts.map +1 -1
- package/dist/esm/graph-utils.js +30 -0
- package/dist/esm/graph-utils.js.map +1 -1
- package/dist/esm/index.d.ts +20 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +27 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/invoice/api-routes-code.d.ts +9 -3
- package/dist/esm/invoice/api-routes-code.d.ts.map +1 -1
- package/dist/esm/invoice/api-routes-code.js +9 -7
- package/dist/esm/invoice/api-routes-code.js.map +1 -1
- package/dist/esm/invoice/index.d.ts.map +1 -1
- package/dist/esm/invoice/index.js +12 -7
- package/dist/esm/invoice/index.js.map +1 -1
- package/dist/esm/invoice/invoice-assembly-code.d.ts.map +1 -1
- package/dist/esm/invoice/invoice-assembly-code.js +8 -3
- package/dist/esm/invoice/invoice-assembly-code.js.map +1 -1
- package/dist/esm/invoice/pdf-generator-code.d.ts.map +1 -1
- package/dist/esm/invoice/pdf-generator-code.js +2 -1
- package/dist/esm/invoice/pdf-generator-code.js.map +1 -1
- package/dist/esm/invoice/pdf-service-client-code.d.ts.map +1 -1
- package/dist/esm/invoice/pdf-service-client-code.js +15 -7
- package/dist/esm/invoice/pdf-service-client-code.js.map +1 -1
- package/dist/esm/newsletter/admin-route-code.d.ts +60 -0
- package/dist/esm/newsletter/admin-route-code.d.ts.map +1 -0
- package/dist/esm/newsletter/admin-route-code.js +295 -0
- package/dist/esm/newsletter/admin-route-code.js.map +1 -0
- package/dist/esm/newsletter/helper-code.d.ts +109 -0
- package/dist/esm/newsletter/helper-code.d.ts.map +1 -0
- package/dist/esm/newsletter/helper-code.js +656 -0
- package/dist/esm/newsletter/helper-code.js.map +1 -0
- package/dist/esm/newsletter/newsletter-scope.d.ts +65 -0
- package/dist/esm/newsletter/newsletter-scope.d.ts.map +1 -0
- package/dist/esm/newsletter/newsletter-scope.js +97 -0
- package/dist/esm/newsletter/newsletter-scope.js.map +1 -0
- package/dist/esm/newsletter/project-plugin.d.ts +18 -0
- package/dist/esm/newsletter/project-plugin.d.ts.map +1 -0
- package/dist/esm/newsletter/project-plugin.js +93 -0
- package/dist/esm/newsletter/project-plugin.js.map +1 -0
- package/dist/esm/newsletter/routes-code.d.ts +6 -0
- package/dist/esm/newsletter/routes-code.d.ts.map +1 -0
- package/dist/esm/newsletter/routes-code.js +175 -0
- package/dist/esm/newsletter/routes-code.js.map +1 -0
- package/dist/esm/nodes/account/account-compare-passwords.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-compare-passwords.js +16 -6
- package/dist/esm/nodes/account/account-compare-passwords.js.map +1 -1
- package/dist/esm/nodes/account/account-delete-current.js +1 -1
- package/dist/esm/nodes/account/account-get-current.js +1 -1
- package/dist/esm/nodes/account/account-hash-password.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-hash-password.js +15 -3
- package/dist/esm/nodes/account/account-hash-password.js.map +1 -1
- package/dist/esm/nodes/account/account-login.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-login.js +23 -6
- package/dist/esm/nodes/account/account-login.js.map +1 -1
- package/dist/esm/nodes/account/account-logout.js +1 -1
- package/dist/esm/nodes/account/account-refresh-session.js +1 -1
- package/dist/esm/nodes/account/account-request-password-reset.d.ts +3 -0
- package/dist/esm/nodes/account/account-request-password-reset.d.ts.map +1 -0
- package/dist/esm/nodes/account/account-request-password-reset.js +43 -0
- package/dist/esm/nodes/account/account-request-password-reset.js.map +1 -0
- package/dist/esm/nodes/account/account-reset-password.d.ts +3 -0
- package/dist/esm/nodes/account/account-reset-password.d.ts.map +1 -0
- package/dist/esm/nodes/account/account-reset-password.js +41 -0
- package/dist/esm/nodes/account/account-reset-password.js.map +1 -0
- package/dist/esm/nodes/account/account-signup.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-signup.js +82 -19
- package/dist/esm/nodes/account/account-signup.js.map +1 -1
- package/dist/esm/nodes/account/account-social-login.js +1 -1
- package/dist/esm/nodes/ai/ai-custom-prompt.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-custom-prompt.js +20 -5
- package/dist/esm/nodes/ai/ai-custom-prompt.js.map +1 -1
- package/dist/esm/nodes/ai/ai-detect-language.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-detect-language.js +20 -5
- package/dist/esm/nodes/ai/ai-detect-language.js.map +1 -1
- package/dist/esm/nodes/ai/ai-generate-text-embedding.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-generate-text-embedding.js +10 -2
- package/dist/esm/nodes/ai/ai-generate-text-embedding.js.map +1 -1
- package/dist/esm/nodes/ai/ai-provider-utils-sources.generated.js +2 -2
- package/dist/esm/nodes/ai/ai-provider-utils-sources.generated.js.map +1 -1
- package/dist/esm/nodes/ai/ai-provider-utils.d.ts +31 -2
- package/dist/esm/nodes/ai/ai-provider-utils.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-provider-utils.js +124 -10
- package/dist/esm/nodes/ai/ai-provider-utils.js.map +1 -1
- package/dist/esm/nodes/ai/ai-select-database-data.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-select-database-data.js +6 -2
- package/dist/esm/nodes/ai/ai-select-database-data.js.map +1 -1
- package/dist/esm/nodes/ai/ai-sentiment-analysis.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-sentiment-analysis.js +20 -5
- package/dist/esm/nodes/ai/ai-sentiment-analysis.js.map +1 -1
- package/dist/esm/nodes/ai/ai-summarization.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-summarization.js +20 -5
- package/dist/esm/nodes/ai/ai-summarization.js.map +1 -1
- package/dist/esm/nodes/ai/ai-text-classifier.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-text-classifier.js +20 -5
- package/dist/esm/nodes/ai/ai-text-classifier.js.map +1 -1
- package/dist/esm/nodes/ai/ai-text-transform.d.ts.map +1 -1
- package/dist/esm/nodes/ai/ai-text-transform.js +20 -5
- package/dist/esm/nodes/ai/ai-text-transform.js.map +1 -1
- package/dist/esm/nodes/analytics/analytics-track-outcome.d.ts +3 -0
- package/dist/esm/nodes/analytics/analytics-track-outcome.d.ts.map +1 -0
- package/dist/esm/nodes/analytics/analytics-track-outcome.js +107 -0
- package/dist/esm/nodes/analytics/analytics-track-outcome.js.map +1 -0
- package/dist/esm/nodes/cart/cart-price-order.d.ts.map +1 -1
- package/dist/esm/nodes/cart/cart-price-order.js +161 -2
- package/dist/esm/nodes/cart/cart-price-order.js.map +1 -1
- package/dist/esm/nodes/cost-bearing-node-types.d.ts +4 -0
- package/dist/esm/nodes/cost-bearing-node-types.d.ts.map +1 -0
- package/dist/esm/nodes/cost-bearing-node-types.js +39 -0
- package/dist/esm/nodes/cost-bearing-node-types.js.map +1 -0
- package/dist/esm/nodes/data/data-select.d.ts.map +1 -1
- package/dist/esm/nodes/data/data-select.js +12 -2
- package/dist/esm/nodes/data/data-select.js.map +1 -1
- package/dist/esm/nodes/email/email-mailersend.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-mailersend.js +15 -11
- package/dist/esm/nodes/email/email-mailersend.js.map +1 -1
- package/dist/esm/nodes/email/email-mailgun.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-mailgun.js +15 -15
- package/dist/esm/nodes/email/email-mailgun.js.map +1 -1
- package/dist/esm/nodes/email/email-postmark.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-postmark.js +15 -15
- package/dist/esm/nodes/email/email-postmark.js.map +1 -1
- package/dist/esm/nodes/email/email-resend.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-resend.js +15 -15
- package/dist/esm/nodes/email/email-resend.js.map +1 -1
- package/dist/esm/nodes/email/email-sendgrid.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-sendgrid.js +15 -15
- package/dist/esm/nodes/email/email-sendgrid.js.map +1 -1
- package/dist/esm/nodes/email/html-escape-code.d.ts +3 -0
- package/dist/esm/nodes/email/html-escape-code.d.ts.map +1 -0
- package/dist/esm/nodes/email/html-escape-code.js +18 -0
- package/dist/esm/nodes/email/html-escape-code.js.map +1 -0
- package/dist/esm/nodes/email/message-trust.d.ts +40 -0
- package/dist/esm/nodes/email/message-trust.d.ts.map +1 -0
- package/dist/esm/nodes/email/message-trust.js +161 -0
- package/dist/esm/nodes/email/message-trust.js.map +1 -0
- package/dist/esm/nodes/email/outbound-message-guard.d.ts +113 -0
- package/dist/esm/nodes/email/outbound-message-guard.d.ts.map +1 -0
- package/dist/esm/nodes/email/outbound-message-guard.js +452 -0
- package/dist/esm/nodes/email/outbound-message-guard.js.map +1 -0
- package/dist/esm/nodes/email/trusted-html-code.d.ts +22 -0
- package/dist/esm/nodes/email/trusted-html-code.d.ts.map +1 -0
- package/dist/esm/nodes/email/trusted-html-code.js +49 -0
- package/dist/esm/nodes/email/trusted-html-code.js.map +1 -0
- package/dist/esm/nodes/general/general-custom-js.d.ts.map +1 -1
- package/dist/esm/nodes/general/general-custom-js.js +44 -7
- package/dist/esm/nodes/general/general-custom-js.js.map +1 -1
- package/dist/esm/nodes/general/general-http-request.d.ts.map +1 -1
- package/dist/esm/nodes/general/general-http-request.js +431 -45
- package/dist/esm/nodes/general/general-http-request.js.map +1 -1
- package/dist/esm/nodes/general/general-rate-limiter.d.ts.map +1 -1
- package/dist/esm/nodes/general/general-rate-limiter.js +46 -78
- package/dist/esm/nodes/general/general-rate-limiter.js.map +1 -1
- package/dist/esm/nodes/general/general-wait.d.ts +3 -0
- package/dist/esm/nodes/general/general-wait.d.ts.map +1 -0
- package/dist/esm/nodes/general/general-wait.js +78 -0
- package/dist/esm/nodes/general/general-wait.js.map +1 -0
- package/dist/esm/nodes/general/visitor-request.d.ts +44 -0
- package/dist/esm/nodes/general/visitor-request.d.ts.map +1 -0
- package/dist/esm/nodes/general/visitor-request.js +65 -0
- package/dist/esm/nodes/general/visitor-request.js.map +1 -0
- package/dist/esm/nodes/index.d.ts.map +1 -1
- package/dist/esm/nodes/index.js +7 -1
- package/dist/esm/nodes/index.js.map +1 -1
- package/dist/esm/nodes/integrations/integration-slack.d.ts.map +1 -1
- package/dist/esm/nodes/integrations/integration-slack.js +17 -6
- package/dist/esm/nodes/integrations/integration-slack.js.map +1 -1
- package/dist/esm/nodes/newsletter/newsletter-confirm.d.ts +3 -0
- package/dist/esm/nodes/newsletter/newsletter-confirm.d.ts.map +1 -0
- package/dist/esm/nodes/newsletter/newsletter-confirm.js +38 -0
- package/dist/esm/nodes/newsletter/newsletter-confirm.js.map +1 -0
- package/dist/esm/nodes/newsletter/newsletter-subscribe.d.ts +7 -0
- package/dist/esm/nodes/newsletter/newsletter-subscribe.d.ts.map +1 -0
- package/dist/esm/nodes/newsletter/newsletter-subscribe.js +66 -0
- package/dist/esm/nodes/newsletter/newsletter-subscribe.js.map +1 -0
- package/dist/esm/nodes/newsletter/newsletter-unsubscribe.d.ts +3 -0
- package/dist/esm/nodes/newsletter/newsletter-unsubscribe.d.ts.map +1 -0
- package/dist/esm/nodes/newsletter/newsletter-unsubscribe.js +39 -0
- package/dist/esm/nodes/newsletter/newsletter-unsubscribe.js.map +1 -0
- package/dist/esm/nodes/realtime/realtime-join-channel.d.ts.map +1 -1
- package/dist/esm/nodes/realtime/realtime-join-channel.js +5 -4
- package/dist/esm/nodes/realtime/realtime-join-channel.js.map +1 -1
- package/dist/esm/nodes/realtime/realtime-send-channel-event.d.ts.map +1 -1
- package/dist/esm/nodes/realtime/realtime-send-channel-event.js +11 -2
- package/dist/esm/nodes/realtime/realtime-send-channel-event.js.map +1 -1
- package/dist/esm/nodes/realtime/realtime-send-channel-message.d.ts.map +1 -1
- package/dist/esm/nodes/realtime/realtime-send-channel-message.js +11 -2
- package/dist/esm/nodes/realtime/realtime-send-channel-message.js.map +1 -1
- package/dist/esm/nodes/sms/sms-infobip.d.ts.map +1 -1
- package/dist/esm/nodes/sms/sms-infobip.js +11 -8
- package/dist/esm/nodes/sms/sms-infobip.js.map +1 -1
- package/dist/esm/nodes/sms/sms-smsapi.d.ts.map +1 -1
- package/dist/esm/nodes/sms/sms-smsapi.js +11 -8
- package/dist/esm/nodes/sms/sms-smsapi.js.map +1 -1
- package/dist/esm/nodes/sms/sms-textmagic.d.ts.map +1 -1
- package/dist/esm/nodes/sms/sms-textmagic.js +11 -8
- package/dist/esm/nodes/sms/sms-textmagic.js.map +1 -1
- package/dist/esm/nodes/sms/sms-twilio.d.ts.map +1 -1
- package/dist/esm/nodes/sms/sms-twilio.js +12 -10
- package/dist/esm/nodes/sms/sms-twilio.js.map +1 -1
- package/dist/esm/nodes/types.d.ts +8 -1
- package/dist/esm/nodes/types.d.ts.map +1 -1
- package/dist/esm/nodes/utility/utility-similarity-scoring.d.ts.map +1 -1
- package/dist/esm/nodes/utility/utility-similarity-scoring.js +29 -10
- package/dist/esm/nodes/utility/utility-similarity-scoring.js.map +1 -1
- package/dist/esm/page-access-guard-generator.d.ts +23 -0
- package/dist/esm/page-access-guard-generator.d.ts.map +1 -0
- package/dist/esm/page-access-guard-generator.js +131 -0
- package/dist/esm/page-access-guard-generator.js.map +1 -0
- package/dist/esm/password-reset-route-generator.d.ts +119 -0
- package/dist/esm/password-reset-route-generator.d.ts.map +1 -0
- package/dist/esm/password-reset-route-generator.js +448 -0
- package/dist/esm/password-reset-route-generator.js.map +1 -0
- package/dist/esm/payments/payment-core-code.d.ts.map +1 -1
- package/dist/esm/payments/payment-core-code.js +12 -7
- package/dist/esm/payments/payment-core-code.js.map +1 -1
- package/dist/esm/project-code-scanner.d.ts +11 -0
- package/dist/esm/project-code-scanner.d.ts.map +1 -0
- package/dist/esm/project-code-scanner.js +75 -0
- package/dist/esm/project-code-scanner.js.map +1 -0
- package/dist/esm/public-step-guards.d.ts +122 -0
- package/dist/esm/public-step-guards.d.ts.map +1 -0
- package/dist/esm/public-step-guards.js +280 -0
- package/dist/esm/public-step-guards.js.map +1 -0
- package/dist/esm/publish-refusals.d.ts +66 -0
- package/dist/esm/publish-refusals.d.ts.map +1 -0
- package/dist/esm/publish-refusals.js +271 -0
- package/dist/esm/publish-refusals.js.map +1 -0
- package/dist/esm/realtime-generator.d.ts +63 -1
- package/dist/esm/realtime-generator.d.ts.map +1 -1
- package/dist/esm/realtime-generator.js +401 -229
- package/dist/esm/realtime-generator.js.map +1 -1
- package/dist/esm/runtime-storage-generator.d.ts +32 -1
- package/dist/esm/runtime-storage-generator.d.ts.map +1 -1
- package/dist/esm/runtime-storage-generator.js +264 -11
- package/dist/esm/runtime-storage-generator.js.map +1 -1
- package/dist/esm/security-runtime/index.d.ts +14 -0
- package/dist/esm/security-runtime/index.d.ts.map +1 -0
- package/dist/esm/security-runtime/index.js +37 -0
- package/dist/esm/security-runtime/index.js.map +1 -0
- package/dist/esm/security-runtime/security-runtime-code.d.ts +53 -0
- package/dist/esm/security-runtime/security-runtime-code.d.ts.map +1 -0
- package/dist/esm/security-runtime/security-runtime-code.js +575 -0
- package/dist/esm/security-runtime/security-runtime-code.js.map +1 -0
- package/dist/esm/security-scanner.d.ts +72 -3
- package/dist/esm/security-scanner.d.ts.map +1 -1
- package/dist/esm/security-scanner.js +217 -8
- package/dist/esm/security-scanner.js.map +1 -1
- package/dist/esm/segment-splitter.d.ts.map +1 -1
- package/dist/esm/segment-splitter.js +10 -2
- package/dist/esm/segment-splitter.js.map +1 -1
- package/dist/esm/segment-trust.d.ts +36 -0
- package/dist/esm/segment-trust.d.ts.map +1 -1
- package/dist/esm/segment-trust.js +146 -2
- package/dist/esm/segment-trust.js.map +1 -1
- package/dist/esm/self-call-headers.d.ts +31 -0
- package/dist/esm/self-call-headers.d.ts.map +1 -0
- package/dist/esm/self-call-headers.js +43 -0
- package/dist/esm/self-call-headers.js.map +1 -0
- package/dist/esm/sent-email-log/index.d.ts +7 -0
- package/dist/esm/sent-email-log/index.d.ts.map +1 -1
- package/dist/esm/sent-email-log/index.js +15 -0
- package/dist/esm/sent-email-log/index.js.map +1 -1
- package/dist/esm/sent-email-log/sent-email-log-code.d.ts +7 -0
- package/dist/esm/sent-email-log/sent-email-log-code.d.ts.map +1 -1
- package/dist/esm/sent-email-log/sent-email-log-code.js +17 -3
- package/dist/esm/sent-email-log/sent-email-log-code.js.map +1 -1
- package/dist/esm/server-custom-node-call.d.ts +79 -0
- package/dist/esm/server-custom-node-call.d.ts.map +1 -0
- package/dist/esm/server-custom-node-call.js +278 -0
- package/dist/esm/server-custom-node-call.js.map +1 -0
- package/dist/esm/server-runtime-code.d.ts +39 -12
- package/dist/esm/server-runtime-code.d.ts.map +1 -1
- package/dist/esm/server-runtime-code.js +483 -40
- package/dist/esm/server-runtime-code.js.map +1 -1
- package/dist/esm/session-cookie-resolver.d.ts +3 -1
- package/dist/esm/session-cookie-resolver.d.ts.map +1 -1
- package/dist/esm/session-revocation-generator.d.ts +39 -0
- package/dist/esm/session-revocation-generator.d.ts.map +1 -0
- package/dist/esm/session-revocation-generator.js +172 -0
- package/dist/esm/session-revocation-generator.js.map +1 -0
- package/dist/esm/slack-message-values.d.ts +6 -0
- package/dist/esm/slack-message-values.d.ts.map +1 -0
- package/dist/esm/slack-message-values.js +68 -0
- package/dist/esm/slack-message-values.js.map +1 -0
- package/dist/esm/transactional-email-code.d.ts +11 -0
- package/dist/esm/transactional-email-code.d.ts.map +1 -1
- package/dist/esm/transactional-email-code.js +28 -4
- package/dist/esm/transactional-email-code.js.map +1 -1
- package/dist/esm/trigger-generator.d.ts +2 -0
- package/dist/esm/trigger-generator.d.ts.map +1 -1
- package/dist/esm/trigger-generator.js +15 -3
- package/dist/esm/trigger-generator.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/visitor-step-limits.d.ts +66 -0
- package/dist/esm/visitor-step-limits.d.ts.map +1 -0
- package/dist/esm/visitor-step-limits.js +181 -0
- package/dist/esm/visitor-step-limits.js.map +1 -0
- package/dist/esm/webhook-generator.d.ts.map +1 -1
- package/dist/esm/webhook-generator.js +93 -15
- package/dist/esm/webhook-generator.js.map +1 -1
- package/dist/esm/webhook-signature-verification.d.ts.map +1 -1
- package/dist/esm/webhook-signature-verification.js +13 -3
- package/dist/esm/webhook-signature-verification.js.map +1 -1
- package/dist/esm/workflow-auth-generator.d.ts +4 -1
- package/dist/esm/workflow-auth-generator.d.ts.map +1 -1
- package/dist/esm/workflow-auth-generator.js +8 -3
- package/dist/esm/workflow-auth-generator.js.map +1 -1
- package/dist/esm/workflow-component-plugin.js +12 -4
- package/dist/esm/workflow-component-plugin.js.map +1 -1
- package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-project-plugin.js +306 -60
- package/dist/esm/workflow-project-plugin.js.map +1 -1
- package/dist/esm/workflow-scope.d.ts.map +1 -1
- package/dist/esm/workflow-scope.js +5 -0
- package/dist/esm/workflow-scope.js.map +1 -1
- package/package.json +4 -4
- package/src/account-delete-route-generator.ts +28 -4
- package/src/api-route-generator.ts +712 -134
- package/src/auth-generator.ts +1471 -142
- package/src/automations/activity-kicks.ts +139 -0
- package/src/automations/app-events-source.ts +117 -0
- package/src/automations/automations-project-plugin.ts +202 -0
- package/src/automations/automations-scope.ts +196 -0
- package/src/automations/dispatcher-source.ts +1072 -0
- package/src/automations/event-bus-source.ts +428 -0
- package/src/automations/handler-module-generator.ts +107 -0
- package/src/automations/hydrators-source.ts +647 -0
- package/src/booking-calendar-feed-route-generator.ts +8 -0
- package/src/configured-nextauth-url.ts +17 -0
- package/src/cron-request-verification.ts +55 -5
- package/src/data-api-route-generator.ts +780 -43
- package/src/ecommerce/parameterized-stock-chain.ts +52 -5
- package/src/ecommerce/place-order-guards.ts +785 -0
- package/src/ecommerce-customhandler-rewriter.ts +55 -2
- package/src/executor-generator.ts +372 -60
- package/src/graph-utils.ts +33 -0
- package/src/index.ts +70 -0
- package/src/invoice/api-routes-code.ts +20 -7
- package/src/invoice/index.ts +11 -6
- package/src/invoice/invoice-assembly-code.ts +8 -3
- package/src/invoice/pdf-generator-code.ts +2 -1
- package/src/invoice/pdf-service-client-code.ts +15 -7
- package/src/newsletter/admin-route-code.ts +319 -0
- package/src/newsletter/helper-code.ts +748 -0
- package/src/newsletter/newsletter-scope.ts +116 -0
- package/src/newsletter/project-plugin.ts +119 -0
- package/src/newsletter/routes-code.ts +186 -0
- package/src/nodes/account/account-compare-passwords.ts +17 -6
- package/src/nodes/account/account-delete-current.ts +1 -1
- package/src/nodes/account/account-get-current.ts +1 -1
- package/src/nodes/account/account-hash-password.ts +19 -3
- package/src/nodes/account/account-login.ts +24 -7
- package/src/nodes/account/account-logout.ts +1 -1
- package/src/nodes/account/account-refresh-session.ts +1 -1
- package/src/nodes/account/account-request-password-reset.ts +47 -0
- package/src/nodes/account/account-reset-password.ts +45 -0
- package/src/nodes/account/account-signup.ts +86 -20
- package/src/nodes/account/account-social-login.ts +1 -1
- package/src/nodes/ai/ai-custom-prompt.ts +25 -6
- package/src/nodes/ai/ai-detect-language.ts +25 -6
- package/src/nodes/ai/ai-generate-text-embedding.ts +16 -3
- package/src/nodes/ai/ai-provider-utils-sources.generated.ts +2 -2
- package/src/nodes/ai/ai-provider-utils.ts +135 -10
- package/src/nodes/ai/ai-select-database-data.ts +8 -3
- package/src/nodes/ai/ai-sentiment-analysis.ts +25 -6
- package/src/nodes/ai/ai-summarization.ts +25 -6
- package/src/nodes/ai/ai-text-classifier.ts +25 -6
- package/src/nodes/ai/ai-text-transform.ts +25 -6
- package/src/nodes/analytics/analytics-track-outcome.ts +111 -0
- package/src/nodes/cart/cart-price-order.ts +200 -2
- package/src/nodes/cost-bearing-node-types.ts +45 -0
- package/src/nodes/data/data-select.ts +11 -2
- package/src/nodes/email/email-mailersend.ts +16 -8
- package/src/nodes/email/email-mailgun.ts +17 -12
- package/src/nodes/email/email-postmark.ts +17 -12
- package/src/nodes/email/email-resend.ts +17 -12
- package/src/nodes/email/email-sendgrid.ts +17 -12
- package/src/nodes/email/html-escape-code.ts +21 -0
- package/src/nodes/email/message-trust.ts +198 -0
- package/src/nodes/email/outbound-message-guard.ts +572 -0
- package/src/nodes/email/trusted-html-code.ts +49 -0
- package/src/nodes/general/general-custom-js.ts +46 -7
- package/src/nodes/general/general-http-request.ts +449 -46
- package/src/nodes/general/general-rate-limiter.ts +54 -81
- package/src/nodes/general/general-wait.ts +80 -0
- package/src/nodes/general/visitor-request.ts +79 -0
- package/src/nodes/index.ts +12 -0
- package/src/nodes/integrations/integration-slack.ts +21 -6
- package/src/nodes/newsletter/newsletter-confirm.ts +44 -0
- package/src/nodes/newsletter/newsletter-subscribe.ts +78 -0
- package/src/nodes/newsletter/newsletter-unsubscribe.ts +43 -0
- package/src/nodes/realtime/realtime-join-channel.ts +5 -4
- package/src/nodes/realtime/realtime-send-channel-event.ts +13 -2
- package/src/nodes/realtime/realtime-send-channel-message.ts +13 -2
- package/src/nodes/sms/sms-infobip.ts +14 -4
- package/src/nodes/sms/sms-smsapi.ts +14 -4
- package/src/nodes/sms/sms-textmagic.ts +14 -4
- package/src/nodes/sms/sms-twilio.ts +14 -4
- package/src/nodes/types.ts +9 -1
- package/src/nodes/utility/utility-similarity-scoring.ts +33 -12
- package/src/page-access-guard-generator.ts +154 -0
- package/src/password-reset-route-generator.ts +531 -0
- package/src/payments/payment-core-code.ts +12 -7
- package/src/project-code-scanner.ts +98 -0
- package/src/public-step-guards.ts +448 -0
- package/src/publish-refusals.ts +459 -0
- package/src/realtime-generator.ts +457 -234
- package/src/runtime-storage-generator.ts +270 -11
- package/src/security-runtime/index.ts +53 -0
- package/src/security-runtime/security-runtime-code.ts +579 -0
- package/src/security-scanner.ts +253 -10
- package/src/segment-splitter.ts +10 -2
- package/src/segment-trust.ts +205 -2
- package/src/self-call-headers.ts +46 -0
- package/src/sent-email-log/index.ts +16 -0
- package/src/sent-email-log/sent-email-log-code.ts +30 -4
- package/src/server-custom-node-call.ts +331 -0
- package/src/server-runtime-code.ts +488 -40
- package/src/session-revocation-generator.ts +177 -0
- package/src/slack-message-values.ts +84 -0
- package/src/transactional-email-code.ts +30 -4
- package/src/trigger-generator.ts +19 -3
- package/src/visitor-step-limits.ts +228 -0
- package/src/webhook-generator.ts +94 -14
- package/src/webhook-signature-verification.ts +13 -3
- package/src/workflow-auth-generator.ts +9 -4
- package/src/workflow-component-plugin.ts +14 -4
- package/src/workflow-project-plugin.ts +374 -61
- package/src/workflow-scope.ts +6 -0
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateSessionProviderWrapper = exports.generateNextAuthUrlGuardModule = exports.generateMiddlewareFile = exports.generateSignupRouteFile = exports.generateNextAuthRouteFile = exports.generateHashPasswordFile = exports.generateAuthDbHealthFile = exports.generateAuthErrorMessagesFile = exports.generateAuthOptionsFile = exports.buildSessionUserFields = exports.SENSITIVE_USER_FIELDS = exports.SESSION_SAFE_USER_FIELDS = exports.USER_REFRESH_INTERVAL_MS = exports.getDatabaseDriverDependencies = void 0;
|
|
3
|
+
exports.generateSessionProviderWrapper = exports.generateNextAuthUrlGuardModule = exports.generateMiddlewareFile = exports.generateSignupRouteFile = exports.KNOWN_ADDRESS_MIN_RESPONSE_MS = exports.resolveSignupFields = exports.isPrivilegedUserField = exports.generateAccountEventRecorder = exports.generateNextAuthRouteFile = exports.generateHashPasswordFile = exports.MAX_PASSWORD_BYTES = exports.MIN_PASSWORD_LENGTH = exports.generateAuthDbHealthFile = exports.generateAuthErrorMessagesFile = exports.oauthIdsNeedNamespace = exports.generateAuthOptionsFile = exports.PASSWORD_RESETS_TABLE = exports.SIGNUP_OWN_COLUMNS = exports.buildSessionUserFields = exports.SENSITIVE_USER_FIELDS = exports.SESSION_SAFE_USER_FIELDS = exports.USER_REFRESH_INTERVAL_MS = exports.getDatabaseDriverDependencies = exports.SIGN_UP_WINDOW_SECONDS = exports.EXISTING_ACCOUNT_EMAIL_LIMIT = exports.SIGN_UP_LIMIT = exports.SIGN_IN_VISITOR_FAILURE_LIMIT = exports.SIGN_IN_WINDOW_SECONDS = exports.SIGN_IN_ATTEMPT_LIMIT = exports.SIGN_IN_FAILURE_LIMIT = void 0;
|
|
4
4
|
const transactional_email_code_1 = require("./transactional-email-code");
|
|
5
|
+
const password_reset_route_generator_1 = require("./password-reset-route-generator");
|
|
5
6
|
const teleport_shared_1 = require("@teleporthq/teleport-shared");
|
|
6
7
|
const session_cookie_resolver_1 = require("./session-cookie-resolver");
|
|
8
|
+
const automations_scope_1 = require("./automations/automations-scope");
|
|
9
|
+
const session_revocation_generator_1 = require("./session-revocation-generator");
|
|
10
|
+
const security_runtime_code_1 = require("./security-runtime/security-runtime-code");
|
|
11
|
+
/**
|
|
12
|
+
* Credentials sign-in throttle: a visitor's next sign-in for an email is
|
|
13
|
+
* refused (429) once this many failures for it are in the window, or once it
|
|
14
|
+
* was tried SIGN_IN_ATTEMPT_LIMIT times in the window whatever the outcome.
|
|
15
|
+
*/
|
|
16
|
+
exports.SIGN_IN_FAILURE_LIMIT = 10;
|
|
17
|
+
exports.SIGN_IN_ATTEMPT_LIMIT = 30;
|
|
18
|
+
exports.SIGN_IN_WINDOW_SECONDS = 900;
|
|
19
|
+
/** Failed sign-ins one visitor may make across every email in the window. */
|
|
20
|
+
exports.SIGN_IN_VISITOR_FAILURE_LIMIT = 50;
|
|
21
|
+
/** Sign-ups per visitor per hour. */
|
|
22
|
+
exports.SIGN_UP_LIMIT = 10;
|
|
23
|
+
/**
|
|
24
|
+
* "You already have an account" (or, for an address without a password,
|
|
25
|
+
* "Set your password") emails one address can be sent per hour.
|
|
26
|
+
*/
|
|
27
|
+
exports.EXISTING_ACCOUNT_EMAIL_LIMIT = 3;
|
|
28
|
+
exports.SIGN_UP_WINDOW_SECONDS = 3600;
|
|
7
29
|
// GUI provider id → the actual next-auth v4 provider MODULE name, for the few
|
|
8
30
|
// cases where they differ. Everything else uses the id as the module name.
|
|
9
31
|
// (Providers next-auth v4 cannot supply are not offered by the GUI; if one
|
|
@@ -195,8 +217,6 @@ exports.SESSION_SAFE_USER_FIELDS = [
|
|
|
195
217
|
'roleName',
|
|
196
218
|
'roles',
|
|
197
219
|
];
|
|
198
|
-
/** A `users` key the session derives the visitor's role from, in any case. */
|
|
199
|
-
const ROLE_BEARING_USER_KEY = /^(roles?|role_?name)$/i;
|
|
200
220
|
/**
|
|
201
221
|
* Credentials that must never leave the server, whatever the `users` table
|
|
202
222
|
* happens to declare: the OAuth single-table adapter's provider tokens plus the
|
|
@@ -231,6 +251,12 @@ exports.SENSITIVE_USER_FIELDS = [
|
|
|
231
251
|
* it is kept out of `SENSITIVE_USER_FIELDS`.
|
|
232
252
|
*/
|
|
233
253
|
const ID_ALIAS_FIELD = '_id';
|
|
254
|
+
/**
|
|
255
|
+
* The Newsletter's own `users` columns (`newsletter_status`, …). A sign-in
|
|
256
|
+
* snapshot of them goes stale the moment the person confirms or unsubscribes,
|
|
257
|
+
* so they never reach the session: a page reads the row.
|
|
258
|
+
*/
|
|
259
|
+
const NEWSLETTER_COLUMN_PREFIX = 'newsletter_';
|
|
234
260
|
/**
|
|
235
261
|
* The exact set of `users` columns this project allows into the session.
|
|
236
262
|
*
|
|
@@ -243,7 +269,9 @@ const ID_ALIAS_FIELD = '_id';
|
|
|
243
269
|
* properties without a matching `tables` entry.
|
|
244
270
|
*/
|
|
245
271
|
const buildSessionUserFields = (tables, customProps) => {
|
|
246
|
-
const declared = ((tables === null || tables === void 0 ? void 0 : tables.users) || [])
|
|
272
|
+
const declared = ((tables === null || tables === void 0 ? void 0 : tables.users) || [])
|
|
273
|
+
.map((column) => column.name)
|
|
274
|
+
.filter((name) => !name.startsWith(NEWSLETTER_COLUMN_PREFIX));
|
|
247
275
|
const custom = customProps.map((prop) => prop.key);
|
|
248
276
|
const denied = new Set(exports.SENSITIVE_USER_FIELDS);
|
|
249
277
|
return Array.from(new Set([...exports.SESSION_SAFE_USER_FIELDS, ...declared, ...custom])).filter((field) => Boolean(field) && field !== ID_ALIAS_FIELD && !denied.has(field));
|
|
@@ -324,6 +352,45 @@ const generateCustomPropAirtableEntries = (props) => {
|
|
|
324
352
|
.map((p) => ` ${p.key}: userData.${p.key} != null ? userData.${p.key} : '',`)
|
|
325
353
|
.join('\n'));
|
|
326
354
|
};
|
|
355
|
+
/**
|
|
356
|
+
* The `users` columns a sign-up writes itself, never from its form: where the
|
|
357
|
+
* person came from (`source = 'signup'`) and their consent as given
|
|
358
|
+
* (`marketing_consent`, from the "Email me news and offers" box). On the
|
|
359
|
+
* Postgres family `createUser` writes each one it is given, and creates the
|
|
360
|
+
* account without them on a users table made before they existed.
|
|
361
|
+
*/
|
|
362
|
+
exports.SIGNUP_OWN_COLUMNS = ['source', 'marketing_consent'];
|
|
363
|
+
/**
|
|
364
|
+
* `findSignupUserByEmail(email)`: the sign-up's own existence check, blind to
|
|
365
|
+
* letter case, so `Ava@site.test` signing up reaches the `ava@site.test` row
|
|
366
|
+
* (a subscriber, a guest, an account) instead of making a second one. The
|
|
367
|
+
* exact address first; else the one row equal to it in any case, none when
|
|
368
|
+
* that is ambiguous. Sign-in keeps its exact `findUserByEmail`. On the other
|
|
369
|
+
* stores it is that exact lookup.
|
|
370
|
+
*/
|
|
371
|
+
const generateSignupLookupFunction = (dataSourceType) => {
|
|
372
|
+
switch (dataSourceType) {
|
|
373
|
+
case 'teleport':
|
|
374
|
+
case 'postgresql':
|
|
375
|
+
case 'cockroachdb':
|
|
376
|
+
return `async function findSignupUserByEmail(email) {
|
|
377
|
+
const client = getClient();
|
|
378
|
+
try {
|
|
379
|
+
await client.connect();
|
|
380
|
+
const exact = await client.query('SELECT * FROM users WHERE email = $1 LIMIT 1', [email]);
|
|
381
|
+
if (exact.rows && exact.rows.length > 0) return exact.rows[0];
|
|
382
|
+
const anyCase = await client.query('SELECT * FROM users WHERE LOWER(email) = LOWER($1) LIMIT 2', [email]);
|
|
383
|
+
return anyCase.rows && anyCase.rows.length === 1 ? anyCase.rows[0] : null;
|
|
384
|
+
} finally {
|
|
385
|
+
try { await client.end(); } catch (_e) {}
|
|
386
|
+
}
|
|
387
|
+
}`;
|
|
388
|
+
default:
|
|
389
|
+
return `async function findSignupUserByEmail(email) {
|
|
390
|
+
return findUserByEmail(email);
|
|
391
|
+
}`;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
327
394
|
const generateFindUserFunction = (dataSourceType, customProps) => {
|
|
328
395
|
if (!dataSourceType) {
|
|
329
396
|
return `async function findUserByEmail() { return null; }
|
|
@@ -337,6 +404,9 @@ async function userExistsByEmail() { return false; }`;
|
|
|
337
404
|
const customValues = generateCustomPropValues(customProps);
|
|
338
405
|
const docEntries = generateCustomPropDocEntries(customProps);
|
|
339
406
|
const airtableEntries = generateCustomPropAirtableEntries(customProps);
|
|
407
|
+
// A declared account property of the same name is already one of the columns.
|
|
408
|
+
const customKeys = new Set(customProps.map((prop) => prop.key));
|
|
409
|
+
const signupOwnColumns = exports.SIGNUP_OWN_COLUMNS.filter((column) => !customKeys.has(column));
|
|
340
410
|
switch (dataSourceType) {
|
|
341
411
|
case 'teleport':
|
|
342
412
|
case 'postgresql':
|
|
@@ -352,14 +422,44 @@ async function userExistsByEmail() { return false; }`;
|
|
|
352
422
|
}
|
|
353
423
|
}
|
|
354
424
|
|
|
425
|
+
// What a sign-up writes besides its form (where the person came from, their
|
|
426
|
+
// consent as given), each when it is given. A users table made before one of
|
|
427
|
+
// them lacks it (the column is added when a feature that reads it is switched
|
|
428
|
+
// on): the account is then created without them.
|
|
429
|
+
const SIGNUP_OWN_COLUMNS = ${JSON.stringify(signupOwnColumns)};
|
|
430
|
+
|
|
355
431
|
async function createUser(userData) {
|
|
356
432
|
const client = getClient();
|
|
357
433
|
try {
|
|
358
434
|
await client.connect();
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
435
|
+
const values = [userData.name || null, userData.email, userData.password, userData.role || 'user', userData.image || null${customValues}];
|
|
436
|
+
const insert = function (extra) {
|
|
437
|
+
let columns = '';
|
|
438
|
+
let placeholders = '';
|
|
439
|
+
for (let i = 0; i < extra.length; i++) {
|
|
440
|
+
columns += ', ' + extra[i];
|
|
441
|
+
placeholders += ', $' + (values.length + i + 1);
|
|
442
|
+
}
|
|
443
|
+
return client.query(
|
|
444
|
+
'INSERT INTO users (name, email, password, role, image${sqlCols}' + columns + ') VALUES ($1, $2, $3, $4, $5${pgPlaceholders}' + placeholders + ') RETURNING *',
|
|
445
|
+
values.concat(extra.map(function (column) { return userData[column]; }))
|
|
446
|
+
);
|
|
447
|
+
};
|
|
448
|
+
let extra = SIGNUP_OWN_COLUMNS.filter(function (column) { return userData[column] !== undefined; });
|
|
449
|
+
let result = null;
|
|
450
|
+
while (!result) {
|
|
451
|
+
try {
|
|
452
|
+
result = await insert(extra);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
// 42703 names a column the table does not have. Only that one is left
|
|
455
|
+
// out, and only when it is one of these: the others are still written.
|
|
456
|
+
const named = err && err.code === '42703' ? /column "([^"]+)"/.exec(String(err.message || '')) : null;
|
|
457
|
+
const missing = named ? named[1] : '';
|
|
458
|
+
if (!missing || extra.indexOf(missing) === -1) { throw err; }
|
|
459
|
+
console.warn('[auth] the users table has no ' + missing + ' column yet: the account is created without it');
|
|
460
|
+
extra = extra.filter(function (column) { return column !== missing; });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
363
463
|
return result.rows && result.rows.length > 0 ? result.rows[0] : null;
|
|
364
464
|
} finally {
|
|
365
465
|
try { await client.end(); } catch (_e) {}
|
|
@@ -560,6 +660,269 @@ async function createUser() { return null; }
|
|
|
560
660
|
async function userExistsByEmail() { return false; }`;
|
|
561
661
|
}
|
|
562
662
|
};
|
|
663
|
+
/** Where the password-reset links live: hashed, single-use, per account id. */
|
|
664
|
+
exports.PASSWORD_RESETS_TABLE = 'teleport_password_resets';
|
|
665
|
+
const PG_PASSWORD_RESET_FUNCTIONS = `
|
|
666
|
+
// Password-reset links (pages/api/auth/forgot-password + reset-password). Only
|
|
667
|
+
// the SHA-256 of a link's token is stored, bound to the account id, with its
|
|
668
|
+
// expiry; the table is created on first use. The earlier password_reset_tokens
|
|
669
|
+
// table and its rows are never read.
|
|
670
|
+
var PASSWORD_RESETS_TABLE = '${exports.PASSWORD_RESETS_TABLE}';
|
|
671
|
+
var __passwordResetsTableReady = false;
|
|
672
|
+
|
|
673
|
+
async function ensurePasswordResetsTable(client) {
|
|
674
|
+
if (__passwordResetsTableReady) return;
|
|
675
|
+
await client.query('BEGIN');
|
|
676
|
+
try {
|
|
677
|
+
await client.query('SELECT pg_advisory_xact_lock(hashtext($1))', [PASSWORD_RESETS_TABLE]);
|
|
678
|
+
await client.query(
|
|
679
|
+
'CREATE TABLE IF NOT EXISTS ' + PASSWORD_RESETS_TABLE + ' (token_hash TEXT PRIMARY KEY, user_id TEXT NOT NULL, ' +
|
|
680
|
+
'expires_at TIMESTAMPTZ NOT NULL, used_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW())'
|
|
681
|
+
);
|
|
682
|
+
await client.query('CREATE INDEX IF NOT EXISTS ' + PASSWORD_RESETS_TABLE + '_user_id_idx ON ' + PASSWORD_RESETS_TABLE + ' (user_id)');
|
|
683
|
+
await client.query('COMMIT');
|
|
684
|
+
} catch (err) {
|
|
685
|
+
try { await client.query('ROLLBACK'); } catch (_e) {}
|
|
686
|
+
throw err;
|
|
687
|
+
}
|
|
688
|
+
__passwordResetsTableReady = true;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// One live link per account: a new one ends the ones sent before it.
|
|
692
|
+
async function storePasswordReset(userId, tokenHash, ttlSeconds) {
|
|
693
|
+
const client = getClient();
|
|
694
|
+
try {
|
|
695
|
+
await client.connect();
|
|
696
|
+
await ensurePasswordResetsTable(client);
|
|
697
|
+
await client.query(
|
|
698
|
+
'UPDATE ' + PASSWORD_RESETS_TABLE + ' SET used_at = NOW() WHERE user_id = $1 AND used_at IS NULL',
|
|
699
|
+
[String(userId)]
|
|
700
|
+
);
|
|
701
|
+
await client.query(
|
|
702
|
+
'INSERT INTO ' + PASSWORD_RESETS_TABLE + " (token_hash, user_id, expires_at) VALUES ($1, $2, NOW() + ($3::int * INTERVAL '1 second'))",
|
|
703
|
+
[tokenHash, String(userId), ttlSeconds]
|
|
704
|
+
);
|
|
705
|
+
await client.query(
|
|
706
|
+
'DELETE FROM ' + PASSWORD_RESETS_TABLE + " WHERE expires_at < NOW() - INTERVAL '1 day'"
|
|
707
|
+
);
|
|
708
|
+
} finally {
|
|
709
|
+
try { await client.end(); } catch (_e) {}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Spends the link and sets the password in ONE statement: the link must be
|
|
714
|
+
// unspent and unexpired, and is spent by this call alone (a second call with the
|
|
715
|
+
// same token finds it used). Returns the account it was for, or null.
|
|
716
|
+
async function consumePasswordReset(tokenHash, passwordHash) {
|
|
717
|
+
const client = getClient();
|
|
718
|
+
try {
|
|
719
|
+
await client.connect();
|
|
720
|
+
const result = await client.query(
|
|
721
|
+
'WITH consumed AS (' +
|
|
722
|
+
' UPDATE ' + PASSWORD_RESETS_TABLE + ' SET used_at = NOW()' +
|
|
723
|
+
' WHERE token_hash = $1 AND used_at IS NULL AND expires_at > NOW()' +
|
|
724
|
+
' RETURNING user_id' +
|
|
725
|
+
'), changed AS (' +
|
|
726
|
+
' UPDATE users SET password = $2 FROM consumed WHERE users.id::text = consumed.user_id' +
|
|
727
|
+
' RETURNING users.id, users.email' +
|
|
728
|
+
') SELECT id, email FROM changed',
|
|
729
|
+
[tokenHash, passwordHash]
|
|
730
|
+
);
|
|
731
|
+
return result.rows && result.rows.length > 0 ? result.rows[0] : null;
|
|
732
|
+
} catch (err) {
|
|
733
|
+
// 42P01: no link was ever sent, so the table is not there yet.
|
|
734
|
+
if (err && err.code === '42P01') return null;
|
|
735
|
+
throw err;
|
|
736
|
+
} finally {
|
|
737
|
+
try { await client.end(); } catch (_e) {}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
async function passwordResetIsLive(tokenHash) {
|
|
742
|
+
const client = getClient();
|
|
743
|
+
try {
|
|
744
|
+
await client.connect();
|
|
745
|
+
const result = await client.query(
|
|
746
|
+
'SELECT 1 FROM ' + PASSWORD_RESETS_TABLE + ' WHERE token_hash = $1 AND used_at IS NULL AND expires_at > NOW() LIMIT 1',
|
|
747
|
+
[tokenHash]
|
|
748
|
+
);
|
|
749
|
+
return !!(result.rows && result.rows.length > 0);
|
|
750
|
+
} catch (err) {
|
|
751
|
+
if (err && err.code === '42P01') return false;
|
|
752
|
+
throw err;
|
|
753
|
+
} finally {
|
|
754
|
+
try { await client.end(); } catch (_e) {}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const PASSWORD_RESET_SUPPORTED = true;`;
|
|
759
|
+
const UNSUPPORTED_PASSWORD_RESET_FUNCTIONS = `
|
|
760
|
+
// Password-reset links need a Postgres database; this project's user store is
|
|
761
|
+
// not one, so the reset routes answer without ever sending a link.
|
|
762
|
+
async function storePasswordReset() { throw new Error('Password reset needs a Postgres database'); }
|
|
763
|
+
async function consumePasswordReset() { return null; }
|
|
764
|
+
async function passwordResetIsLive() { return false; }
|
|
765
|
+
const PASSWORD_RESET_SUPPORTED = false;`;
|
|
766
|
+
/**
|
|
767
|
+
* `updateUserPasswordHash(user, newHash, oldHash)` — the sign-in moving a hash
|
|
768
|
+
* onto the project's own pepper. It writes only over the hash it verified, so a
|
|
769
|
+
* reset that landed in between is never undone. Plus the password-reset link
|
|
770
|
+
* store (Postgres family only).
|
|
771
|
+
*/
|
|
772
|
+
const generatePasswordStoreFunctions = (dataSourceType) => {
|
|
773
|
+
switch (dataSourceType) {
|
|
774
|
+
case 'teleport':
|
|
775
|
+
case 'postgresql':
|
|
776
|
+
case 'cockroachdb':
|
|
777
|
+
return `// The account a stamped session was issued for (session-revocation). A value
|
|
778
|
+
// that is not an id of this table's type (22P02) is no account; any other
|
|
779
|
+
// error is thrown, so the session check fails closed.
|
|
780
|
+
async function findUserById(id) {
|
|
781
|
+
const client = getClient();
|
|
782
|
+
try {
|
|
783
|
+
await client.connect();
|
|
784
|
+
const result = await client.query('SELECT * FROM users WHERE id = $1 LIMIT 1', [id]);
|
|
785
|
+
return result.rows && result.rows.length > 0 ? result.rows[0] : null;
|
|
786
|
+
} catch (err) {
|
|
787
|
+
if (err && err.code === '22P02') return null;
|
|
788
|
+
throw err;
|
|
789
|
+
} finally {
|
|
790
|
+
try { await client.end(); } catch (_e) {}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
795
|
+
const client = getClient();
|
|
796
|
+
try {
|
|
797
|
+
await client.connect();
|
|
798
|
+
const result = await client.query('UPDATE users SET password = $1 WHERE id = $2 AND password = $3', [newHash, user.id, oldHash]);
|
|
799
|
+
return !!(result && result.rowCount > 0);
|
|
800
|
+
} finally {
|
|
801
|
+
try { await client.end(); } catch (_e) {}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
${PG_PASSWORD_RESET_FUNCTIONS}`;
|
|
805
|
+
case 'supabase':
|
|
806
|
+
return `async function findUserById(id) {
|
|
807
|
+
const client = getSupabaseClient();
|
|
808
|
+
const result = await client.from('users').select('*').eq('id', id).maybeSingle();
|
|
809
|
+
if (result.error) throw new Error(result.error.message || 'users lookup failed');
|
|
810
|
+
return result.data || null;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
814
|
+
const client = getSupabaseClient();
|
|
815
|
+
const result = await client.from('users').update({ password: newHash }).eq('id', user.id).eq('password', oldHash).select('id');
|
|
816
|
+
return !!(result.data && result.data.length > 0);
|
|
817
|
+
}
|
|
818
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
819
|
+
case 'mysql':
|
|
820
|
+
case 'mariadb':
|
|
821
|
+
case 'tidb':
|
|
822
|
+
return `async function findUserById(id) {
|
|
823
|
+
const conn = await getConnection();
|
|
824
|
+
try {
|
|
825
|
+
const [rows] = await conn.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [id]);
|
|
826
|
+
return rows && rows.length > 0 ? rows[0] : null;
|
|
827
|
+
} finally {
|
|
828
|
+
try { await conn.end(); } catch (_e) {}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
833
|
+
const conn = await getConnection();
|
|
834
|
+
try {
|
|
835
|
+
const [result] = await conn.execute('UPDATE users SET password = ? WHERE id = ? AND password = ?', [newHash, user.id, oldHash]);
|
|
836
|
+
return !!(result && result.affectedRows > 0);
|
|
837
|
+
} finally {
|
|
838
|
+
try { await conn.end(); } catch (_e) {}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
842
|
+
case 'mongodb':
|
|
843
|
+
return `async function findUserById(id) {
|
|
844
|
+
const client = await _mongoClientPromise;
|
|
845
|
+
const db = client.db();
|
|
846
|
+
const ObjectId = require('mongodb').ObjectId;
|
|
847
|
+
if (!ObjectId.isValid(String(id))) return null;
|
|
848
|
+
return await db.collection('users').findOne({ _id: new ObjectId(String(id)) });
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
852
|
+
const client = await _mongoClientPromise;
|
|
853
|
+
const db = client.db();
|
|
854
|
+
const result = await db.collection('users').updateOne({ _id: user._id, password: oldHash }, { $set: { password: newHash } });
|
|
855
|
+
return !!(result && result.modifiedCount > 0);
|
|
856
|
+
}
|
|
857
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
858
|
+
case 'firestore':
|
|
859
|
+
return `async function findUserById(id) {
|
|
860
|
+
const snap = await firestoreDb.collection('users').doc(String(id)).get();
|
|
861
|
+
return snap.exists ? Object.assign({ id: snap.id }, snap.data()) : null;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
865
|
+
const ref = firestoreDb.collection('users').doc(String(user.id));
|
|
866
|
+
return firestoreDb.runTransaction(async function (tx) {
|
|
867
|
+
const snap = await tx.get(ref);
|
|
868
|
+
if (!snap.exists || snap.data().password !== oldHash) return false;
|
|
869
|
+
tx.update(ref, { password: newHash });
|
|
870
|
+
return true;
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
874
|
+
case 'turso':
|
|
875
|
+
return `async function findUserById(id) {
|
|
876
|
+
const result = await tursoClient.execute({ sql: 'SELECT * FROM users WHERE id = ? LIMIT 1', args: [id] });
|
|
877
|
+
return result.rows && result.rows.length > 0 ? result.rows[0] : null;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
881
|
+
const result = await tursoClient.execute({ sql: 'UPDATE users SET password = ? WHERE id = ? AND password = ?', args: [newHash, user.id, oldHash] });
|
|
882
|
+
return !!(result && result.rowsAffected > 0);
|
|
883
|
+
}
|
|
884
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
885
|
+
case 'airtable':
|
|
886
|
+
return `async function findUserById(id) {
|
|
887
|
+
return new Promise(function(resolve, reject) {
|
|
888
|
+
airtableBase('users').find(String(id), function(err, rec) {
|
|
889
|
+
if (err && (err.statusCode === 404 || err.error === 'NOT_FOUND')) { resolve(null); return; }
|
|
890
|
+
if (err) { reject(err); return; }
|
|
891
|
+
resolve(rec ? Object.assign({ id: rec.id }, rec.fields) : null);
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
async function updateUserPasswordHash(user, newHash, oldHash) {
|
|
897
|
+
const current = await findUserByEmail(user.email);
|
|
898
|
+
if (!current || current.password !== oldHash) return false;
|
|
899
|
+
await new Promise(function(resolve, reject) {
|
|
900
|
+
airtableBase('users').update(String(user.id), { password: newHash }, function(err) { if (err) { reject(err); } else { resolve(); } });
|
|
901
|
+
});
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
905
|
+
default:
|
|
906
|
+
return `async function findUserById() { return null; }
|
|
907
|
+
async function updateUserPasswordHash() { return false; }
|
|
908
|
+
${UNSUPPORTED_PASSWORD_RESET_FUNCTIONS}`;
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
/** Microsoft provider ids whose email claim is only trusted for one tenant. */
|
|
912
|
+
const MICROSOFT_PROVIDER_IDS = new Set(['azure-ad', 'microsoft-entra-id']);
|
|
913
|
+
/** The credential key naming a Microsoft provider's tenant (`*_TENANT_ID`, or Entra's `*_ISSUER`). */
|
|
914
|
+
const microsoftTenantKeyOf = (provider) => Object.keys(provider.credentials || {}).find((key) => key.endsWith('_TENANT_ID') || key.endsWith('_ISSUER'));
|
|
915
|
+
/** provider id → env var of its configured tenant, for each Microsoft provider. */
|
|
916
|
+
const microsoftTenantEnvKeys = (auth) => {
|
|
917
|
+
const keys = {};
|
|
918
|
+
for (const provider of auth.providers || []) {
|
|
919
|
+
const key = MICROSOFT_PROVIDER_IDS.has(provider.id) ? microsoftTenantKeyOf(provider) : undefined;
|
|
920
|
+
if (key) {
|
|
921
|
+
keys[provider.id] = key;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return keys;
|
|
925
|
+
};
|
|
563
926
|
// Only the CredentialsProvider is imported at module scope (it always exists).
|
|
564
927
|
// OAuth providers are required lazily inside generateProvidersSetup.
|
|
565
928
|
const generateProviderImports = (auth) => {
|
|
@@ -597,11 +960,11 @@ const generateProvidersSetup = (auth) => {
|
|
|
597
960
|
//
|
|
598
961
|
// Unknown email and wrong password deliberately share one code: telling them
|
|
599
962
|
// apart is a user-enumeration oracle.
|
|
600
|
-
async authorize(credentials) {
|
|
963
|
+
async authorize(credentials, req) {
|
|
601
964
|
if (!credentials || !credentials.email || !credentials.password) {
|
|
602
965
|
throw new Error('MissingCredentials');
|
|
603
966
|
}
|
|
604
|
-
const {
|
|
967
|
+
const { checkPassword, hashPassword } = require('./hash-password');
|
|
605
968
|
const { classifyAuthInfrastructureError } = require('./db-health');
|
|
606
969
|
let user;
|
|
607
970
|
try {
|
|
@@ -611,22 +974,35 @@ const generateProvidersSetup = (auth) => {
|
|
|
611
974
|
console.error('[auth] Could not reach the user store (' + kind + '):', err && err.message ? err.message : err);
|
|
612
975
|
throw new Error('ServiceUnavailable');
|
|
613
976
|
}
|
|
977
|
+
let verdict;
|
|
614
978
|
try {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
const signedIn = sanitizeUser(user);
|
|
620
|
-
signedIn[TOKEN_PASSWORD_FINGERPRINT] = passwordFingerprint(user.password);
|
|
621
|
-
return signedIn;
|
|
979
|
+
// No account, or one without a password, is checked too (against a
|
|
980
|
+
// stand-in hash): every failure costs what a wrong password costs.
|
|
981
|
+
verdict = checkPassword(String(credentials.password), user ? user.password : null);
|
|
622
982
|
} catch (err) {
|
|
623
|
-
if (err && err.message === 'InvalidCredentials') throw err;
|
|
624
983
|
// A hashing/serialisation fault is an infrastructure problem, not a
|
|
625
984
|
// wrong password — saying "check your details" would send the user
|
|
626
985
|
// round in circles.
|
|
627
986
|
console.error('[auth] Credential check failed:', err && err.message ? err.message : err);
|
|
628
987
|
throw new Error('ServiceUnavailable');
|
|
629
988
|
}
|
|
989
|
+
if (!verdict.ok) {
|
|
990
|
+
await countSignInFailure(req, credentials.email);
|
|
991
|
+
throw new Error('InvalidCredentials');
|
|
992
|
+
}
|
|
993
|
+
// A hash still made with the old pepper moves onto the project's own.
|
|
994
|
+
let storedHash = user.password;
|
|
995
|
+
if (verdict.needsRehash) {
|
|
996
|
+
try {
|
|
997
|
+
const rehashed = hashPassword(String(credentials.password));
|
|
998
|
+
if (await updateUserPasswordHash(user, rehashed, storedHash)) {
|
|
999
|
+
storedHash = rehashed;
|
|
1000
|
+
}
|
|
1001
|
+
} catch (err) {
|
|
1002
|
+
console.error('[auth] Could not move the password onto the current pepper:', err && err.message ? err.message : err);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return withSessionStamp(sanitizeUser(user), Object.assign({}, user, { password: storedHash }));
|
|
630
1006
|
}
|
|
631
1007
|
}));`);
|
|
632
1008
|
}
|
|
@@ -640,7 +1016,12 @@ const generateProvidersSetup = (auth) => {
|
|
|
640
1016
|
` clientId: process.env.${idKey}`,
|
|
641
1017
|
` clientSecret: process.env.${secretKey}`,
|
|
642
1018
|
];
|
|
643
|
-
if (issuerKey) {
|
|
1019
|
+
if (provider.id === 'azure-ad' && issuerKey && issuerKey.endsWith('_TENANT_ID')) {
|
|
1020
|
+
// next-auth's Azure AD provider takes the tenant as tenantId ("common",
|
|
1021
|
+
// every tenant, when absent).
|
|
1022
|
+
cfg.push(` tenantId: process.env.${issuerKey} || undefined`);
|
|
1023
|
+
}
|
|
1024
|
+
else if (issuerKey) {
|
|
644
1025
|
cfg.push(` issuer: process.env.${issuerKey}`);
|
|
645
1026
|
}
|
|
646
1027
|
lines.push(`try {
|
|
@@ -661,16 +1042,34 @@ const generateAuthOptionsFile = (auth, dataSourceConfig) => {
|
|
|
661
1042
|
const providerImports = generateProviderImports(auth);
|
|
662
1043
|
const providersSetup = generateProvidersSetup(auth);
|
|
663
1044
|
const findUserCode = auth.passwordAuthEnabled
|
|
664
|
-
? generateFindUserFunction(auth.dataSourceType, customProps)
|
|
1045
|
+
? `${generateFindUserFunction(auth.dataSourceType, customProps)}
|
|
1046
|
+
|
|
1047
|
+
${generateSignupLookupFunction(auth.dataSourceType)}`
|
|
665
1048
|
: '';
|
|
666
1049
|
const sanitizeUserCode = generateSanitizeUserFunction(auth.tables, customProps);
|
|
667
1050
|
const signInRoute = ((_a = auth.authPages.signIn) === null || _a === void 0 ? void 0 : _a.route) || '/auth/sign-in';
|
|
1051
|
+
// Password sign-in brings the local account a session can be revoked from;
|
|
1052
|
+
// with an OAuth provider beside it, an OAuth sign-in may become that account.
|
|
1053
|
+
const withPassword = !!auth.passwordAuthEnabled;
|
|
1054
|
+
// Every OAuth project checks whether the provider vouches for the email: an
|
|
1055
|
+
// unvouched email never is the visitor's identity, whether or not a local
|
|
1056
|
+
// account could be adopted (R15).
|
|
1057
|
+
const withOAuthBinding = (auth.providers || []).length > 0;
|
|
1058
|
+
const passwordSessionCode = withPassword
|
|
1059
|
+
? `${generatePasswordStoreFunctions(auth.dataSourceType)}
|
|
1060
|
+
|
|
1061
|
+
${CREDENTIAL_SESSION_HELPERS}
|
|
1062
|
+
`
|
|
1063
|
+
: '';
|
|
1064
|
+
const oauthBindingCode = withOAuthBinding
|
|
1065
|
+
? `${generateOAuthEmailVerificationHelpers(auth)}\n`
|
|
1066
|
+
: '';
|
|
668
1067
|
return `${providerImports}
|
|
669
1068
|
${dbSetupCode}
|
|
670
1069
|
${sanitizeUserCode}
|
|
671
1070
|
|
|
672
1071
|
${findUserCode}
|
|
673
|
-
|
|
1072
|
+
${passwordSessionCode}${oauthBindingCode}
|
|
674
1073
|
${providersSetup}
|
|
675
1074
|
|
|
676
1075
|
// How long a JWT may serve profile fields before the next /api/auth/session
|
|
@@ -721,7 +1120,25 @@ const authOptions = {
|
|
|
721
1120
|
for (let i = 0; i < keys.length; i++) {
|
|
722
1121
|
token[keys[i]] = user[keys[i]];
|
|
723
1122
|
}
|
|
724
|
-
|
|
1123
|
+
${withOAuthBinding
|
|
1124
|
+
? ` // An OAuth sign-in becomes the local account with its email only when
|
|
1125
|
+
// the provider vouches for that email; otherwise it stays a session of
|
|
1126
|
+
// its own, without the account's id or role.
|
|
1127
|
+
if (params.account && params.account.type !== 'credentials') {
|
|
1128
|
+
if (!(await oauthEmailIsVerified(params.account, params.profile, token))) {
|
|
1129
|
+
asDistinctOAuthIdentity(token, params.account);
|
|
1130
|
+
}${withPassword
|
|
1131
|
+
? ` else {
|
|
1132
|
+
// Vouched for: the next refresh adopts the local account with that
|
|
1133
|
+
// email (its id and role). Until then — and for good when there is
|
|
1134
|
+
// none — the id is the provider's, namespaced where it could equal
|
|
1135
|
+
// a local id.
|
|
1136
|
+
namespaceOAuthId(token, params.account);
|
|
1137
|
+
}`
|
|
1138
|
+
: ''}
|
|
1139
|
+
}
|
|
1140
|
+
`
|
|
1141
|
+
: ''} // Deliberately NOT stamped. An OAuth \`user\` is the PROVIDER's profile
|
|
725
1142
|
// (id/name/email/image) — it carries no \`role\`, which only exists on
|
|
726
1143
|
// the \`users\` row. Stamping here would make the first session request
|
|
727
1144
|
// after an OAuth sign-in skip the database and leave the visitor
|
|
@@ -730,7 +1147,13 @@ const authOptions = {
|
|
|
730
1147
|
// exactly as it does today, and the interval applies from there on.
|
|
731
1148
|
return token;
|
|
732
1149
|
}
|
|
733
|
-
|
|
1150
|
+
${withOAuthBinding
|
|
1151
|
+
? ` // A session the provider did not vouch for never takes a local account.
|
|
1152
|
+
if (token && token[OAUTH_UNVERIFIED]) {
|
|
1153
|
+
return token;
|
|
1154
|
+
}
|
|
1155
|
+
`
|
|
1156
|
+
: ''} // Subsequent calls (every /api/auth/session): re-read the user from the
|
|
734
1157
|
// database so profile edits — name, image, role, etc. — propagate into the
|
|
735
1158
|
// session. The JWT is otherwise a snapshot captured at login, which is why
|
|
736
1159
|
// the navbar avatar/name reverted to the old value after a refresh: the
|
|
@@ -751,35 +1174,17 @@ const authOptions = {
|
|
|
751
1174
|
if (params.trigger !== 'update' && isFresh) {
|
|
752
1175
|
return token;
|
|
753
1176
|
}
|
|
754
|
-
|
|
755
|
-
try {
|
|
756
|
-
if (token && token.email && typeof findUserByEmail === 'function') {
|
|
757
|
-
const fresh = await findUserByEmail(String(token.email));
|
|
758
|
-
const safe = fresh && typeof sanitizeUser === 'function' ? sanitizeUser(fresh) : null;
|
|
759
|
-
if (safe && typeof safe === 'object') {
|
|
760
|
-
const fk = Object.keys(safe);
|
|
761
|
-
for (let i = 0; i < fk.length; i++) {
|
|
762
|
-
token[fk[i]] = safe[fk[i]];
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
if (fresh) {
|
|
766
|
-
const currentFingerprint = passwordFingerprint(fresh.password);
|
|
767
|
-
if (typeof token[TOKEN_PASSWORD_FINGERPRINT] !== 'string') {
|
|
768
|
-
token[TOKEN_PASSWORD_FINGERPRINT] = currentFingerprint;
|
|
769
|
-
} else if (token[TOKEN_PASSWORD_FINGERPRINT] !== currentFingerprint) {
|
|
770
|
-
passwordChanged = true;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
} catch (e) {
|
|
1177
|
+
${withPassword ? PASSWORD_REFRESH_CODE : PLAIN_REFRESH_CODE} } catch (e) {
|
|
775
1178
|
// Keep the existing token on any DB hiccup — never sign the user out.
|
|
776
1179
|
}
|
|
777
|
-
|
|
1180
|
+
${withPassword
|
|
1181
|
+
? ` if (revoked) {
|
|
778
1182
|
// NextAuth answers a jwt callback that throws by clearing the session
|
|
779
|
-
// cookie
|
|
780
|
-
throw new Error('
|
|
1183
|
+
// cookie: the visitor is signed out.
|
|
1184
|
+
throw new Error('SessionRevoked');
|
|
781
1185
|
}
|
|
782
|
-
|
|
1186
|
+
`
|
|
1187
|
+
: ''} // Stamped even when the read threw, so an unreachable database costs one
|
|
783
1188
|
// attempt per interval instead of one per request.
|
|
784
1189
|
if (token) {
|
|
785
1190
|
token[TOKEN_REFRESH_STAMP] = now;
|
|
@@ -793,7 +1198,7 @@ const authOptions = {
|
|
|
793
1198
|
const skip = { iat: 1, exp: 1, jti: 1, sub: 1 };
|
|
794
1199
|
skip[TOKEN_REFRESH_STAMP] = 1;
|
|
795
1200
|
skip[TOKEN_PASSWORD_FINGERPRINT] = 1;
|
|
796
|
-
const keys = Object.keys(token);
|
|
1201
|
+
${withPassword ? ` skip['${session_revocation_generator_1.SESSION_STAMP_KEY}'] = 1;\n` : ''}${withOAuthBinding ? ' skip[OAUTH_UNVERIFIED] = 1;\n' : ''} const keys = Object.keys(token);
|
|
797
1202
|
for (let i = 0; i < keys.length; i++) {
|
|
798
1203
|
if (!skip[keys[i]]) {
|
|
799
1204
|
session.user[keys[i]] = token[keys[i]];
|
|
@@ -809,14 +1214,398 @@ const authOptions = {
|
|
|
809
1214
|
secret: process.env.NEXTAUTH_SECRET,
|
|
810
1215
|
};
|
|
811
1216
|
|
|
1217
|
+
// On https the session token and the callback-url cookie are "__Host-" cookies
|
|
1218
|
+
// (Path=/, Secure, HttpOnly, SameSite=Lax, no Domain): only this site can set
|
|
1219
|
+
// them. Published sites share a parent domain that is not a public suffix, so a
|
|
1220
|
+
// sibling site could otherwise plant a session cookie this site reads (cookie
|
|
1221
|
+
// tossing: the visitor silently signed in to someone else's account). Every
|
|
1222
|
+
// reader in the app reads this one name (session-cookie-resolver). NextAuth
|
|
1223
|
+
// reads the option on every request, after the auth route has set
|
|
1224
|
+
// NEXTAUTH_URL to the request's origin; on http (a local server) its defaults stay.
|
|
1225
|
+
var SECURE_SESSION_COOKIES = {
|
|
1226
|
+
sessionToken: {
|
|
1227
|
+
name: '${teleport_shared_1.SessionCookieResolver.SECURE_SESSION_COOKIE_NAME}',
|
|
1228
|
+
options: { httpOnly: true, sameSite: 'lax', path: '/', secure: true },
|
|
1229
|
+
},
|
|
1230
|
+
callbackUrl: {
|
|
1231
|
+
name: '${teleport_shared_1.SessionCookieResolver.SECURE_CALLBACK_URL_COOKIE_NAME}',
|
|
1232
|
+
options: { httpOnly: true, sameSite: 'lax', path: '/', secure: true },
|
|
1233
|
+
},
|
|
1234
|
+
};
|
|
1235
|
+
Object.defineProperty(authOptions, 'cookies', {
|
|
1236
|
+
enumerable: true,
|
|
1237
|
+
get: function () {
|
|
1238
|
+
return String(process.env.NEXTAUTH_URL || '').indexOf('https://') === 0 ? SECURE_SESSION_COOKIES : undefined;
|
|
1239
|
+
},
|
|
1240
|
+
});
|
|
1241
|
+
|
|
812
1242
|
module.exports = authOptions;
|
|
813
1243
|
module.exports.sanitizeUser = sanitizeUser;
|
|
814
1244
|
${auth.passwordAuthEnabled
|
|
815
|
-
? `module.exports.findUserByEmail = findUserByEmail;\nmodule.exports.createUser = createUser;\nmodule.exports.userExistsByEmail = userExistsByEmail;\n`
|
|
1245
|
+
? `module.exports.findUserByEmail = findUserByEmail;\nmodule.exports.findSignupUserByEmail = findSignupUserByEmail;\nmodule.exports.createUser = createUser;\nmodule.exports.userExistsByEmail = userExistsByEmail;\n`
|
|
1246
|
+
: ''}${withPassword
|
|
1247
|
+
? `module.exports.findUserById = findUserById;
|
|
1248
|
+
module.exports.checkSignInAllowed = checkSignInAllowed;
|
|
1249
|
+
module.exports.countSignInFailure = countSignInFailure;
|
|
1250
|
+
module.exports.SIGN_IN_VISITOR_HEADER = SIGN_IN_VISITOR_HEADER;
|
|
1251
|
+
module.exports.storePasswordReset = storePasswordReset;
|
|
1252
|
+
module.exports.consumePasswordReset = consumePasswordReset;
|
|
1253
|
+
module.exports.passwordResetIsLive = passwordResetIsLive;
|
|
1254
|
+
module.exports.PASSWORD_RESET_SUPPORTED = PASSWORD_RESET_SUPPORTED;
|
|
1255
|
+
`
|
|
816
1256
|
: ''}
|
|
817
1257
|
`;
|
|
818
1258
|
};
|
|
819
1259
|
exports.generateAuthOptionsFile = generateAuthOptionsFile;
|
|
1260
|
+
// The jwt callback's refresh without password sign-in (no local lookup exists).
|
|
1261
|
+
const PLAIN_REFRESH_CODE = ` try {
|
|
1262
|
+
if (token && token.email && typeof findUserByEmail === 'function') {
|
|
1263
|
+
const fresh = await findUserByEmail(String(token.email));
|
|
1264
|
+
const safe = fresh && typeof sanitizeUser === 'function' ? sanitizeUser(fresh) : null;
|
|
1265
|
+
if (safe && typeof safe === 'object') {
|
|
1266
|
+
const fk = Object.keys(safe);
|
|
1267
|
+
for (let i = 0; i < fk.length; i++) {
|
|
1268
|
+
token[fk[i]] = safe[fk[i]];
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
`;
|
|
1273
|
+
// With password sign-in: a stamped session follows its account BY ID (the key
|
|
1274
|
+
// every consumer acts on), so a deleted account or a changed password ends it
|
|
1275
|
+
// even after an email change; an unstamped one (an OAuth sign-in, a token from
|
|
1276
|
+
// before stamps) adopts the local account by its email.
|
|
1277
|
+
const PASSWORD_REFRESH_CODE = ` let revoked = false;
|
|
1278
|
+
try {
|
|
1279
|
+
const stamped = !!token && typeof token['${session_revocation_generator_1.SESSION_STAMP_KEY}'] === 'string';
|
|
1280
|
+
const accountId = token && (token.id != null ? token.id : token.sub);
|
|
1281
|
+
let fresh = null;
|
|
1282
|
+
let looked = false;
|
|
1283
|
+
if (stamped && typeof findUserById === 'function') {
|
|
1284
|
+
fresh = accountId != null ? await findUserById(String(accountId)) : null;
|
|
1285
|
+
looked = true;
|
|
1286
|
+
} else if (token && token.email && typeof findUserByEmail === 'function') {
|
|
1287
|
+
fresh = await findUserByEmail(String(token.email));
|
|
1288
|
+
looked = true;
|
|
1289
|
+
}
|
|
1290
|
+
if (looked) {
|
|
1291
|
+
// The account's password (or session_version) changed since this
|
|
1292
|
+
// token was stamped, or the account is gone: the session is over.
|
|
1293
|
+
// Otherwise the token takes the stamp of the account as it is now.
|
|
1294
|
+
revoked = sessionStampCheck(token, fresh) === 'revoked';
|
|
1295
|
+
// The same end, for a token issued before stamps: a short fingerprint
|
|
1296
|
+
// of the stored hash, never the hash. A change (a reset included)
|
|
1297
|
+
// signs that session out at its next refresh.
|
|
1298
|
+
if (!revoked && fresh) {
|
|
1299
|
+
const currentFingerprint = passwordFingerprint(fresh.password);
|
|
1300
|
+
if (typeof token[TOKEN_PASSWORD_FINGERPRINT] !== 'string') {
|
|
1301
|
+
token[TOKEN_PASSWORD_FINGERPRINT] = currentFingerprint;
|
|
1302
|
+
} else if (token[TOKEN_PASSWORD_FINGERPRINT] !== currentFingerprint) {
|
|
1303
|
+
revoked = true;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
const safe = !revoked && fresh && typeof sanitizeUser === 'function' ? sanitizeUser(fresh) : null;
|
|
1307
|
+
if (safe && typeof safe === 'object') {
|
|
1308
|
+
const fk = Object.keys(safe);
|
|
1309
|
+
for (let i = 0; i < fk.length; i++) {
|
|
1310
|
+
token[fk[i]] = safe[fk[i]];
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
`;
|
|
1315
|
+
/**
|
|
1316
|
+
* auth-options helpers for password sign-in: the sign-in throttle (failures
|
|
1317
|
+
* per visitor and email, counted after a wrong password, checked by the
|
|
1318
|
+
* NextAuth route before the next attempt) and the session stamp
|
|
1319
|
+
* (`utils/auth/session-revocation.js`).
|
|
1320
|
+
*/
|
|
1321
|
+
const CREDENTIAL_SESSION_HELPERS = `// Sign-in throttle. The NextAuth route refuses a visitor's next sign-in for an
|
|
1322
|
+
// email once SIGN_IN_FAILURE_LIMIT failures for it are in the window, once the
|
|
1323
|
+
// visitor has SIGN_IN_VISITOR_FAILURE_LIMIT failures across all emails, or once
|
|
1324
|
+
// they tried it SIGN_IN_ATTEMPT_LIMIT times (so a burst of parallel guesses
|
|
1325
|
+
// cannot all start before the first failure is counted). The route writes who
|
|
1326
|
+
// the visitor is on the request (SIGN_IN_VISITOR_HEADER, replacing whatever the
|
|
1327
|
+
// request carried), because NextAuth hands authorize() the headers only.
|
|
1328
|
+
var SIGN_IN_VISITOR_HEADER = 'x-teleport-signin-visitor';
|
|
1329
|
+
var SIGN_IN_FAILURE_SCOPE = 'auth-signin-failures';
|
|
1330
|
+
var SIGN_IN_FAILURE_LIMIT = ${exports.SIGN_IN_FAILURE_LIMIT};
|
|
1331
|
+
var SIGN_IN_ATTEMPT_SCOPE = 'auth-signin-attempts';
|
|
1332
|
+
var SIGN_IN_ATTEMPT_LIMIT = ${exports.SIGN_IN_ATTEMPT_LIMIT};
|
|
1333
|
+
var SIGN_IN_WINDOW_SECONDS = ${exports.SIGN_IN_WINDOW_SECONDS};
|
|
1334
|
+
// Failures one visitor may make across every email (credential stuffing tries
|
|
1335
|
+
// many addresses once each), counted the same way: after a failure only.
|
|
1336
|
+
var SIGN_IN_VISITOR_FAILURE_SCOPE = 'auth-signin-visitor-failures';
|
|
1337
|
+
var SIGN_IN_VISITOR_FAILURE_LIMIT = ${exports.SIGN_IN_VISITOR_FAILURE_LIMIT};
|
|
1338
|
+
|
|
1339
|
+
function signInVisitorRequest(req) {
|
|
1340
|
+
var headers = (req && req.headers) || {};
|
|
1341
|
+
var visitor = headers[SIGN_IN_VISITOR_HEADER];
|
|
1342
|
+
return { headers: {}, socket: { remoteAddress: typeof visitor === 'string' ? visitor : '' } };
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
function signInEmailKey(email) {
|
|
1346
|
+
return String(email == null ? '' : email).trim().toLowerCase();
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// { allowed, retryAfter } for the sign-in the request is about to make.
|
|
1350
|
+
async function checkSignInAllowed(req) {
|
|
1351
|
+
var security = require('../security-runtime');
|
|
1352
|
+
var visitor = signInVisitorRequest(req);
|
|
1353
|
+
var email = signInEmailKey(req && req.body ? req.body.email : '');
|
|
1354
|
+
var failures = await security.peekRateLimitRequest(visitor, {
|
|
1355
|
+
scope: SIGN_IN_FAILURE_SCOPE,
|
|
1356
|
+
limit: SIGN_IN_FAILURE_LIMIT,
|
|
1357
|
+
windowSeconds: SIGN_IN_WINDOW_SECONDS,
|
|
1358
|
+
keyParts: [email],
|
|
1359
|
+
});
|
|
1360
|
+
if (!failures.allowed) {
|
|
1361
|
+
return failures;
|
|
1362
|
+
}
|
|
1363
|
+
var visitorFailures = await security.peekRateLimitRequest(visitor, {
|
|
1364
|
+
scope: SIGN_IN_VISITOR_FAILURE_SCOPE,
|
|
1365
|
+
limit: SIGN_IN_VISITOR_FAILURE_LIMIT,
|
|
1366
|
+
windowSeconds: SIGN_IN_WINDOW_SECONDS,
|
|
1367
|
+
});
|
|
1368
|
+
if (!visitorFailures.allowed) {
|
|
1369
|
+
return visitorFailures;
|
|
1370
|
+
}
|
|
1371
|
+
var attemptAllowed = await security.rateLimitRequest(visitor, null, {
|
|
1372
|
+
scope: SIGN_IN_ATTEMPT_SCOPE,
|
|
1373
|
+
limit: SIGN_IN_ATTEMPT_LIMIT,
|
|
1374
|
+
windowSeconds: SIGN_IN_WINDOW_SECONDS,
|
|
1375
|
+
keyParts: [email],
|
|
1376
|
+
});
|
|
1377
|
+
if (!attemptAllowed) {
|
|
1378
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
1379
|
+
return { allowed: false, retryAfter: Math.max(1, SIGN_IN_WINDOW_SECONDS - (nowSec % SIGN_IN_WINDOW_SECONDS)) };
|
|
1380
|
+
}
|
|
1381
|
+
return { allowed: true, retryAfter: 0 };
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
async function countSignInFailure(req, email) {
|
|
1385
|
+
try {
|
|
1386
|
+
var security = require('../security-runtime');
|
|
1387
|
+
var visitor = signInVisitorRequest(req);
|
|
1388
|
+
await security.rateLimitRequest(visitor, null, {
|
|
1389
|
+
scope: SIGN_IN_FAILURE_SCOPE,
|
|
1390
|
+
limit: SIGN_IN_FAILURE_LIMIT,
|
|
1391
|
+
windowSeconds: SIGN_IN_WINDOW_SECONDS,
|
|
1392
|
+
keyParts: [signInEmailKey(email)],
|
|
1393
|
+
});
|
|
1394
|
+
await security.rateLimitRequest(visitor, null, {
|
|
1395
|
+
scope: SIGN_IN_VISITOR_FAILURE_SCOPE,
|
|
1396
|
+
limit: SIGN_IN_VISITOR_FAILURE_LIMIT,
|
|
1397
|
+
windowSeconds: SIGN_IN_WINDOW_SECONDS,
|
|
1398
|
+
});
|
|
1399
|
+
} catch (err) {
|
|
1400
|
+
console.error('[auth] Could not count a failed sign-in:', err && err.message ? err.message : err);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// The shared session-stamp check (utils/auth/session-revocation.js).
|
|
1405
|
+
function sessionRevocationModule() {
|
|
1406
|
+
try {
|
|
1407
|
+
var revocation = require('./${session_revocation_generator_1.SESSION_REVOCATION_FILE_NAME}');
|
|
1408
|
+
return revocation && typeof revocation.stampVerdict === 'function' ? revocation : null;
|
|
1409
|
+
} catch (e) {
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// A signed-in user's session carries the stamp of the account it was issued for.
|
|
1415
|
+
function withSessionStamp(safeUser, row) {
|
|
1416
|
+
var revocation = sessionRevocationModule();
|
|
1417
|
+
var stamp = revocation ? revocation.credentialStamp(row) : null;
|
|
1418
|
+
if (safeUser && stamp) {
|
|
1419
|
+
safeUser[revocation.SESSION_STAMP_KEY] = stamp;
|
|
1420
|
+
}
|
|
1421
|
+
if (safeUser) {
|
|
1422
|
+
safeUser[TOKEN_PASSWORD_FINGERPRINT] = passwordFingerprint(row ? row.password : null);
|
|
1423
|
+
}
|
|
1424
|
+
return safeUser;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// 'revoked' when the stored account no longer matches the token's stamp;
|
|
1428
|
+
// otherwise the token takes the account's current stamp.
|
|
1429
|
+
function sessionStampCheck(token, row) {
|
|
1430
|
+
var revocation = sessionRevocationModule();
|
|
1431
|
+
if (!revocation) {
|
|
1432
|
+
return 'unchecked';
|
|
1433
|
+
}
|
|
1434
|
+
var verdict = revocation.stampVerdict(token, row || null);
|
|
1435
|
+
if (verdict !== 'revoked' && row) {
|
|
1436
|
+
var stamp = revocation.credentialStamp(row);
|
|
1437
|
+
if (stamp) {
|
|
1438
|
+
token[revocation.SESSION_STAMP_KEY] = stamp;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
return verdict;
|
|
1442
|
+
}`;
|
|
1443
|
+
/**
|
|
1444
|
+
* The user stores whose own account ids can never equal an OAuth provider's id:
|
|
1445
|
+
* MongoDB (`_id`, a 24-hex ObjectId), Airtable (`rec…` record ids) and
|
|
1446
|
+
* Firestore (20-character auto ids). Read from `auth.dataSourceType`, the same
|
|
1447
|
+
* value that picks the auth driver.
|
|
1448
|
+
*/
|
|
1449
|
+
const STORES_WITH_DISJOINT_IDS = new Set(['mongodb', 'airtable', 'firestore']);
|
|
1450
|
+
/**
|
|
1451
|
+
* Whether an OAuth session's id must be namespaced (`oauth:<provider>:<id>`)
|
|
1452
|
+
* because the provider's raw account id could equal another account's id:
|
|
1453
|
+
* - the project has more than one OAuth provider: two providers' id spaces
|
|
1454
|
+
* can collide, whatever the store;
|
|
1455
|
+
* - otherwise a store whose ids are its own (STORES_WITH_DISJOINT_IDS) keeps
|
|
1456
|
+
* the raw id;
|
|
1457
|
+
* - any other store (SQL or not) is decided by the declared `users` id type:
|
|
1458
|
+
* a UUID keeps the raw id; any other type (a provider's integer id can equal
|
|
1459
|
+
* a local integer id on every owner rule), or none declared, namespaces.
|
|
1460
|
+
* The editor declares the type the database reported for its `users` table
|
|
1461
|
+
* (teleport-gui `liveUsersIdType`), and no type when none was recorded.
|
|
1462
|
+
* Where the raw id stays it never equals a local id, and every record a
|
|
1463
|
+
* published site wrote under it (entitlements, cart, favourites, chats,
|
|
1464
|
+
* downloads) stays the visitor's. Wherever a collision cannot be ruled out the
|
|
1465
|
+
* id is namespaced: a collision hands one person another's records, which no
|
|
1466
|
+
* later fix undoes; lost continuity leaves the records in place under the old
|
|
1467
|
+
* id.
|
|
1468
|
+
*/
|
|
1469
|
+
const oauthIdsNeedNamespace = (auth) => {
|
|
1470
|
+
if ((auth.providers || []).length > 1) {
|
|
1471
|
+
return true;
|
|
1472
|
+
}
|
|
1473
|
+
if (auth.dataSourceType && STORES_WITH_DISJOINT_IDS.has(auth.dataSourceType)) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
const idColumn = ((auth.tables && auth.tables.users) || []).find((column) => !!column && String(column.name || '').toLowerCase() === 'id');
|
|
1477
|
+
return !(idColumn && /^\s*uuid\b/i.test(String(idColumn.type || '')));
|
|
1478
|
+
};
|
|
1479
|
+
exports.oauthIdsNeedNamespace = oauthIdsNeedNamespace;
|
|
1480
|
+
/**
|
|
1481
|
+
* auth-options helpers for a project with password sign-in AND an OAuth
|
|
1482
|
+
* provider: whether the provider vouches for the email an OAuth sign-in
|
|
1483
|
+
* presents. Only then may that sign-in become the local account with the same
|
|
1484
|
+
* email (its id and role); otherwise it is a session of its own.
|
|
1485
|
+
*/
|
|
1486
|
+
const generateOAuthEmailVerificationHelpers = (auth) => {
|
|
1487
|
+
const tenants = microsoftTenantEnvKeys(auth);
|
|
1488
|
+
const tenantEntries = Object.keys(tenants)
|
|
1489
|
+
.map((id) => `${JSON.stringify(id)}: process.env.${tenants[id]}`)
|
|
1490
|
+
.join(', ');
|
|
1491
|
+
return `// Providers that only ever hand out an email its owner proved. Any other
|
|
1492
|
+
// provider must say so in the profile (email_verified); an explicit "not
|
|
1493
|
+
// verified" outweighs the list.
|
|
1494
|
+
var VERIFIED_EMAIL_PROVIDERS = { google: true, apple: true };
|
|
1495
|
+
var OAUTH_UNVERIFIED = '__oauthUnverified';
|
|
1496
|
+
// A provider's raw account id could equal another account's id here (see
|
|
1497
|
+
// oauthIdsNeedNamespace in the code generator): sessions without a local
|
|
1498
|
+
// account take the provider's id as "oauth:<provider>:<id>".
|
|
1499
|
+
var OAUTH_IDS_NAMESPACED = ${JSON.stringify((0, exports.oauthIdsNeedNamespace)(auth))};
|
|
1500
|
+
|
|
1501
|
+
// Microsoft (Azure AD / Entra ID) tokens carry no email_verified, and on the
|
|
1502
|
+
// multi-tenant endpoint the email claim is whatever the signing-in tenant's
|
|
1503
|
+
// administrator set (the "nOAuth" class). An email counts only when the token
|
|
1504
|
+
// says its domain is verified (xms_edov), or when the provider is configured
|
|
1505
|
+
// for one specific tenant (an id or a domain, never common / organizations /
|
|
1506
|
+
// consumers): the site owner's own directory.
|
|
1507
|
+
var MICROSOFT_PROVIDERS = { 'azure-ad': true, 'microsoft-entra-id': true };
|
|
1508
|
+
var MICROSOFT_TENANTS = { ${tenantEntries} };
|
|
1509
|
+
var MICROSOFT_TENANT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
1510
|
+
|
|
1511
|
+
// The one tenant a Microsoft provider is configured for, or '' (multi-tenant).
|
|
1512
|
+
function microsoftTenantOf(value) {
|
|
1513
|
+
var raw = String(value == null ? '' : value).trim().toLowerCase();
|
|
1514
|
+
if (!raw || raw.indexOf('teleporthq.secrets.') === 0) {
|
|
1515
|
+
return '';
|
|
1516
|
+
}
|
|
1517
|
+
var fromIssuer = /^https:\\/\\/login\\.microsoftonline\\.com\\/([^\\/?#]+)/.exec(raw);
|
|
1518
|
+
var tenant = fromIssuer ? fromIssuer[1] : raw;
|
|
1519
|
+
if (tenant === 'common' || tenant === 'organizations' || tenant === 'consumers') {
|
|
1520
|
+
return '';
|
|
1521
|
+
}
|
|
1522
|
+
return MICROSOFT_TENANT_ID.test(tenant) || /^[a-z0-9-]+(\\.[a-z0-9-]+)+$/.test(tenant) ? tenant : '';
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function microsoftEmailIsVerified(provider, claims) {
|
|
1526
|
+
if (claims.xms_edov === true || claims.xms_edov === 'true' || claims.xms_edov === 1) {
|
|
1527
|
+
return true;
|
|
1528
|
+
}
|
|
1529
|
+
var tenant = microsoftTenantOf(MICROSOFT_TENANTS[provider]);
|
|
1530
|
+
if (!tenant) {
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
// A tenant given by id must be the one the token was issued in.
|
|
1534
|
+
if (MICROSOFT_TENANT_ID.test(tenant) && claims.tid != null && String(claims.tid).toLowerCase() !== tenant) {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
return true;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// GitHub's profile email is not always a verified one: ask GitHub (the
|
|
1541
|
+
// provider requests the user:email scope) whether this address is.
|
|
1542
|
+
async function githubEmailIsVerified(account, email) {
|
|
1543
|
+
if (!account || !account.access_token || !email) {
|
|
1544
|
+
return false;
|
|
1545
|
+
}
|
|
1546
|
+
try {
|
|
1547
|
+
var res = await fetch('https://api.github.com/user/emails', {
|
|
1548
|
+
headers: {
|
|
1549
|
+
Authorization: 'Bearer ' + account.access_token,
|
|
1550
|
+
Accept: 'application/vnd.github+json',
|
|
1551
|
+
'User-Agent': 'teleporthq-auth',
|
|
1552
|
+
},
|
|
1553
|
+
});
|
|
1554
|
+
if (!res.ok) {
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
var list = await res.json();
|
|
1558
|
+
var wanted = String(email).toLowerCase();
|
|
1559
|
+
return Array.isArray(list) && list.some(function (entry) {
|
|
1560
|
+
return !!entry && entry.verified === true && String(entry.email || '').toLowerCase() === wanted;
|
|
1561
|
+
});
|
|
1562
|
+
} catch (e) {
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
async function oauthEmailIsVerified(account, profile, token) {
|
|
1568
|
+
var claims = profile || {};
|
|
1569
|
+
if (claims.email_verified === false || claims.email_verified === 'false') {
|
|
1570
|
+
return false;
|
|
1571
|
+
}
|
|
1572
|
+
if (claims.email_verified === true || claims.email_verified === 'true') {
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
var provider = String((account && account.provider) || '');
|
|
1576
|
+
if (provider === 'github') {
|
|
1577
|
+
return githubEmailIsVerified(account, token && token.email);
|
|
1578
|
+
}
|
|
1579
|
+
if (MICROSOFT_PROVIDERS[provider] === true) {
|
|
1580
|
+
return microsoftEmailIsVerified(provider, claims);
|
|
1581
|
+
}
|
|
1582
|
+
return VERIFIED_EMAIL_PROVIDERS[provider] === true;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// The provider's own account id as "oauth:<provider>:<id>": a namespace no
|
|
1586
|
+
// local account id can share (a provider's integer ids would otherwise match
|
|
1587
|
+
// local integer ids on owner rules). Where no id can collide, the raw id stays.
|
|
1588
|
+
function namespaceOAuthId(token, account) {
|
|
1589
|
+
if (!OAUTH_IDS_NAMESPACED) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
var accountId = String((account && account.providerAccountId) || token.sub || token.id || '');
|
|
1593
|
+
var id = 'oauth:' + String((account && account.provider) || 'provider') + ':' + accountId;
|
|
1594
|
+
token.id = id;
|
|
1595
|
+
token.sub = id;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// Not vouched for: the visitor keeps a session under an id no local account can
|
|
1599
|
+
// have (namespaced where one could), with no email to be matched by and no role.
|
|
1600
|
+
function asDistinctOAuthIdentity(token, account) {
|
|
1601
|
+
namespaceOAuthId(token, account);
|
|
1602
|
+
delete token.email;
|
|
1603
|
+
delete token.role;
|
|
1604
|
+
delete token.roleName;
|
|
1605
|
+
delete token.roles;
|
|
1606
|
+
token[OAUTH_UNVERIFIED] = true;
|
|
1607
|
+
}`;
|
|
1608
|
+
};
|
|
820
1609
|
/**
|
|
821
1610
|
* Why a sign-in failed, in words, plus the one-line codes `authorize` throws.
|
|
822
1611
|
*
|
|
@@ -837,6 +1626,7 @@ const AUTH_ERROR_MESSAGES = {
|
|
|
837
1626
|
InvalidCredentials: 'That email and password do not match an account.',
|
|
838
1627
|
ServiceUnavailable:
|
|
839
1628
|
'We could not reach the account service. Please try again in a moment — if it keeps happening, the site owner needs to check its database configuration.',
|
|
1629
|
+
TooManyAttempts: 'Too many sign-in attempts. Please wait a few minutes and try again.',
|
|
840
1630
|
|
|
841
1631
|
// NextAuth's own.
|
|
842
1632
|
CredentialsSignin: 'That email and password do not match an account.',
|
|
@@ -936,37 +1726,156 @@ module.exports = {
|
|
|
936
1726
|
`;
|
|
937
1727
|
};
|
|
938
1728
|
exports.generateAuthDbHealthFile = generateAuthDbHealthFile;
|
|
1729
|
+
/** The password a site accepts when one is set (sign-up, reset, a change). */
|
|
1730
|
+
exports.MIN_PASSWORD_LENGTH = 8;
|
|
1731
|
+
exports.MAX_PASSWORD_BYTES = 128;
|
|
939
1732
|
const generateHashPasswordFile = () => {
|
|
940
1733
|
return `const bcrypt = require('bcryptjs');
|
|
941
1734
|
const crypto = require('crypto');
|
|
942
1735
|
|
|
943
|
-
const PEPPER = process.env.AUTH_PEPPER || '';
|
|
944
1736
|
const COST_FACTOR = 12;
|
|
1737
|
+
const MIN_PASSWORD_LENGTH = ${exports.MIN_PASSWORD_LENGTH};
|
|
1738
|
+
const MAX_PASSWORD_BYTES = ${exports.MAX_PASSWORD_BYTES};
|
|
1739
|
+
|
|
1740
|
+
// Two peppers. AUTH_PEPPER_SECRET is the project's own random secret, which the
|
|
1741
|
+
// deploy step provisions; every new hash uses it. AUTH_PEPPER (the project id,
|
|
1742
|
+
// no secret) is the one every hash was made with before it existed: such a hash
|
|
1743
|
+
// still verifies, and the sign-in replaces it. Without the secret, hashing and
|
|
1744
|
+
// verifying stay on the old pepper — never on none.
|
|
1745
|
+
function pepperSecret() {
|
|
1746
|
+
var value = process.env.AUTH_PEPPER_SECRET;
|
|
1747
|
+
return typeof value === 'string' && value && value.indexOf('teleporthq.secrets.') !== 0 ? value : '';
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
function legacyPepper() {
|
|
1751
|
+
return process.env.AUTH_PEPPER || '';
|
|
1752
|
+
}
|
|
945
1753
|
|
|
946
|
-
|
|
1754
|
+
// SHA-256 first, so bcrypt (which reads at most 72 bytes) sees all of it.
|
|
1755
|
+
function prepare(password, pepper) {
|
|
947
1756
|
const str = String(password);
|
|
948
1757
|
if (str.length > 1000) {
|
|
949
1758
|
throw new Error('Password exceeds maximum length');
|
|
950
1759
|
}
|
|
951
|
-
|
|
952
|
-
return sha;
|
|
1760
|
+
return crypto.createHash('sha256').update(str + pepper).digest('base64');
|
|
953
1761
|
}
|
|
954
1762
|
|
|
955
1763
|
function hashPassword(password) {
|
|
956
|
-
return bcrypt.hashSync(prepare(password), COST_FACTOR);
|
|
1764
|
+
return bcrypt.hashSync(prepare(password, pepperSecret() || legacyPepper()), COST_FACTOR);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// A hash no password matches, made once per process with the project pepper
|
|
1768
|
+
// at the same cost. An address without an account, or a row without a
|
|
1769
|
+
// password (a subscriber, a guest), is checked against it, so every failed
|
|
1770
|
+
// sign-in does the bcrypt work a wrong password does: the time never tells
|
|
1771
|
+
// them apart.
|
|
1772
|
+
var __standInHash = null;
|
|
1773
|
+
function standInHash() {
|
|
1774
|
+
if (!__standInHash) {
|
|
1775
|
+
__standInHash = bcrypt.hashSync(prepare(crypto.randomBytes(32).toString('hex'), pepperSecret() || legacyPepper()), COST_FACTOR);
|
|
1776
|
+
}
|
|
1777
|
+
return __standInHash;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// { ok, needsRehash }: ok when the password matches; needsRehash when it
|
|
1781
|
+
// matched through the old pepper while the project has its own.
|
|
1782
|
+
function checkPassword(password, hash) {
|
|
1783
|
+
var known = typeof hash === 'string' && hash !== '';
|
|
1784
|
+
var against = known ? hash : standInHash();
|
|
1785
|
+
var secret = pepperSecret();
|
|
1786
|
+
if (secret && bcrypt.compareSync(prepare(password, secret), against) && known) {
|
|
1787
|
+
return { ok: true, needsRehash: false };
|
|
1788
|
+
}
|
|
1789
|
+
// Run on every miss, whatever the hash was.
|
|
1790
|
+
if (bcrypt.compareSync(prepare(password, legacyPepper()), against) && known) {
|
|
1791
|
+
return { ok: true, needsRehash: !!secret };
|
|
1792
|
+
}
|
|
1793
|
+
return { ok: false, needsRehash: false };
|
|
957
1794
|
}
|
|
958
1795
|
|
|
959
1796
|
function verifyPassword(password, hash) {
|
|
960
|
-
return
|
|
1797
|
+
return checkPassword(password, hash).ok;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// Why a password may not be set, or '' when it may.
|
|
1801
|
+
function passwordPolicyError(password) {
|
|
1802
|
+
if (typeof password !== 'string' || password.length < MIN_PASSWORD_LENGTH) {
|
|
1803
|
+
return 'Password must be at least ' + MIN_PASSWORD_LENGTH + ' characters';
|
|
1804
|
+
}
|
|
1805
|
+
if (Buffer.byteLength(password, 'utf8') > MAX_PASSWORD_BYTES) {
|
|
1806
|
+
return 'Password is too long (at most ' + MAX_PASSWORD_BYTES + ' bytes)';
|
|
1807
|
+
}
|
|
1808
|
+
return '';
|
|
961
1809
|
}
|
|
962
1810
|
|
|
963
1811
|
module.exports = hashPassword;
|
|
964
1812
|
module.exports.hashPassword = hashPassword;
|
|
965
1813
|
module.exports.verifyPassword = verifyPassword;
|
|
1814
|
+
module.exports.checkPassword = checkPassword;
|
|
1815
|
+
module.exports.passwordPolicyError = passwordPolicyError;
|
|
966
1816
|
`;
|
|
967
1817
|
};
|
|
968
1818
|
exports.generateHashPasswordFile = generateHashPasswordFile;
|
|
969
|
-
|
|
1819
|
+
// Answered to a credentials sign-in the throttle refuses, in the shape
|
|
1820
|
+
// next-auth/react's signIn() reads (it parses \`url\` for the error code, which
|
|
1821
|
+
// describeAuthError turns into copy).
|
|
1822
|
+
const SIGN_IN_THROTTLE_CODE = `var __security = require('${(0, security_runtime_code_1.securityRuntimeSpecifier)('../../..')}');
|
|
1823
|
+
|
|
1824
|
+
function isCredentialsSignIn(req) {
|
|
1825
|
+
var parts = req && req.query ? req.query.nextauth : null;
|
|
1826
|
+
return (
|
|
1827
|
+
req && req.method === 'POST' && Array.isArray(parts) && parts[0] === 'callback' && parts[1] === 'credentials'
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
function refuseSignIn(res, status, code, message, retryAfter) {
|
|
1832
|
+
var origin = process.env.NEXTAUTH_URL || 'http://localhost:3000';
|
|
1833
|
+
var url;
|
|
1834
|
+
try {
|
|
1835
|
+
url = new URL('/api/auth/error?error=' + code, origin).toString();
|
|
1836
|
+
} catch (e) {
|
|
1837
|
+
url = 'http://localhost:3000/api/auth/error?error=' + code;
|
|
1838
|
+
}
|
|
1839
|
+
if (retryAfter) {
|
|
1840
|
+
res.setHeader('Retry-After', String(retryAfter));
|
|
1841
|
+
}
|
|
1842
|
+
res.status(status).json({ url: url, error: message });
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// Too many failed (or attempted) sign-ins for this email from this visitor:
|
|
1846
|
+
// refused before the password is even looked at.
|
|
1847
|
+
async function throttleCredentialsSignIn(req, res) {
|
|
1848
|
+
if (req && req.headers) {
|
|
1849
|
+
delete req.headers[authOptions.SIGN_IN_VISITOR_HEADER];
|
|
1850
|
+
}
|
|
1851
|
+
if (!isCredentialsSignIn(req)) {
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
req.headers[authOptions.SIGN_IN_VISITOR_HEADER] = __security.clientIp(req);
|
|
1855
|
+
var verdict;
|
|
1856
|
+
try {
|
|
1857
|
+
verdict = await authOptions.checkSignInAllowed(req);
|
|
1858
|
+
} catch (err) {
|
|
1859
|
+
console.error('[auth] The sign-in throttle failed:', err && err.message ? err.message : err);
|
|
1860
|
+
refuseSignIn(res, 503, 'ServiceUnavailable', 'Sign-in is unavailable right now. Please try again in a moment.', 0);
|
|
1861
|
+
return true;
|
|
1862
|
+
}
|
|
1863
|
+
if (verdict && verdict.allowed) {
|
|
1864
|
+
return false;
|
|
1865
|
+
}
|
|
1866
|
+
refuseSignIn(
|
|
1867
|
+
res,
|
|
1868
|
+
429,
|
|
1869
|
+
'TooManyAttempts',
|
|
1870
|
+
'Too many sign-in attempts. Try again later.',
|
|
1871
|
+
verdict && verdict.retryAfter ? verdict.retryAfter : ${exports.SIGN_IN_WINDOW_SECONDS}
|
|
1872
|
+
);
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
`;
|
|
1877
|
+
const generateNextAuthRouteFile = (options = {}) => {
|
|
1878
|
+
const throttled = options.passwordAuthEnabled === true;
|
|
970
1879
|
return `let NextAuth = require('next-auth');
|
|
971
1880
|
if (NextAuth.default) NextAuth = NextAuth.default;
|
|
972
1881
|
const authOptions = require('../../../utils/auth/auth-options');
|
|
@@ -1061,46 +1970,291 @@ function resolveRequestOrigin(req) {
|
|
|
1061
1970
|
return proto + '://' + host;
|
|
1062
1971
|
}
|
|
1063
1972
|
|
|
1064
|
-
module.exports = function nextAuthRoute(req, res) {
|
|
1973
|
+
${throttled ? SIGN_IN_THROTTLE_CODE : ''}module.exports = ${throttled ? 'async ' : ''}function nextAuthRoute(req, res) {
|
|
1065
1974
|
try {
|
|
1066
1975
|
process.env.NEXTAUTH_URL = resolveRequestOrigin(req);
|
|
1067
1976
|
} catch (e) {
|
|
1068
1977
|
/* fall back to whatever NEXTAUTH_URL holds */
|
|
1069
1978
|
}
|
|
1070
|
-
|
|
1979
|
+
${throttled
|
|
1980
|
+
? ` if (await throttleCredentialsSignIn(req, res)) {
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
`
|
|
1984
|
+
: ''} return authHandler(req, res);
|
|
1071
1985
|
};
|
|
1072
1986
|
`;
|
|
1073
1987
|
};
|
|
1074
1988
|
exports.generateNextAuthRouteFile = generateNextAuthRouteFile;
|
|
1075
|
-
|
|
1989
|
+
/**
|
|
1990
|
+
* `recordAccountEvent(type, user, source, req)`, for the account routes of a
|
|
1991
|
+
* project with automations: records `user-signed-up` / `user-deleted` on the
|
|
1992
|
+
* in-app event bus, which writes the `teleport_events` row and wakes the
|
|
1993
|
+
* automations off the request path, on the app's own origin as `req` reached
|
|
1994
|
+
* it. Only the profile fields go into the event, never a credential. It never
|
|
1995
|
+
* throws, so the event cannot fail the request it records. `relativePrefix`
|
|
1996
|
+
* leads from the route to the project root.
|
|
1997
|
+
*/
|
|
1998
|
+
const generateAccountEventRecorder = (options) => `
|
|
1999
|
+
// The project has automations: the account event wakes them. Best effort,
|
|
2000
|
+
// like the emails: recording it can never fail the request.
|
|
2001
|
+
function accountEventTime(value) {
|
|
2002
|
+
var date = value ? new Date(value) : new Date();
|
|
2003
|
+
return isNaN(date.getTime()) ? new Date().toISOString() : date.toISOString();
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
async function recordAccountEvent(type, user, source, req) {
|
|
2007
|
+
try {
|
|
2008
|
+
var userId = user ? user.id || user._id : null;
|
|
2009
|
+
if (!userId) { return; }
|
|
2010
|
+
var id = String(userId);
|
|
2011
|
+
var email = user.email ? String(user.email) : '';
|
|
2012
|
+
var bus = require('${options.relativePrefix}/${automations_scope_1.AUTOMATIONS_PATH.join('/')}/event-bus');
|
|
2013
|
+
await bus.emitBusinessEvent({
|
|
2014
|
+
type: type,
|
|
2015
|
+
entityType: 'user',
|
|
2016
|
+
entityId: id,
|
|
2017
|
+
payload: {
|
|
2018
|
+
user: {
|
|
2019
|
+
id: id,
|
|
2020
|
+
email: email || null,
|
|
2021
|
+
name: user.name || null,
|
|
2022
|
+
role: user.role || null,
|
|
2023
|
+
source: source || user.source || null,
|
|
2024
|
+
createdAt: accountEventTime(user.created_at || user.createdAt),
|
|
2025
|
+
},
|
|
2026
|
+
},
|
|
2027
|
+
actor: email,
|
|
2028
|
+
actorType: 'customer',
|
|
2029
|
+
dedupeKey: type + ':' + id,
|
|
2030
|
+
}, req);
|
|
2031
|
+
} catch (eventErr) {
|
|
2032
|
+
var reason = eventErr && eventErr.message ? eventErr.message : eventErr;
|
|
2033
|
+
console.error('${options.logTag} could not record the ' + type + ' event:', reason);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
`;
|
|
2037
|
+
exports.generateAccountEventRecorder = generateAccountEventRecorder;
|
|
2038
|
+
/**
|
|
2039
|
+
* A user column that grants something — a role, an admin or staff flag, a
|
|
2040
|
+
* verified email, credit, a plan or an entitlement — in any spelling
|
|
2041
|
+
* (`is_admin`, `isAdmin`, `Email-Verified`). Only the server sets these; a
|
|
2042
|
+
* sign-up never takes them from the visitor. The one shared rule
|
|
2043
|
+
* (`AccountFields.isPrivilegedAccountField`).
|
|
2044
|
+
*/
|
|
2045
|
+
const isPrivilegedUserField = (key) => teleport_shared_1.AccountFields.isPrivilegedAccountField(key);
|
|
2046
|
+
exports.isPrivilegedUserField = isPrivilegedUserField;
|
|
2047
|
+
/** Config keys of an `account-signup` node that are not user fields. */
|
|
2048
|
+
const SIGNUP_NODE_NON_FIELD_KEYS = new Set([
|
|
2049
|
+
'email',
|
|
2050
|
+
'password',
|
|
2051
|
+
'name',
|
|
2052
|
+
'image',
|
|
2053
|
+
'role',
|
|
2054
|
+
...transactional_email_code_1.WELCOME_EMAIL_CONFIG_KEYS,
|
|
2055
|
+
]);
|
|
2056
|
+
/**
|
|
2057
|
+
* The fields a sign-up may write besides email, password and name: the avatar
|
|
2058
|
+
* (`image`) and each declared custom account property that a sign-up form of
|
|
2059
|
+
* the project collects (a key of an `account-signup` node), never a
|
|
2060
|
+
* privileged one.
|
|
2061
|
+
*/
|
|
2062
|
+
const resolveSignupFields = (auth, signupNodeConfigs) => {
|
|
2063
|
+
const declared = new Set((auth.customUserProperties || []).map((prop) => prop.key));
|
|
2064
|
+
const collected = new Set();
|
|
2065
|
+
for (const config of signupNodeConfigs) {
|
|
2066
|
+
for (const key of Object.keys(config || {})) {
|
|
2067
|
+
if (!SIGNUP_NODE_NON_FIELD_KEYS.has(key) &&
|
|
2068
|
+
declared.has(key) &&
|
|
2069
|
+
!(0, exports.isPrivilegedUserField)(key)) {
|
|
2070
|
+
collected.add(key);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
return ['image', ...Array.from(collected).sort()];
|
|
2075
|
+
};
|
|
2076
|
+
exports.resolveSignupFields = resolveSignupFields;
|
|
2077
|
+
/**
|
|
2078
|
+
* After a new account with a ticked consent box: the newsletter helper adds it
|
|
2079
|
+
* (double opt-in, the `newsletter-subscribed` event, the newsletter's welcome
|
|
2080
|
+
* unless the account's own went out). Best effort, like the welcome email.
|
|
2081
|
+
*/
|
|
2082
|
+
const SIGNUP_NEWSLETTER_CODE = ` // The ticked box also subscribes the person to the newsletter.
|
|
2083
|
+
if (userData.marketing_consent) {
|
|
2084
|
+
try {
|
|
2085
|
+
const __newsletter = require('../../../utils/newsletter/newsletter');
|
|
2086
|
+
await __newsletter.withNewsletterDb(function (db) {
|
|
2087
|
+
return __newsletter.subscribeNewsletterPerson({ db: db, email: email, signUpPoint: 'signup', req: req });
|
|
2088
|
+
});
|
|
2089
|
+
} catch (newsletterErr) {
|
|
2090
|
+
console.error('[account-signup] newsletter sign-up failed:', newsletterErr && newsletterErr.message ? newsletterErr.message : newsletterErr);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
`;
|
|
2094
|
+
/**
|
|
2095
|
+
* The answer to a sign-up for an address the site already knows (an account,
|
|
2096
|
+
* or a row without a password) takes at least this long, as the
|
|
2097
|
+
* forgot-password answer does: the two are answered alike, and the time must
|
|
2098
|
+
* not tell them apart either.
|
|
2099
|
+
*/
|
|
2100
|
+
exports.KNOWN_ADDRESS_MIN_RESPONSE_MS = 1200;
|
|
2101
|
+
/**
|
|
2102
|
+
* The sign-up route's emails to an address the site already knows, sent
|
|
2103
|
+
* through `sender` (the sign-up node's, else the forgot-password node's):
|
|
2104
|
+
* `sendAccountNotice(emailType, msg, logged)`, which sends and records the
|
|
2105
|
+
* email under its own purpose (with what the log may keep), and
|
|
2106
|
+
* `sendSetPasswordEmail(user, siteName, req)`, the link for an address without
|
|
2107
|
+
* a password, built on the forgot-password token flow (a CSPRNG token whose
|
|
2108
|
+
* SHA-256 alone is stored against the account, the same lifetime, origin rule
|
|
2109
|
+
* and reset page).
|
|
2110
|
+
*/
|
|
2111
|
+
const generateKnownAddressEmailCode = (options) => {
|
|
2112
|
+
const { sender } = options;
|
|
2113
|
+
const senderConstants = `
|
|
2114
|
+
// Who sends the emails to an address the site already knows: the sign-up
|
|
2115
|
+
// node's email settings, else the forgot-password node's.
|
|
2116
|
+
const NOTICE_EMAIL_PROVIDER = ${JSON.stringify(sender.provider)};
|
|
2117
|
+
const NOTICE_EMAIL_FROM = ${JSON.stringify(sender.from)};
|
|
2118
|
+
const NOTICE_EMAIL_SECRET_ENV_NAME = ${JSON.stringify(sender.secretEnv)};`;
|
|
2119
|
+
if (!sender.provider) {
|
|
2120
|
+
return `${senderConstants}
|
|
2121
|
+
|
|
2122
|
+
// No email provider: nothing is sent to an address the site already knows.
|
|
2123
|
+
async function sendAccountNotice() {}
|
|
2124
|
+
|
|
2125
|
+
// A sign-up for an address the site knows without a password mails its owner
|
|
2126
|
+
// a link to set one; this project has no email provider, so none is sent.
|
|
2127
|
+
async function sendSetPasswordEmail() {
|
|
2128
|
+
console.warn('[account-signup] set-password link not sent: no email provider is configured');
|
|
2129
|
+
}`;
|
|
2130
|
+
}
|
|
2131
|
+
const ttlMinutes = Math.round(password_reset_route_generator_1.PASSWORD_RESET_TOKEN_TTL_SECONDS / 60);
|
|
2132
|
+
// Where the link carries the token, as the forgot-password link does.
|
|
2133
|
+
const tokenLinkPrefix = options.tokenInFragment ? '#token=' : '?token=';
|
|
2134
|
+
const noticeSend = (0, transactional_email_code_1.generateProviderSendFunction)(sender.provider).replace('async function __sendProviderEmail(', 'async function __sendNoticeEmail(');
|
|
2135
|
+
return `${senderConstants}
|
|
2136
|
+
|
|
2137
|
+
${noticeSend}
|
|
2138
|
+
|
|
2139
|
+
var __noticeEmailLog = require('../../../utils/email/sent-email-log');
|
|
2140
|
+
|
|
2141
|
+
// Sends one of these emails and records it under its own purpose, with what
|
|
2142
|
+
// the log may keep of it (\`logged\`: a link's token hidden).
|
|
2143
|
+
async function sendAccountNotice(emailType, msg, logged) {
|
|
2144
|
+
var failure = null;
|
|
2145
|
+
try {
|
|
2146
|
+
await __sendNoticeEmail(msg);
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
failure = err;
|
|
2149
|
+
}
|
|
2150
|
+
__noticeEmailLog.recordSentEmail({
|
|
2151
|
+
emailType: emailType,
|
|
2152
|
+
audience: 'customer',
|
|
2153
|
+
to: msg.to,
|
|
2154
|
+
from: msg.from,
|
|
2155
|
+
subject: logged.subject,
|
|
2156
|
+
html: logged.html,
|
|
2157
|
+
payload: logged.payload,
|
|
2158
|
+
provider: NOTICE_EMAIL_PROVIDER,
|
|
2159
|
+
status: failure ? 'failed' : 'sent',
|
|
2160
|
+
error: failure ? (failure.message || String(failure)) : null,
|
|
2161
|
+
source: 'signup',
|
|
2162
|
+
sourceRef: 'api/auth/signup',
|
|
2163
|
+
userId: msg.userId,
|
|
2164
|
+
});
|
|
2165
|
+
if (failure) { throw failure; }
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// A sign-up for an address the site knows without a password (a newsletter
|
|
2169
|
+
// subscriber, a checkout or booking guest, a contact the owner added) never
|
|
2170
|
+
// takes that row: whoever typed the address may not own it. Its owner is mailed
|
|
2171
|
+
// a link to set a password instead, a password-reset link minted and stored as
|
|
2172
|
+
// the forgot-password route mints it, at most ${exports.EXISTING_ACCOUNT_EMAIL_LIMIT} an hour per address
|
|
2173
|
+
// (counted with the "already have an account" email).
|
|
2174
|
+
var SET_PASSWORD_SUBJECT = 'Set your password to finish creating your account';
|
|
2175
|
+
var SET_PASSWORD_BODY_HTML =
|
|
2176
|
+
'<p>Someone, hopefully you, tried to create an account{{siteNameSuffix}} with this email address.</p>' +
|
|
2177
|
+
'<p>This address is already known here (from a newsletter sign-up or an order, for example), but it has no password yet. Set one to finish creating your account:</p>' +
|
|
2178
|
+
'<p><a href="{{resetUrl}}">Set your password</a></p>' +
|
|
2179
|
+
'<p>The link works for ${ttlMinutes} minutes. If this was not you, you can ignore this email: nothing has changed.</p>';
|
|
2180
|
+
var SET_PASSWORD_PAGE_PATH = ${JSON.stringify(options.pagePath)};
|
|
2181
|
+
var SET_PASSWORD_TOKEN_LINK_PREFIX = ${JSON.stringify(tokenLinkPrefix)};
|
|
2182
|
+
var SET_PASSWORD_TOKEN_TTL_SECONDS = ${password_reset_route_generator_1.PASSWORD_RESET_TOKEN_TTL_SECONDS};
|
|
2183
|
+
|
|
2184
|
+
${password_reset_route_generator_1.TRUSTED_ORIGIN_CODE}
|
|
2185
|
+
|
|
2186
|
+
// The token, wherever it appears, for what the sent-email log keeps: whoever
|
|
2187
|
+
// can read the log must not be able to set the password with it.
|
|
2188
|
+
function hideToken(text, token) {
|
|
2189
|
+
return String(text == null ? '' : text).split(token).join('[hidden]');
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
async function sendSetPasswordEmail(user, siteName, req) {
|
|
2193
|
+
var toEmail = String(user.email || '');
|
|
2194
|
+
var userId = user.id != null ? user.id : user._id;
|
|
2195
|
+
var apiKey = NOTICE_EMAIL_SECRET_ENV_NAME ? process.env[NOTICE_EMAIL_SECRET_ENV_NAME] : '';
|
|
2196
|
+
if (apiKey && String(apiKey).indexOf('teleporthq.secrets.') === 0) { apiKey = ''; }
|
|
2197
|
+
var from = NOTICE_EMAIL_FROM || process.env.EMAIL_FROM || '';
|
|
2198
|
+
if (!apiKey || !from) { console.warn('[account-signup] set-password link not sent: the email credential or sender is not set'); return; }
|
|
2199
|
+
if (!toEmail || userId == null) { return; }
|
|
2200
|
+
var allowed = await __security.rateLimit({
|
|
2201
|
+
key: 'auth-signup-existing:' + toEmail.trim().toLowerCase(),
|
|
2202
|
+
limit: ${exports.EXISTING_ACCOUNT_EMAIL_LIMIT},
|
|
2203
|
+
windowSeconds: 3600,
|
|
2204
|
+
});
|
|
2205
|
+
if (!allowed.allowed) { return; }
|
|
2206
|
+
var crypto = require('crypto');
|
|
2207
|
+
var token = crypto.randomBytes(32).toString('hex');
|
|
2208
|
+
await authUtils.storePasswordReset(userId, crypto.createHash('sha256').update(token).digest('hex'), SET_PASSWORD_TOKEN_TTL_SECONDS);
|
|
2209
|
+
var resetUrl = trustedOrigin(req) + SET_PASSWORD_PAGE_PATH + SET_PASSWORD_TOKEN_LINK_PREFIX + token;
|
|
2210
|
+
var html = fillTemplate(SET_PASSWORD_BODY_HTML, { siteNameSuffix: siteName ? ' on ' + siteName : '', resetUrl: resetUrl });
|
|
2211
|
+
await sendAccountNotice(
|
|
2212
|
+
${JSON.stringify(transactional_email_code_1.SET_PASSWORD_EMAIL)},
|
|
2213
|
+
{ from: from, to: toEmail, subject: SET_PASSWORD_SUBJECT, html: html, apiKey: apiKey, userId: userId },
|
|
2214
|
+
{
|
|
2215
|
+
subject: SET_PASSWORD_SUBJECT,
|
|
2216
|
+
html: hideToken(html, token),
|
|
2217
|
+
payload: { resetUrl: hideToken(resetUrl, token), recipientEmail: toEmail },
|
|
2218
|
+
}
|
|
2219
|
+
);
|
|
2220
|
+
}`;
|
|
2221
|
+
};
|
|
2222
|
+
const generateSignupRouteFile = (auth, welcome = {}, events = {}, signup = {}) => {
|
|
1076
2223
|
const needsDb = !!auth.dataSourceType;
|
|
2224
|
+
const recordsSignup = needsDb && events.withAutomations === true;
|
|
1077
2225
|
const welcomeProvider = welcome.emailProvider && transactional_email_code_1.SUPPORTED_EMAIL_PROVIDERS.has(welcome.emailProvider)
|
|
1078
2226
|
? welcome.emailProvider
|
|
1079
2227
|
: null;
|
|
1080
|
-
//
|
|
1081
|
-
//
|
|
1082
|
-
//
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
//
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
2228
|
+
// An ALLOW-list: the only body keys copied onto the new user row. Whatever
|
|
2229
|
+
// else the body carries (a role, `is_admin`, `email_verified`, credits, a
|
|
2230
|
+
// plan, a welcome-email config key) never reaches createUser. The columns
|
|
2231
|
+
// the route sets itself (source, consent) are never copied from it either.
|
|
2232
|
+
const signupFields = (signup.fields || ['image']).filter((field) => field && !(0, exports.isPrivilegedUserField)(field) && exports.SIGNUP_OWN_COLUMNS.indexOf(field) === -1);
|
|
2233
|
+
const setPasswordPagePath = (0, password_reset_route_generator_1.resolveResetPagePath)(signup.resetPagePath);
|
|
2234
|
+
// The emails to an address the site already knows go through the sign-up
|
|
2235
|
+
// node's sender, else the forgot-password node's.
|
|
2236
|
+
const fallbackSender = signup.noticeEmail || {};
|
|
2237
|
+
const noticeSender = welcomeProvider
|
|
2238
|
+
? {
|
|
2239
|
+
provider: welcomeProvider,
|
|
2240
|
+
from: welcome.fromEmail || '',
|
|
2241
|
+
secretEnv: welcome.emailSecretEnvName || '',
|
|
1094
2242
|
}
|
|
1095
|
-
|
|
2243
|
+
: {
|
|
2244
|
+
provider: fallbackSender.emailProvider &&
|
|
2245
|
+
transactional_email_code_1.SUPPORTED_EMAIL_PROVIDERS.has(fallbackSender.emailProvider)
|
|
2246
|
+
? fallbackSender.emailProvider
|
|
2247
|
+
: null,
|
|
2248
|
+
from: fallbackSender.fromEmail || '',
|
|
2249
|
+
secretEnv: fallbackSender.emailSecretEnvName || '',
|
|
2250
|
+
};
|
|
1096
2251
|
let dbImport = '';
|
|
1097
2252
|
let createUserCall = '';
|
|
1098
2253
|
let welcomeHelpers = '';
|
|
1099
2254
|
if (needsDb) {
|
|
1100
2255
|
dbImport = `const authUtils = require('../../../utils/auth/auth-options');
|
|
1101
2256
|
const createUser = authUtils.createUser;
|
|
1102
|
-
const
|
|
1103
|
-
const sanitizeUser = authUtils.sanitizeUser;`;
|
|
2257
|
+
const findSignupUserByEmail = authUtils.findSignupUserByEmail;`;
|
|
1104
2258
|
// Baked welcome-email helpers + config (no-op send when no provider set).
|
|
1105
2259
|
welcomeHelpers = `
|
|
1106
2260
|
const WELCOME_EMAIL_PROVIDER = ${JSON.stringify(welcomeProvider)};
|
|
@@ -1139,18 +2293,90 @@ async function sendWelcomeEmail(toEmail, tokenValues, userId, locale) {
|
|
|
1139
2293
|
var from = WELCOME_EMAIL_FROM || process.env.EMAIL_FROM || '';
|
|
1140
2294
|
if (!from) { console.warn('[account-signup] welcome email skipped: sender not configured'); return; }
|
|
1141
2295
|
var copy = resolveWelcomeEmailCopy(locale);
|
|
1142
|
-
var subject =
|
|
2296
|
+
var subject = fillSubjectTemplate(copy.subject, tokenValues);
|
|
1143
2297
|
var html = fillTemplate(copy.body, tokenValues);
|
|
1144
2298
|
await __sendProviderEmail({ from: from, fromName: WELCOME_EMAIL_FROM_NAME, to: toEmail, subject: subject, html: html, apiKey: apiKey, tokenValues: tokenValues, userId: userId });
|
|
1145
2299
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
2300
|
+
|
|
2301
|
+
// A sign-up for an address that already has an account gets the same status,
|
|
2302
|
+
// body and headers as a new one; its time differs from a new address's, which
|
|
2303
|
+
// the account a new sign-up makes reveals anyway. The address's owner is told
|
|
2304
|
+
// by email instead (only the mailbox owner reads it), at most
|
|
2305
|
+
// ${exports.EXISTING_ACCOUNT_EMAIL_LIMIT} times an hour per address, through the known-address sender.
|
|
2306
|
+
var EXISTING_ACCOUNT_SUBJECT = 'You already have an account';
|
|
2307
|
+
var EXISTING_ACCOUNT_BODY_HTML =
|
|
2308
|
+
'<p>Someone, hopefully you, tried to create an account{{siteNameSuffix}} with this email address.</p>' +
|
|
2309
|
+
'<p>You already have one. Sign in with your password, or use "Forgot password" on the sign-in page to choose a new one.</p>' +
|
|
2310
|
+
'<p>If this was not you, you can ignore this email: nothing has changed.</p>';
|
|
2311
|
+
|
|
2312
|
+
async function sendExistingAccountEmail(toEmail, siteName, userId) {
|
|
2313
|
+
if (!NOTICE_EMAIL_PROVIDER || !toEmail) { return; }
|
|
2314
|
+
var apiKey = NOTICE_EMAIL_SECRET_ENV_NAME ? process.env[NOTICE_EMAIL_SECRET_ENV_NAME] : '';
|
|
2315
|
+
if (apiKey && String(apiKey).indexOf('teleporthq.secrets.') === 0) { apiKey = ''; }
|
|
2316
|
+
var from = NOTICE_EMAIL_FROM || process.env.EMAIL_FROM || '';
|
|
2317
|
+
if (!apiKey || !from) { return; }
|
|
2318
|
+
var allowed = await __security.rateLimit({
|
|
2319
|
+
key: 'auth-signup-existing:' + String(toEmail).trim().toLowerCase(),
|
|
2320
|
+
limit: ${exports.EXISTING_ACCOUNT_EMAIL_LIMIT},
|
|
2321
|
+
windowSeconds: 3600,
|
|
2322
|
+
});
|
|
2323
|
+
if (!allowed.allowed) { return; }
|
|
2324
|
+
var tokenValues = { siteNameSuffix: siteName ? ' on ' + siteName : '' };
|
|
2325
|
+
var html = fillTemplate(EXISTING_ACCOUNT_BODY_HTML, tokenValues);
|
|
2326
|
+
await sendAccountNotice(
|
|
2327
|
+
${JSON.stringify(transactional_email_code_1.EXISTING_ACCOUNT_EMAIL)},
|
|
2328
|
+
{ from: from, to: toEmail, subject: EXISTING_ACCOUNT_SUBJECT, html: html, apiKey: apiKey, userId: userId },
|
|
2329
|
+
{ subject: EXISTING_ACCOUNT_SUBJECT, html: html, payload: tokenValues }
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
${generateKnownAddressEmailCode({
|
|
2333
|
+
sender: noticeSender,
|
|
2334
|
+
pagePath: setPasswordPagePath,
|
|
2335
|
+
tokenInFragment: signup.tokenInFragment === true,
|
|
2336
|
+
})}
|
|
2337
|
+
|
|
2338
|
+
function waitUntilElapsed(startedAt, ms) {
|
|
2339
|
+
var left = ms - (Date.now() - startedAt);
|
|
2340
|
+
return left > 0 ? new Promise(function (resolve) { setTimeout(resolve, left); }) : Promise.resolve();
|
|
2341
|
+
}
|
|
2342
|
+
${recordsSignup
|
|
2343
|
+
? (0, exports.generateAccountEventRecorder)({ relativePrefix: '../../..', logTag: '[account-signup]' })
|
|
2344
|
+
: ''}`;
|
|
2345
|
+
createUserCall = ` // The answer is the same for a new account and for an address that already
|
|
2346
|
+
// has one (see sendExistingAccountEmail): built from what the visitor sent.
|
|
2347
|
+
const answer = { user: { email: email, name: name || null } };
|
|
2348
|
+
const __siteName = WELCOME_EMAIL_SITE_NAME ||
|
|
2349
|
+
(req.headers && req.headers.host ? String(req.headers.host).replace(/^www\\./, '').split(':')[0] : '');
|
|
2350
|
+
// In any letter case: \`Ava@site.test\` is the \`ava@site.test\` row.
|
|
2351
|
+
const existing = await findSignupUserByEmail(email);
|
|
2352
|
+
if (existing) {
|
|
2353
|
+
// A newsletter subscriber, a checkout or booking guest or a contact the
|
|
2354
|
+
// owner added has a row but no password (and no OAuth sign-in). Signing
|
|
2355
|
+
// up must not hand that row — and what the site knows about the person —
|
|
2356
|
+
// to whoever types the address first: its owner is mailed a link to set
|
|
2357
|
+
// a password, which proves the mailbox is theirs. An account's owner is
|
|
2358
|
+
// told it already has one. Either way the page is answered alike, so it
|
|
2359
|
+
// never tells a subscriber or a guest from an account.
|
|
2360
|
+
const knownEmail = String(existing.email || email);
|
|
2361
|
+
const existingId = existing.id != null ? existing.id : existing._id;
|
|
2362
|
+
try {
|
|
2363
|
+
if (!existing.password && !existing.provider && authUtils.PASSWORD_RESET_SUPPORTED) {
|
|
2364
|
+
await sendSetPasswordEmail(existing, __siteName, req);
|
|
2365
|
+
} else {
|
|
2366
|
+
// An account; or a row without a password where the store cannot
|
|
2367
|
+
// keep reset links (not Postgres): told it is already known here.
|
|
2368
|
+
await sendExistingAccountEmail(knownEmail, __siteName, existingId);
|
|
2369
|
+
}
|
|
2370
|
+
} catch (existingErr) {
|
|
2371
|
+
console.error('[account-signup] existing-account email failed:', existingErr && existingErr.message ? existingErr.message : existingErr);
|
|
2372
|
+
}
|
|
2373
|
+
${noticeSender.provider
|
|
2374
|
+
? ` await __noticeEmailLog.settleSentEmailLog();
|
|
2375
|
+
`
|
|
2376
|
+
: ''} // At least ${exports.KNOWN_ADDRESS_MIN_RESPONSE_MS} ms, like the forgot-password answer: the time tells
|
|
2377
|
+
// neither which email went out nor whether one did.
|
|
2378
|
+
await waitUntilElapsed(startedAt, ${exports.KNOWN_ADDRESS_MIN_RESPONSE_MS});
|
|
2379
|
+
res.status(201).json(answer);
|
|
1154
2380
|
return;
|
|
1155
2381
|
}
|
|
1156
2382
|
|
|
@@ -1159,12 +2385,16 @@ async function sendWelcomeEmail(toEmail, tokenValues, userId, locale) {
|
|
|
1159
2385
|
email: email,
|
|
1160
2386
|
password: hashedPassword,
|
|
1161
2387
|
role: 'user',
|
|
2388
|
+
// Where the person came from, and their consent as they gave it: the
|
|
2389
|
+
// "Email me news and offers" box ticked ('true') or not. Never assumed.
|
|
2390
|
+
source: 'signup',
|
|
2391
|
+
marketing_consent: body.marketing_consent === 'true',
|
|
1162
2392
|
};
|
|
1163
|
-
|
|
1164
|
-
const
|
|
1165
|
-
for (let i = 0; i <
|
|
1166
|
-
if (
|
|
1167
|
-
userData[
|
|
2393
|
+
// Only the fields this project's sign-up forms collect.
|
|
2394
|
+
const signupFields = ${JSON.stringify(signupFields)};
|
|
2395
|
+
for (let i = 0; i < signupFields.length; i++) {
|
|
2396
|
+
if (Object.prototype.hasOwnProperty.call(body, signupFields[i])) {
|
|
2397
|
+
userData[signupFields[i]] = body[signupFields[i]];
|
|
1168
2398
|
}
|
|
1169
2399
|
}
|
|
1170
2400
|
|
|
@@ -1177,12 +2407,10 @@ async function sendWelcomeEmail(toEmail, tokenValues, userId, locale) {
|
|
|
1177
2407
|
|
|
1178
2408
|
// Best-effort welcome email — a failed send must never fail the signup.
|
|
1179
2409
|
try {
|
|
1180
|
-
const __welcomeSiteName = WELCOME_EMAIL_SITE_NAME ||
|
|
1181
|
-
(req.headers && req.headers.host ? String(req.headers.host).replace(/^www\\./, '').split(':')[0] : '');
|
|
1182
2410
|
await sendWelcomeEmail(email, {
|
|
1183
2411
|
userName: name || (newUser && newUser.name) || 'there',
|
|
1184
2412
|
userEmail: email,
|
|
1185
|
-
siteName:
|
|
2413
|
+
siteName: __siteName,
|
|
1186
2414
|
}, newUser && newUser.id, __emailLocale.resolveRequestLocale(req));
|
|
1187
2415
|
} catch (welcomeErr) {
|
|
1188
2416
|
console.error('[account-signup] welcome email failed:', welcomeErr && welcomeErr.message ? welcomeErr.message : welcomeErr);
|
|
@@ -1191,13 +2419,18 @@ ${welcomeProvider
|
|
|
1191
2419
|
? ` // Land the welcome email's ledger row before replying.
|
|
1192
2420
|
if (typeof __sentEmailLog !== 'undefined') { await __sentEmailLog.settleSentEmailLog(); }
|
|
1193
2421
|
`
|
|
1194
|
-
: ''}
|
|
1195
|
-
|
|
2422
|
+
: ''}${recordsSignup
|
|
2423
|
+
? ` // The account exists: the last thing the signup does is record it.
|
|
2424
|
+
await recordAccountEvent('user-signed-up', newUser, 'signup', req);
|
|
2425
|
+
`
|
|
2426
|
+
: ''}${signup.newsletter === true ? SIGNUP_NEWSLETTER_CODE : ''}
|
|
2427
|
+
res.status(201).json(answer);`;
|
|
1196
2428
|
}
|
|
1197
2429
|
else {
|
|
1198
2430
|
createUserCall = ` res.status(501).json({ error: 'No data source configured for user storage' });`;
|
|
1199
2431
|
}
|
|
1200
2432
|
return `const hashPassword = require('../../../utils/auth/hash-password');
|
|
2433
|
+
const __security = require('${(0, security_runtime_code_1.securityRuntimeSpecifier)('../../..')}');
|
|
1201
2434
|
${dbImport}
|
|
1202
2435
|
${welcomeHelpers}
|
|
1203
2436
|
|
|
@@ -1207,11 +2440,16 @@ module.exports = async function handler(req, res) {
|
|
|
1207
2440
|
return;
|
|
1208
2441
|
}
|
|
1209
2442
|
|
|
2443
|
+
// ${exports.SIGN_UP_LIMIT} sign-ups per visitor per hour.
|
|
2444
|
+
if (!(await __security.rateLimitRequest(req, res, { scope: 'auth-signup', limit: ${exports.SIGN_UP_LIMIT}, windowSeconds: ${exports.SIGN_UP_WINDOW_SECONDS} }))) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
${needsDb ? ' const startedAt = Date.now();\n' : ''}
|
|
1210
2448
|
try {
|
|
1211
|
-
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
|
|
1212
|
-
const email = body.email;
|
|
1213
|
-
const password = body.password;
|
|
1214
|
-
const name = body.name;
|
|
2449
|
+
const body = (typeof req.body === 'string' ? JSON.parse(req.body) : req.body) || {};
|
|
2450
|
+
const email = typeof body.email === 'string' ? body.email : '';
|
|
2451
|
+
const password = typeof body.password === 'string' ? body.password : '';
|
|
2452
|
+
const name = typeof body.name === 'string' ? body.name : '';
|
|
1215
2453
|
|
|
1216
2454
|
if (!email || !password) {
|
|
1217
2455
|
res.status(400).json({ error: 'Email and password are required' });
|
|
@@ -1224,13 +2462,9 @@ module.exports = async function handler(req, res) {
|
|
|
1224
2462
|
return;
|
|
1225
2463
|
}
|
|
1226
2464
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
if (password.length > 128) {
|
|
1233
|
-
res.status(400).json({ error: 'Password must not exceed 128 characters' });
|
|
2465
|
+
const policyError = hashPassword.passwordPolicyError(password);
|
|
2466
|
+
if (policyError) {
|
|
2467
|
+
res.status(400).json({ error: policyError });
|
|
1234
2468
|
return;
|
|
1235
2469
|
}
|
|
1236
2470
|
|
|
@@ -1238,8 +2472,7 @@ module.exports = async function handler(req, res) {
|
|
|
1238
2472
|
|
|
1239
2473
|
${createUserCall}
|
|
1240
2474
|
} catch (error) {
|
|
1241
|
-
|
|
1242
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
2475
|
+
__security.sendClientError(res, 500, 'Sign-up failed. Please try again.', error);
|
|
1243
2476
|
}
|
|
1244
2477
|
};
|
|
1245
2478
|
`;
|
|
@@ -1491,40 +2724,19 @@ function getUserRoleFromToken(token) {
|
|
|
1491
2724
|
return null;
|
|
1492
2725
|
}
|
|
1493
2726
|
|
|
1494
|
-
//
|
|
1495
|
-
//
|
|
1496
|
-
function readCookie(request, name) {
|
|
1497
|
-
try {
|
|
1498
|
-
var c = request.cookies.get(name);
|
|
1499
|
-
if (!c) return null;
|
|
1500
|
-
return typeof c === 'string' ? c : (c.value || null);
|
|
1501
|
-
} catch (e) {
|
|
1502
|
-
return null;
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
// next-auth stores the session token under a secure-prefixed cookie on https
|
|
1507
|
-
// and a plain one on http, and splits large JWTs into ".0"/".1" chunks. The
|
|
2727
|
+
// The session token lives in the "__Host-" cookie on https and the plain one on
|
|
2728
|
+
// http (see the auth options), split into ".0"/".1" chunks when large. The
|
|
1508
2729
|
// Edge runtime's getToken() can return null for a session the Node runtime
|
|
1509
2730
|
// considers valid (e.g. NEXTAUTH_SECRET not identical in the Edge runtime, or
|
|
1510
2731
|
// the JWE simply failing to decode at the edge), which previously redirected
|
|
1511
2732
|
// logged-in users to sign-in. So cookie PRESENCE is the source of truth for
|
|
1512
2733
|
// "is there a session"; getToken is only trusted for the optional role check.
|
|
1513
|
-
//
|
|
1514
|
-
//
|
|
2734
|
+
// Only the cookie the request's protocol allows counts — the same one
|
|
2735
|
+
// __tqSessionTokenCandidates names: a cookie a sibling site planted under
|
|
2736
|
+
// another name is never a session. Server-side (getServerSideProps / API
|
|
2737
|
+
// routes) remain the authoritative validators of the session and role.
|
|
1515
2738
|
function hasSessionCookie(request) {
|
|
1516
|
-
|
|
1517
|
-
'__Secure-next-auth.session-token',
|
|
1518
|
-
'next-auth.session-token',
|
|
1519
|
-
'__Secure-authjs.session-token',
|
|
1520
|
-
'authjs.session-token'
|
|
1521
|
-
];
|
|
1522
|
-
for (var i = 0; i < names.length; i++) {
|
|
1523
|
-
if (readCookie(request, names[i]) || readCookie(request, names[i] + '.0')) {
|
|
1524
|
-
return true;
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
return false;
|
|
2739
|
+
return __tqSessionTokenCandidates(request).length > 0;
|
|
1528
2740
|
}
|
|
1529
2741
|
|
|
1530
2742
|
// Turns a Next.js route pattern into an anchored matcher: "[param]" matches a
|
|
@@ -1787,8 +2999,10 @@ export const config = {
|
|
|
1787
2999
|
// Next.js does NOT run the second (negative-lookahead) matcher for the root
|
|
1788
3000
|
// path, which would leave a page published at "/" (e.g. a protected dashboard)
|
|
1789
3001
|
// publicly reachable. '/' compiles to ^/$ and covers exactly the home route;
|
|
1790
|
-
// the second entry covers every deeper path.
|
|
1791
|
-
|
|
3002
|
+
// the second entry covers every deeper path. Only the API routes are left
|
|
3003
|
+
// out — "/api" and "/api/..." — never a page whose path merely starts with
|
|
3004
|
+
// "api" (a protected "/api-keys" page).
|
|
3005
|
+
matcher: ['/', '/((?!api(?:/|$)|_next/static|_next/image|favicon\\\\.ico).*)'],
|
|
1792
3006
|
};
|
|
1793
3007
|
`;
|
|
1794
3008
|
};
|