@openlup/platform 0.15.0 → 0.17.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/CHANGELOG.md +124 -0
- package/README.md +633 -6
- package/dist/communications/accountingDocumentDelivery.d.ts +35 -0
- package/dist/communications/accountingDocumentDelivery.js +33 -0
- package/dist/communications/authEmailError.d.ts +5 -0
- package/dist/communications/authEmailError.js +8 -0
- package/dist/communications/authEmailPorts.d.ts +2 -0
- package/dist/communications/authEmailPorts.js +1 -0
- package/dist/communications/authSendEmailHook.d.ts +35 -0
- package/dist/communications/authSendEmailHook.js +250 -0
- package/dist/communications/capturedTransactionalDelivery.d.ts +13 -0
- package/dist/communications/capturedTransactionalDelivery.js +14 -0
- package/dist/communications/communicationSync.d.ts +11 -0
- package/dist/communications/communicationSync.js +79 -0
- package/dist/communications/communicationSyncWorker.d.ts +68 -0
- package/dist/communications/communicationSyncWorker.js +218 -0
- package/dist/communications/consentPolicy.d.ts +19 -0
- package/dist/communications/consentPolicy.js +43 -0
- package/dist/communications/consentPurposeRegistry.d.ts +4 -0
- package/dist/communications/consentPurposeRegistry.js +24 -0
- package/dist/communications/contracts/abandonedCart.d.ts +27 -0
- package/dist/communications/contracts/abandonedCart.js +25 -0
- package/dist/communications/contracts/authEmail.d.ts +62 -0
- package/dist/communications/contracts/authEmail.js +1 -0
- package/dist/communications/contracts/backInStock.d.ts +28 -0
- package/dist/communications/contracts/backInStock.js +28 -0
- package/dist/communications/contracts/blocks.d.ts +24 -0
- package/dist/communications/contracts/blocks.js +23 -0
- package/dist/communications/contracts/checkoutExpired.d.ts +22 -0
- package/dist/communications/contracts/checkoutExpired.js +25 -0
- package/dist/communications/contracts/checkoutRecovery.d.ts +26 -0
- package/dist/communications/contracts/checkoutRecovery.js +37 -0
- package/dist/communications/contracts/commerceEmailContent.d.ts +176 -0
- package/dist/communications/contracts/commerceEmailContent.js +1 -0
- package/dist/communications/contracts/commerceEngagementEmailContent.d.ts +95 -0
- package/dist/communications/contracts/commerceEngagementEmailContent.js +1 -0
- package/dist/communications/contracts/consent.d.ts +12 -0
- package/dist/communications/contracts/consent.js +1 -0
- package/dist/communications/contracts/email.d.ts +182 -0
- package/dist/communications/contracts/email.js +1 -0
- package/dist/communications/contracts/guideCard.d.ts +3 -0
- package/dist/communications/contracts/guideCard.js +26 -0
- package/dist/communications/contracts/index.d.ts +51 -0
- package/dist/communications/contracts/index.js +42 -0
- package/dist/communications/contracts/inquiryContent.d.ts +32 -0
- package/dist/communications/contracts/inquiryContent.js +33 -0
- package/dist/communications/contracts/invoiceDocumentContent.d.ts +26 -0
- package/dist/communications/contracts/invoiceDocumentContent.js +36 -0
- package/dist/communications/contracts/marketingAutomation.d.ts +52 -0
- package/dist/communications/contracts/marketingAutomation.js +1 -0
- package/dist/communications/contracts/newsletter.d.ts +63 -0
- package/dist/communications/contracts/newsletter.js +1 -0
- package/dist/communications/contracts/newsletterWebhook.d.ts +26 -0
- package/dist/communications/contracts/newsletterWebhook.js +1 -0
- package/dist/communications/contracts/optOutContent.d.ts +11 -0
- package/dist/communications/contracts/optOutContent.js +17 -0
- package/dist/communications/contracts/orderCanceled.d.ts +25 -0
- package/dist/communications/contracts/orderCanceled.js +27 -0
- package/dist/communications/contracts/orderDraft.d.ts +46 -0
- package/dist/communications/contracts/orderDraft.js +46 -0
- package/dist/communications/contracts/orderPaid.d.ts +53 -0
- package/dist/communications/contracts/orderPaid.js +71 -0
- package/dist/communications/contracts/orderRefunded.d.ts +24 -0
- package/dist/communications/contracts/orderRefunded.js +27 -0
- package/dist/communications/contracts/paymentFailed.d.ts +24 -0
- package/dist/communications/contracts/paymentFailed.js +24 -0
- package/dist/communications/contracts/publicIntake.d.ts +49 -0
- package/dist/communications/contracts/publicIntake.js +1 -0
- package/dist/communications/contracts/render.d.ts +2 -0
- package/dist/communications/contracts/render.js +229 -0
- package/dist/communications/contracts/reorderReminder.d.ts +24 -0
- package/dist/communications/contracts/reorderReminder.js +24 -0
- package/dist/communications/contracts/returnEmails.d.ts +25 -0
- package/dist/communications/contracts/returnEmails.js +62 -0
- package/dist/communications/contracts/reviewEffects.d.ts +26 -0
- package/dist/communications/contracts/reviewEffects.js +25 -0
- package/dist/communications/contracts/reviewRequest.d.ts +26 -0
- package/dist/communications/contracts/reviewRequest.js +24 -0
- package/dist/communications/contracts/roleNotificationContent.d.ts +25 -0
- package/dist/communications/contracts/roleNotificationContent.js +46 -0
- package/dist/communications/contracts/shipmentDelivered.d.ts +23 -0
- package/dist/communications/contracts/shipmentDelivered.js +24 -0
- package/dist/communications/contracts/shipmentDispatched.d.ts +27 -0
- package/dist/communications/contracts/shipmentDispatched.js +35 -0
- package/dist/communications/contracts/shipmentException.d.ts +21 -0
- package/dist/communications/contracts/shipmentException.js +26 -0
- package/dist/communications/contracts/subscriptionActivationActionRequired.d.ts +17 -0
- package/dist/communications/contracts/subscriptionActivationActionRequired.js +22 -0
- package/dist/communications/contracts/subscriptionAddressChanged.d.ts +19 -0
- package/dist/communications/contracts/subscriptionAddressChanged.js +25 -0
- package/dist/communications/contracts/subscriptionCancelled.d.ts +19 -0
- package/dist/communications/contracts/subscriptionCancelled.js +25 -0
- package/dist/communications/contracts/subscriptionCycleSkipped.d.ts +25 -0
- package/dist/communications/contracts/subscriptionCycleSkipped.js +40 -0
- package/dist/communications/contracts/subscriptionDeliveryRescheduled.d.ts +25 -0
- package/dist/communications/contracts/subscriptionDeliveryRescheduled.js +39 -0
- package/dist/communications/contracts/subscriptionEmailContent.d.ts +145 -0
- package/dist/communications/contracts/subscriptionEmailContent.js +1 -0
- package/dist/communications/contracts/subscriptionPackageChanged.d.ts +20 -0
- package/dist/communications/contracts/subscriptionPackageChanged.js +26 -0
- package/dist/communications/contracts/subscriptionPauseReminder.d.ts +23 -0
- package/dist/communications/contracts/subscriptionPauseReminder.js +29 -0
- package/dist/communications/contracts/subscriptionPaused.d.ts +21 -0
- package/dist/communications/contracts/subscriptionPaused.js +25 -0
- package/dist/communications/contracts/subscriptionPaymentExpired.d.ts +34 -0
- package/dist/communications/contracts/subscriptionPaymentExpired.js +37 -0
- package/dist/communications/contracts/subscriptionPaymentFailed.d.ts +49 -0
- package/dist/communications/contracts/subscriptionPaymentFailed.js +56 -0
- package/dist/communications/contracts/subscriptionPaymentRecovered.d.ts +19 -0
- package/dist/communications/contracts/subscriptionPaymentRecovered.js +22 -0
- package/dist/communications/contracts/subscriptionRenewalAtRisk.d.ts +36 -0
- package/dist/communications/contracts/subscriptionRenewalAtRisk.js +36 -0
- package/dist/communications/contracts/subscriptionRenewalUpcoming.d.ts +49 -0
- package/dist/communications/contracts/subscriptionRenewalUpcoming.js +43 -0
- package/dist/communications/contracts/subscriptionResumed.d.ts +23 -0
- package/dist/communications/contracts/subscriptionResumed.js +25 -0
- package/dist/communications/contracts/subscriptionWelcome.d.ts +44 -0
- package/dist/communications/contracts/subscriptionWelcome.js +45 -0
- package/dist/communications/contracts/subscriptionWinback.d.ts +21 -0
- package/dist/communications/contracts/subscriptionWinback.js +28 -0
- package/dist/communications/contracts/transactionalDelivery.d.ts +42 -0
- package/dist/communications/contracts/transactionalDelivery.js +1 -0
- package/dist/communications/contracts/types.d.ts +46 -0
- package/dist/communications/contracts/types.js +1 -0
- package/dist/communications/contribution.d.ts +10 -0
- package/dist/communications/contribution.js +24 -0
- package/dist/communications/deliveryReconciliation.d.ts +64 -0
- package/dist/communications/deliveryReconciliation.js +60 -0
- package/dist/communications/emailEgress.d.ts +38 -0
- package/dist/communications/emailEgress.js +100 -0
- package/dist/communications/emailHealthAssessment.d.ts +39 -0
- package/dist/communications/emailHealthAssessment.js +59 -0
- package/dist/communications/emailHealthMetricsPort.d.ts +17 -0
- package/dist/communications/emailHealthMetricsPort.js +1 -0
- package/dist/communications/emailNotificationControl.d.ts +27 -0
- package/dist/communications/emailNotificationControl.js +10 -0
- package/dist/communications/emailObservation.d.ts +28 -0
- package/dist/communications/emailObservation.js +23 -0
- package/dist/communications/emailTransport.d.ts +82 -0
- package/dist/communications/emailTransport.js +95 -0
- package/dist/communications/errors.d.ts +15 -0
- package/dist/communications/errors.js +30 -0
- package/dist/communications/index.d.ts +46 -0
- package/dist/communications/index.js +38 -0
- package/dist/communications/integrationEventSignature.d.ts +15 -0
- package/dist/communications/integrationEventSignature.js +37 -0
- package/dist/communications/managedTransactionalDeliveryStore.d.ts +12 -0
- package/dist/communications/managedTransactionalDeliveryStore.js +49 -0
- package/dist/communications/nativeOperations.d.ts +79 -0
- package/dist/communications/nativeOperations.js +3 -0
- package/dist/communications/newsletterIntegrationContracts.d.ts +130 -0
- package/dist/communications/newsletterIntegrationContracts.js +128 -0
- package/dist/communications/newsletterWebhookPort.d.ts +11 -0
- package/dist/communications/newsletterWebhookPort.js +84 -0
- package/dist/communications/newsletterWebhookProcessing.d.ts +17 -0
- package/dist/communications/newsletterWebhookProcessing.js +71 -0
- package/dist/communications/originPolicy.d.ts +40 -0
- package/dist/communications/originPolicy.js +100 -0
- package/dist/communications/postgresCommunicationSync.d.ts +79 -0
- package/dist/communications/postgresCommunicationSync.js +111 -0
- package/dist/communications/postgresConsentEvaluator.d.ts +19 -0
- package/dist/communications/postgresConsentEvaluator.js +43 -0
- package/dist/communications/postgresDeliveryReconciliation.d.ts +3 -0
- package/dist/communications/postgresDeliveryReconciliation.js +113 -0
- package/dist/communications/postgresNativeOperations.d.ts +13 -0
- package/dist/communications/postgresNativeOperations.js +63 -0
- package/dist/communications/postgresNotificationRecipients.d.ts +37 -0
- package/dist/communications/postgresNotificationRecipients.js +60 -0
- package/dist/communications/postgresTransactionalDeliveryStore.d.ts +7 -0
- package/dist/communications/postgresTransactionalDeliveryStore.js +55 -0
- package/dist/communications/preferencesToken.d.ts +10 -0
- package/dist/communications/preferencesToken.js +40 -0
- package/dist/communications/publicPreferenceToken.d.ts +24 -0
- package/dist/communications/publicPreferenceToken.js +53 -0
- package/dist/communications/shipmentNotifications.d.ts +34 -0
- package/dist/communications/shipmentNotifications.js +61 -0
- package/dist/communications/sql.d.ts +26 -0
- package/dist/communications/sql.js +7 -0
- package/dist/communications/standardWebhooks.d.ts +14 -0
- package/dist/communications/standardWebhooks.js +73 -0
- package/dist/communications/status.d.ts +11 -0
- package/dist/communications/status.js +36 -0
- package/dist/communications/transactionalDelivery.d.ts +19 -0
- package/dist/communications/transactionalDelivery.js +96 -0
- package/dist/communications/transactionalDeliveryIdentifier.d.ts +3 -0
- package/dist/communications/transactionalDeliveryIdentifier.js +6 -0
- package/dist/communications/unsubscribeToken.d.ts +77 -0
- package/dist/communications/unsubscribeToken.js +150 -0
- package/dist/communications/validation.d.ts +2 -0
- package/dist/communications/validation.js +3 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/http/webhookSignature.d.ts +42 -0
- package/dist/http/webhookSignature.js +86 -0
- package/dist/identity/adminAuthBinding.d.ts +28 -0
- package/dist/identity/adminAuthBinding.js +66 -0
- package/dist/identity/adminAuthorization.d.ts +34 -0
- package/dist/identity/adminAuthorization.js +72 -0
- package/dist/identity/adminContracts.d.ts +70 -0
- package/dist/identity/adminContracts.js +43 -0
- package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
- package/dist/identity/adminMagicLinkHandler.js +94 -0
- package/dist/identity/adminMeHandler.d.ts +15 -0
- package/dist/identity/adminMeHandler.js +49 -0
- package/dist/identity/adminPreflight.d.ts +16 -0
- package/dist/identity/adminPreflight.js +38 -0
- package/dist/identity/adminUserActionPorts.d.ts +31 -0
- package/dist/identity/adminUserActionPorts.js +51 -0
- package/dist/identity/adminUserHandlers.d.ts +38 -0
- package/dist/identity/adminUserHandlers.js +140 -0
- package/dist/identity/authVerifyUrl.d.ts +6 -0
- package/dist/identity/authVerifyUrl.js +54 -0
- package/dist/identity/contracts/contracts.d.ts +8 -0
- package/dist/identity/contracts/contracts.js +2 -0
- package/dist/identity/contracts/emailTrust.d.ts +3 -0
- package/dist/identity/contracts/emailTrust.js +4 -0
- package/dist/identity/contracts/index.d.ts +8 -0
- package/dist/identity/contracts/index.js +8 -0
- package/dist/identity/contracts/oidc.d.ts +3 -0
- package/dist/identity/contracts/oidc.js +9 -0
- package/dist/identity/contracts/operatorAuth.d.ts +35 -0
- package/dist/identity/contracts/operatorAuth.js +53 -0
- package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
- package/dist/identity/contracts/operatorInvitations.js +1 -0
- package/dist/identity/contracts/ports.d.ts +22 -0
- package/dist/identity/contracts/ports.js +1 -0
- package/dist/identity/contracts/privacy.d.ts +62 -0
- package/dist/identity/contracts/privacy.js +99 -0
- package/dist/identity/contracts/providerList.d.ts +3 -0
- package/dist/identity/contracts/providerList.js +11 -0
- package/dist/identity/contracts/types.d.ts +16 -0
- package/dist/identity/contracts/types.js +4 -0
- package/dist/identity/contribution.d.ts +8 -0
- package/dist/identity/contribution.js +22 -0
- package/dist/identity/customerAuth.d.ts +8 -0
- package/dist/identity/customerAuth.js +1 -0
- package/dist/identity/customerAuthorization.d.ts +5 -0
- package/dist/identity/customerAuthorization.js +22 -0
- package/dist/identity/customerContracts.d.ts +42 -0
- package/dist/identity/customerContracts.js +43 -0
- package/dist/identity/customerIdentityBinding.d.ts +44 -0
- package/dist/identity/customerIdentityBinding.js +60 -0
- package/dist/identity/customerIdentityVerifier.d.ts +8 -0
- package/dist/identity/customerIdentityVerifier.js +101 -0
- package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
- package/dist/identity/customerMagicLinkHandler.js +115 -0
- package/dist/identity/customerMeHandler.d.ts +11 -0
- package/dist/identity/customerMeHandler.js +46 -0
- package/dist/identity/customerPorts.d.ts +4 -0
- package/dist/identity/customerPorts.js +1 -0
- package/dist/identity/customerSessionBinding.d.ts +38 -0
- package/dist/identity/customerSessionBinding.js +80 -0
- package/dist/identity/customerSessionIssuer.d.ts +10 -0
- package/dist/identity/customerSessionIssuer.js +57 -0
- package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
- package/dist/identity/customerVerifyOtpHandler.js +41 -0
- package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
- package/dist/identity/directOperatorInvitationStore.js +193 -0
- package/dist/identity/emailTrustPolicy.d.ts +14 -0
- package/dist/identity/emailTrustPolicy.js +16 -0
- package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
- package/dist/identity/identityPersonalDataContributor.js +46 -0
- package/dist/identity/identityReadiness.d.ts +10 -0
- package/dist/identity/identityReadiness.js +25 -0
- package/dist/identity/index.d.ts +67 -0
- package/dist/identity/index.js +66 -0
- package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
- package/dist/identity/inviteAdminUserUseCase.js +75 -0
- package/dist/identity/jwtLibrary.d.ts +54 -0
- package/dist/identity/jwtLibrary.js +1 -0
- package/dist/identity/machineActorWriteFence.d.ts +4 -0
- package/dist/identity/machineActorWriteFence.js +13 -0
- package/dist/identity/magicLinkPorts.d.ts +28 -0
- package/dist/identity/magicLinkPorts.js +1 -0
- package/dist/identity/managedAccountLinkStore.d.ts +2 -0
- package/dist/identity/managedAccountLinkStore.js +93 -0
- package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
- package/dist/identity/managedAdminAuthVerifier.js +27 -0
- package/dist/identity/managedAdminUserGateways.d.ts +123 -0
- package/dist/identity/managedAdminUserGateways.js +127 -0
- package/dist/identity/managedClient.d.ts +90 -0
- package/dist/identity/managedClient.js +10 -0
- package/dist/identity/managedClientAuthLink.d.ts +3 -0
- package/dist/identity/managedClientAuthLink.js +115 -0
- package/dist/identity/managedCustomerMe.d.ts +13 -0
- package/dist/identity/managedCustomerMe.js +30 -0
- package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
- package/dist/identity/managedEmailTrustPolicy.js +105 -0
- package/dist/identity/managedIdentityVerifier.d.ts +2 -0
- package/dist/identity/managedIdentityVerifier.js +16 -0
- package/dist/identity/managedPrivacy.d.ts +4 -0
- package/dist/identity/managedPrivacy.js +39 -0
- package/dist/identity/messages.d.ts +29 -0
- package/dist/identity/messages.js +39 -0
- package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
- package/dist/identity/oidcIdentityVerifier.js +309 -0
- package/dist/identity/oidcPrincipal.d.ts +2 -0
- package/dist/identity/oidcPrincipal.js +10 -0
- package/dist/identity/operatorAdminAuth.d.ts +6 -0
- package/dist/identity/operatorAdminAuth.js +13 -0
- package/dist/identity/operatorAuthHandlers.d.ts +18 -0
- package/dist/identity/operatorAuthHandlers.js +71 -0
- package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
- package/dist/identity/operatorIdentityVerifier.js +19 -0
- package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
- package/dist/identity/operatorInvitationLifecycle.js +15 -0
- package/dist/identity/operatorInvitationPorts.d.ts +31 -0
- package/dist/identity/operatorInvitationPorts.js +1 -0
- package/dist/identity/operatorPorts.d.ts +37 -0
- package/dist/identity/operatorPorts.js +1 -0
- package/dist/identity/operatorSession.d.ts +13 -0
- package/dist/identity/operatorSession.js +31 -0
- package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
- package/dist/identity/operatorTokenAdminAuth.js +50 -0
- package/dist/identity/personalDataRegistry.d.ts +4 -0
- package/dist/identity/personalDataRegistry.js +11 -0
- package/dist/identity/platformAdminAuth.d.ts +15 -0
- package/dist/identity/platformAdminAuth.js +1 -0
- package/dist/identity/ports.d.ts +127 -0
- package/dist/identity/ports.js +38 -0
- package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
- package/dist/identity/postgresAccountLinkStore.js +52 -0
- package/dist/identity/postgresCustomerMe.d.ts +3 -0
- package/dist/identity/postgresCustomerMe.js +21 -0
- package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
- package/dist/identity/postgresCustomerRecovery.js +57 -0
- package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
- package/dist/identity/postgresOperatorIdentity.js +137 -0
- package/dist/identity/postgresPrivacy.d.ts +7 -0
- package/dist/identity/postgresPrivacy.js +126 -0
- package/dist/identity/privacyHandlers.d.ts +10 -0
- package/dist/identity/privacyHandlers.js +35 -0
- package/dist/identity/privacyPorts.d.ts +72 -0
- package/dist/identity/privacyPorts.js +8 -0
- package/dist/identity/privacyUseCases.d.ts +5 -0
- package/dist/identity/privacyUseCases.js +55 -0
- package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
- package/dist/identity/reconcileAccountForPrincipal.js +66 -0
- package/dist/identity/reconcileAccountHandler.d.ts +11 -0
- package/dist/identity/reconcileAccountHandler.js +30 -0
- package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
- package/dist/identity/removeAdminUserUseCase.js +60 -0
- package/dist/identity/response.d.ts +3 -0
- package/dist/identity/response.js +2 -0
- package/dist/identity/sql.d.ts +44 -0
- package/dist/identity/sql.js +10 -0
- package/package.json +22 -2
- package/release-gates.json +63 -8
- package/sql/20260807190000_comms_outbox_rail.sql +329 -0
- package/sql/20260810120001_transactional_delivery_receipts.sql +209 -0
- package/sql/20260810180001_self_host_actor_auth.sql +40 -0
- package/sql/20260810204057_customer_identity_read.sql +31 -0
- package/sql/20260813120000_communications_control_plane.sql +568 -0
- package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
- package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
- package/sql/20261018120000_identity_operator_invitations.sql +42 -0
- package/sql/20261018180000_identity_operator_challenges.sql +56 -0
- package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
- package/sql/20261019200000_communications_native_operations.sql +68 -0
- package/src/communications/accountingDocumentDelivery.ts +63 -0
- package/src/communications/authEmailError.ts +10 -0
- package/src/communications/authEmailPorts.ts +2 -0
- package/src/communications/authSendEmailHook.ts +310 -0
- package/src/communications/capturedTransactionalDelivery.ts +29 -0
- package/src/communications/communicationSync.ts +96 -0
- package/src/communications/communicationSyncWorker.ts +302 -0
- package/src/communications/consentPolicy.ts +82 -0
- package/src/communications/consentPurposeRegistry.ts +31 -0
- package/src/communications/contracts/abandonedCart.ts +64 -0
- package/src/communications/contracts/authEmail.ts +73 -0
- package/src/communications/contracts/backInStock.ts +69 -0
- package/src/communications/contracts/blocks.ts +47 -0
- package/src/communications/contracts/checkoutExpired.ts +62 -0
- package/src/communications/contracts/checkoutRecovery.ts +68 -0
- package/src/communications/contracts/commerceEmailContent.ts +189 -0
- package/src/communications/contracts/commerceEngagementEmailContent.ts +106 -0
- package/src/communications/contracts/consent.ts +13 -0
- package/src/communications/contracts/email.ts +210 -0
- package/src/communications/contracts/guideCard.ts +34 -0
- package/src/communications/contracts/index.ts +51 -0
- package/src/communications/contracts/inquiryContent.ts +53 -0
- package/src/communications/contracts/invoiceDocumentContent.ts +57 -0
- package/src/communications/contracts/marketingAutomation.ts +45 -0
- package/src/communications/contracts/newsletter.ts +78 -0
- package/src/communications/contracts/newsletterWebhook.ts +23 -0
- package/src/communications/contracts/optOutContent.ts +23 -0
- package/src/communications/contracts/orderCanceled.ts +64 -0
- package/src/communications/contracts/orderDraft.ts +112 -0
- package/src/communications/contracts/orderPaid.ts +147 -0
- package/src/communications/contracts/orderRefunded.ts +63 -0
- package/src/communications/contracts/paymentFailed.ts +59 -0
- package/src/communications/contracts/publicIntake.ts +53 -0
- package/src/communications/contracts/render.ts +271 -0
- package/src/communications/contracts/reorderReminder.ts +59 -0
- package/src/communications/contracts/returnEmails.ts +113 -0
- package/src/communications/contracts/reviewEffects.ts +60 -0
- package/src/communications/contracts/reviewRequest.ts +59 -0
- package/src/communications/contracts/roleNotificationContent.ts +65 -0
- package/src/communications/contracts/shipmentDelivered.ts +59 -0
- package/src/communications/contracts/shipmentDispatched.ts +77 -0
- package/src/communications/contracts/shipmentException.ts +62 -0
- package/src/communications/contracts/subscriptionActivationActionRequired.ts +37 -0
- package/src/communications/contracts/subscriptionAddressChanged.ts +56 -0
- package/src/communications/contracts/subscriptionCancelled.ts +57 -0
- package/src/communications/contracts/subscriptionCycleSkipped.ts +80 -0
- package/src/communications/contracts/subscriptionDeliveryRescheduled.ts +79 -0
- package/src/communications/contracts/subscriptionEmailContent.ts +163 -0
- package/src/communications/contracts/subscriptionPackageChanged.ts +59 -0
- package/src/communications/contracts/subscriptionPauseReminder.ts +63 -0
- package/src/communications/contracts/subscriptionPaused.ts +58 -0
- package/src/communications/contracts/subscriptionPaymentExpired.ts +83 -0
- package/src/communications/contracts/subscriptionPaymentFailed.ts +117 -0
- package/src/communications/contracts/subscriptionPaymentRecovered.ts +50 -0
- package/src/communications/contracts/subscriptionRenewalAtRisk.ts +85 -0
- package/src/communications/contracts/subscriptionRenewalUpcoming.ts +103 -0
- package/src/communications/contracts/subscriptionResumed.ts +60 -0
- package/src/communications/contracts/subscriptionWelcome.ts +102 -0
- package/src/communications/contracts/subscriptionWinback.ts +62 -0
- package/src/communications/contracts/transactionalDelivery.ts +43 -0
- package/src/communications/contracts/types.ts +58 -0
- package/src/communications/contribution.ts +33 -0
- package/src/communications/deliveryReconciliation.ts +108 -0
- package/src/communications/emailEgress.ts +111 -0
- package/src/communications/emailHealthAssessment.ts +106 -0
- package/src/communications/emailHealthMetricsPort.ts +17 -0
- package/src/communications/emailNotificationControl.ts +28 -0
- package/src/communications/emailObservation.ts +43 -0
- package/src/communications/emailTransport.ts +167 -0
- package/src/communications/errors.ts +34 -0
- package/src/communications/index.ts +46 -0
- package/src/communications/integrationEventSignature.ts +49 -0
- package/src/communications/managedTransactionalDeliveryStore.ts +59 -0
- package/src/communications/nativeOperations.ts +40 -0
- package/src/communications/newsletterIntegrationContracts.ts +154 -0
- package/src/communications/newsletterWebhookPort.ts +104 -0
- package/src/communications/newsletterWebhookProcessing.ts +102 -0
- package/src/communications/originPolicy.ts +151 -0
- package/src/communications/postgresCommunicationSync.ts +127 -0
- package/src/communications/postgresConsentEvaluator.ts +49 -0
- package/src/communications/postgresDeliveryReconciliation.ts +101 -0
- package/src/communications/postgresNativeOperations.ts +55 -0
- package/src/communications/postgresNotificationRecipients.ts +57 -0
- package/src/communications/postgresTransactionalDeliveryStore.ts +79 -0
- package/src/communications/preferencesToken.ts +62 -0
- package/src/communications/publicPreferenceToken.ts +68 -0
- package/src/communications/shipmentNotifications.ts +78 -0
- package/src/communications/sql.ts +7 -0
- package/src/communications/standardWebhooks.ts +84 -0
- package/src/communications/status.ts +51 -0
- package/src/communications/transactionalDelivery.ts +114 -0
- package/src/communications/transactionalDeliveryIdentifier.ts +9 -0
- package/src/communications/unsubscribeToken.ts +186 -0
- package/src/communications/validation.ts +3 -0
- package/src/http/index.ts +1 -0
- package/src/http/webhookSignature.ts +96 -0
- package/src/identity/adminAuthBinding.ts +76 -0
- package/src/identity/adminAuthorization.ts +106 -0
- package/src/identity/adminContracts.ts +52 -0
- package/src/identity/adminMagicLinkHandler.ts +132 -0
- package/src/identity/adminMeHandler.ts +64 -0
- package/src/identity/adminPreflight.ts +46 -0
- package/src/identity/adminUserActionPorts.ts +78 -0
- package/src/identity/adminUserHandlers.ts +174 -0
- package/src/identity/authVerifyUrl.ts +56 -0
- package/src/identity/contracts/contracts.ts +15 -0
- package/src/identity/contracts/emailTrust.ts +5 -0
- package/src/identity/contracts/index.ts +8 -0
- package/src/identity/contracts/oidc.ts +10 -0
- package/src/identity/contracts/operatorAuth.ts +61 -0
- package/src/identity/contracts/operatorInvitations.ts +32 -0
- package/src/identity/contracts/ports.ts +25 -0
- package/src/identity/contracts/privacy.ts +86 -0
- package/src/identity/contracts/providerList.ts +13 -0
- package/src/identity/contracts/types.ts +25 -0
- package/src/identity/contribution.ts +29 -0
- package/src/identity/customerAuth.ts +3 -0
- package/src/identity/customerAuthorization.ts +38 -0
- package/src/identity/customerContracts.ts +52 -0
- package/src/identity/customerIdentityBinding.ts +125 -0
- package/src/identity/customerIdentityVerifier.ts +141 -0
- package/src/identity/customerMagicLinkHandler.ts +171 -0
- package/src/identity/customerMeHandler.ts +64 -0
- package/src/identity/customerPorts.ts +2 -0
- package/src/identity/customerSessionBinding.ts +114 -0
- package/src/identity/customerSessionIssuer.ts +71 -0
- package/src/identity/customerVerifyOtpHandler.ts +44 -0
- package/src/identity/directOperatorInvitationStore.ts +178 -0
- package/src/identity/emailTrustPolicy.ts +16 -0
- package/src/identity/identityPersonalDataContributor.ts +48 -0
- package/src/identity/identityReadiness.ts +26 -0
- package/src/identity/index.ts +69 -0
- package/src/identity/inviteAdminUserUseCase.ts +134 -0
- package/src/identity/jwtLibrary.ts +24 -0
- package/src/identity/machineActorWriteFence.ts +19 -0
- package/src/identity/magicLinkPorts.ts +7 -0
- package/src/identity/managedAccountLinkStore.ts +105 -0
- package/src/identity/managedAdminAuthVerifier.ts +40 -0
- package/src/identity/managedAdminUserGateways.ts +164 -0
- package/src/identity/managedClient.ts +58 -0
- package/src/identity/managedClientAuthLink.ts +120 -0
- package/src/identity/managedCustomerMe.ts +52 -0
- package/src/identity/managedEmailTrustPolicy.ts +90 -0
- package/src/identity/managedIdentityVerifier.ts +20 -0
- package/src/identity/managedPrivacy.ts +37 -0
- package/src/identity/messages.ts +41 -0
- package/src/identity/oidcIdentityVerifier.ts +229 -0
- package/src/identity/oidcPrincipal.ts +10 -0
- package/src/identity/operatorAdminAuth.ts +14 -0
- package/src/identity/operatorAuthHandlers.ts +52 -0
- package/src/identity/operatorIdentityVerifier.ts +19 -0
- package/src/identity/operatorInvitationLifecycle.ts +14 -0
- package/src/identity/operatorInvitationPorts.ts +11 -0
- package/src/identity/operatorPorts.ts +11 -0
- package/src/identity/operatorSession.ts +30 -0
- package/src/identity/operatorTokenAdminAuth.ts +79 -0
- package/src/identity/personalDataRegistry.ts +8 -0
- package/src/identity/platformAdminAuth.ts +7 -0
- package/src/identity/ports.ts +158 -0
- package/src/identity/postgresAccountLinkStore.ts +49 -0
- package/src/identity/postgresCustomerMe.ts +33 -0
- package/src/identity/postgresCustomerRecovery.ts +72 -0
- package/src/identity/postgresOperatorIdentity.ts +123 -0
- package/src/identity/postgresPrivacy.ts +117 -0
- package/src/identity/privacyHandlers.ts +33 -0
- package/src/identity/privacyPorts.ts +52 -0
- package/src/identity/privacyUseCases.ts +48 -0
- package/src/identity/reconcileAccountForPrincipal.ts +99 -0
- package/src/identity/reconcileAccountHandler.ts +36 -0
- package/src/identity/removeAdminUserUseCase.ts +85 -0
- package/src/identity/response.ts +3 -0
- package/src/identity/sql.ts +10 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
/** Hard cap on raw webhook body bytes — the payment provider events are well under 256 KiB. */
|
|
3
|
+
export const MAX_WEBHOOK_BODY_BYTES = 1_048_576;
|
|
4
|
+
/** Thrown by readRawBody when the streamed body exceeds MAX_WEBHOOK_BODY_BYTES. */
|
|
5
|
+
export class WebhookBodyTooLargeError extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("Webhook body exceeds maximum size");
|
|
8
|
+
this.name = "WebhookBodyTooLargeError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Reads the exact raw request body bytes for provider webhook signature
|
|
13
|
+
* verification.
|
|
14
|
+
*
|
|
15
|
+
* `@vercel/node` (v3, the builder for `/api` functions in this Vite project)
|
|
16
|
+
* auto-parses `application/json` request bodies into `req.body` and IGNORES the
|
|
17
|
+
* `export const config = { api: { bodyParser: false } }` opt-out — that opt-out
|
|
18
|
+
* is a Next.js Pages Router convention, not a vanilla `@vercel/node` one
|
|
19
|
+
* (verified empirically against the hidden-preview deploy: a JSON payload posted
|
|
20
|
+
* with `Content-Type: application/json` arrives parsed, the same bytes posted as
|
|
21
|
+
* `text/plain` arrive raw). Re-serializing the parsed object with
|
|
22
|
+
* `JSON.stringify` does NOT reproduce the provider's exact bytes — the payment provider signs
|
|
23
|
+
* pretty-printed JSON, so a compacted re-serialization breaks HMAC verification.
|
|
24
|
+
*
|
|
25
|
+
* Reading the underlying request stream FIRST — before touching `req.body`,
|
|
26
|
+
* whose getter triggers the parse that consumes the stream — preserves the exact
|
|
27
|
+
* bytes when the platform parses lazily. When `req` is not a stream (unit tests
|
|
28
|
+
* pass a plain `{ body }` object) or the stream yields nothing (already
|
|
29
|
+
* consumed), we fall back to `req.body`.
|
|
30
|
+
*/
|
|
31
|
+
export async function readRawBody(req) {
|
|
32
|
+
if (isAsyncIterable(req)) {
|
|
33
|
+
const chunks = [];
|
|
34
|
+
let total = 0;
|
|
35
|
+
for await (const chunk of req) {
|
|
36
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
37
|
+
total += buf.byteLength;
|
|
38
|
+
// Enforce the cap mid-stream so an oversized body is rejected before it is
|
|
39
|
+
// fully buffered into memory, not after.
|
|
40
|
+
if (total > MAX_WEBHOOK_BODY_BYTES)
|
|
41
|
+
throw new WebhookBodyTooLargeError();
|
|
42
|
+
chunks.push(buf);
|
|
43
|
+
}
|
|
44
|
+
if (chunks.length > 0)
|
|
45
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
46
|
+
}
|
|
47
|
+
if (typeof req.body === "string")
|
|
48
|
+
return capBody(req.body);
|
|
49
|
+
if (Buffer.isBuffer(req.body))
|
|
50
|
+
return capBody(req.body.toString("utf8"));
|
|
51
|
+
if (req.body && typeof req.body === "object")
|
|
52
|
+
return capBody(JSON.stringify(req.body));
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
function capBody(body) {
|
|
56
|
+
if (Buffer.byteLength(body, "utf8") > MAX_WEBHOOK_BODY_BYTES) {
|
|
57
|
+
throw new WebhookBodyTooLargeError();
|
|
58
|
+
}
|
|
59
|
+
return body;
|
|
60
|
+
}
|
|
61
|
+
function isAsyncIterable(value) {
|
|
62
|
+
return (value != null &&
|
|
63
|
+
typeof value[Symbol.asyncIterator] ===
|
|
64
|
+
"function");
|
|
65
|
+
}
|
|
66
|
+
export function verifyHmacSignature(input) {
|
|
67
|
+
if (!input.signatureHeader || !input.secret)
|
|
68
|
+
return false;
|
|
69
|
+
return timingSafeStringEqual(input.signatureHeader, hmac(input.rawBody, input.secret));
|
|
70
|
+
}
|
|
71
|
+
function hmac(value, secret) {
|
|
72
|
+
return createHmac("sha256", secret).update(value).digest("hex");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Constant-time comparison of two shared-secret strings.
|
|
76
|
+
*
|
|
77
|
+
* Exported because it is not payment-specific: any route holding a shared token has to compare it
|
|
78
|
+
* the same way, and a second hand-rolled copy of a security primitive is how one of them quietly
|
|
79
|
+
* stops being constant-time. The length check comes first because `timingSafeEqual` THROWS on a
|
|
80
|
+
* length mismatch rather than returning false, and the length of a shared token is not the secret.
|
|
81
|
+
*/
|
|
82
|
+
export function timingSafeStringEqual(left, right) {
|
|
83
|
+
const leftBuffer = Buffer.from(left);
|
|
84
|
+
const rightBuffer = Buffer.from(right);
|
|
85
|
+
return leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer);
|
|
86
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AdminAuthPort } from "./ports.js";
|
|
2
|
+
import type { AdminAuthBinding } from "./adminPreflight.js";
|
|
3
|
+
type Env = Record<string, string | undefined>;
|
|
4
|
+
export interface PlatformOperatorChecker {
|
|
5
|
+
isOperatorAllowed(principalId: string): Promise<boolean>;
|
|
6
|
+
close(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type PlatformOperatorCheckerFactory = (env: Env) => PlatformOperatorChecker | Promise<PlatformOperatorChecker>;
|
|
9
|
+
export type AdminAuthBindingResolution = {
|
|
10
|
+
readonly binding: AdminAuthBinding;
|
|
11
|
+
readonly error?: undefined;
|
|
12
|
+
} | {
|
|
13
|
+
readonly binding?: undefined;
|
|
14
|
+
readonly error: "managed_admin_auth_env_required" | "direct_admin_auth_config_invalid";
|
|
15
|
+
};
|
|
16
|
+
export interface AdminAuthBindingOptions {
|
|
17
|
+
humanAuthFactory?: (env: Env) => AdminAuthPort & {
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
checkerFactory?: PlatformOperatorCheckerFactory;
|
|
21
|
+
managedAuthFactory?: (env: {
|
|
22
|
+
url: string;
|
|
23
|
+
anonKey: string;
|
|
24
|
+
}, accessToken: string | null) => AdminAuthPort;
|
|
25
|
+
}
|
|
26
|
+
/** Bind the admin authorizer selected by PLATFORM_BUNDLE. */
|
|
27
|
+
export declare function createAdminAuthBindingResolver(defaults: Required<Pick<AdminAuthBindingOptions, "checkerFactory" | "managedAuthFactory">> & Pick<AdminAuthBindingOptions, "humanAuthFactory">): (env: Env, options?: AdminAuthBindingOptions) => AdminAuthBindingResolution;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createOperatorTokenAdminAuth, resolvePlatformOperatorAuthConfig } from "./operatorTokenAdminAuth.js";
|
|
2
|
+
import { resolveBundleId } from "../kernel/index.js";
|
|
3
|
+
function managedAuthEnv(env) {
|
|
4
|
+
const url = env.SUPABASE_URL ?? env.VITE_SUPABASE_URL;
|
|
5
|
+
const anonKey = env.VITE_SUPABASE_PUBLISHABLE_KEY ?? env.VITE_SUPABASE_ANON_KEY ?? env.SUPABASE_ANON_KEY;
|
|
6
|
+
return url && anonKey ? { url, anonKey } : null;
|
|
7
|
+
}
|
|
8
|
+
/** Bind the admin authorizer selected by PLATFORM_BUNDLE. */
|
|
9
|
+
export function createAdminAuthBindingResolver(defaults) {
|
|
10
|
+
return function resolveAdminAuthBinding(env, options = {}) {
|
|
11
|
+
const bundleId = resolveBundleId(env);
|
|
12
|
+
if (bundleId === "node-postgres") {
|
|
13
|
+
const config = resolvePlatformOperatorAuthConfig(env);
|
|
14
|
+
const humanFactory = options.humanAuthFactory ?? defaults.humanAuthFactory;
|
|
15
|
+
const humanEnabled = !!env.OPERATOR_IDENTITY_AUDIENCE && !!humanFactory;
|
|
16
|
+
if (!config.config && !humanEnabled)
|
|
17
|
+
return { error: "direct_admin_auth_config_invalid" };
|
|
18
|
+
const checkerFactory = options.checkerFactory ?? defaults.checkerFactory;
|
|
19
|
+
return {
|
|
20
|
+
binding: {
|
|
21
|
+
identity: bundleId,
|
|
22
|
+
async run(accessToken, work) {
|
|
23
|
+
const scope = {};
|
|
24
|
+
const machine = config.config ? createOperatorTokenAdminAuth(config.config, async (principalId) => {
|
|
25
|
+
scope.checker ??= await checkerFactory(env);
|
|
26
|
+
return scope.checker.isOperatorAllowed(principalId);
|
|
27
|
+
}) : null;
|
|
28
|
+
let human;
|
|
29
|
+
const auth = { async authorize(token, restrictions) {
|
|
30
|
+
// Only a credential mismatch may select human verification; machine
|
|
31
|
+
// revocation and database errors retain their existing result.
|
|
32
|
+
const result = machine ? await machine.authorize(token, restrictions) : null;
|
|
33
|
+
if (result && (result.ok || result.code !== "UNAUTHORIZED"))
|
|
34
|
+
return result;
|
|
35
|
+
if (!humanEnabled || !token)
|
|
36
|
+
return result ?? { ok: false, code: "UNAUTHORIZED", message: "Operator authentication required" };
|
|
37
|
+
human ??= humanFactory(env);
|
|
38
|
+
return human.authorize(token, restrictions);
|
|
39
|
+
} };
|
|
40
|
+
try {
|
|
41
|
+
return await work(auth);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
try {
|
|
45
|
+
await scope.checker?.close();
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await human?.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const authEnv = managedAuthEnv(env);
|
|
56
|
+
if (!authEnv)
|
|
57
|
+
return { error: "managed_admin_auth_env_required" };
|
|
58
|
+
const managedAuthFactory = options.managedAuthFactory ?? defaults.managedAuthFactory;
|
|
59
|
+
return {
|
|
60
|
+
binding: {
|
|
61
|
+
identity: bundleId,
|
|
62
|
+
run: (accessToken, work) => work(managedAuthFactory(authEnv, accessToken)),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { HttpResponse, HttpRequest } from "../http/index.js";
|
|
2
|
+
import { type IdentityResponseOptions } from "./response.js";
|
|
3
|
+
export type AdminRole = "admin" | "distributor";
|
|
4
|
+
/** Authorization outcome: a resolved admin `userId`, or a typed denial. */
|
|
5
|
+
export type AdminAuthorization = {
|
|
6
|
+
ok: true;
|
|
7
|
+
userId: string;
|
|
8
|
+
role: AdminRole;
|
|
9
|
+
isMachineActor: boolean;
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
code: "UNAUTHORIZED" | "FORBIDDEN";
|
|
13
|
+
message: string;
|
|
14
|
+
identityCode?: string;
|
|
15
|
+
};
|
|
16
|
+
/** A request-bound thunk that resolves the calling admin (or a denial). */
|
|
17
|
+
export type AuthorizeAdmin = () => Promise<AdminAuthorization>;
|
|
18
|
+
export declare function authorizeAdminWithUser(rawClient: unknown, accessToken: string | null, options?: {
|
|
19
|
+
allowedRoles?: readonly AdminRole[];
|
|
20
|
+
}): Promise<AdminAuthorization>;
|
|
21
|
+
export declare function authorizeCommerceAdminWithUser(client: unknown, accessToken: string | null): Promise<AdminAuthorization>;
|
|
22
|
+
export declare function authorizeAdminBooleanWithUser(client: unknown, accessToken: string | null, options?: {
|
|
23
|
+
allowedRoles?: readonly AdminRole[];
|
|
24
|
+
}): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Shared authz gate: returns the resolved admin (`userId` + DB-derived
|
|
27
|
+
* `isMachineActor`) or sends the error envelope and returns null.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveAdmin(authorizeAdmin: AuthorizeAdmin, res: HttpResponse, options?: IdentityResponseOptions): Promise<{
|
|
30
|
+
userId: string;
|
|
31
|
+
role: AdminRole;
|
|
32
|
+
isMachineActor: boolean;
|
|
33
|
+
} | null>;
|
|
34
|
+
export declare function readBearerToken(req: HttpRequest): string | null;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createIdentityResponder } from "./response.js";
|
|
2
|
+
export async function authorizeAdminWithUser(rawClient, accessToken, options = {}) {
|
|
3
|
+
const client = rawClient;
|
|
4
|
+
if (!accessToken) {
|
|
5
|
+
return { ok: false, code: "UNAUTHORIZED", message: "Admin session required" };
|
|
6
|
+
}
|
|
7
|
+
const { data: userData, error: userError } = await client.auth.getUser(accessToken);
|
|
8
|
+
if (userError || !userData.user) {
|
|
9
|
+
return { ok: false, code: "UNAUTHORIZED", message: "Admin session required" };
|
|
10
|
+
}
|
|
11
|
+
const { data, error } = await client
|
|
12
|
+
.from("admin_users")
|
|
13
|
+
.select("id, role, is_machine_actor")
|
|
14
|
+
.eq("id", userData.user.id)
|
|
15
|
+
.eq("membership_state", "active")
|
|
16
|
+
.maybeSingle();
|
|
17
|
+
if (error)
|
|
18
|
+
throw error;
|
|
19
|
+
if (!data) {
|
|
20
|
+
return { ok: false, code: "FORBIDDEN", message: "Admin role required" };
|
|
21
|
+
}
|
|
22
|
+
const role = normalizeAdminRole(data.role);
|
|
23
|
+
if (role === null || (options.allowedRoles && !options.allowedRoles.includes(role))) {
|
|
24
|
+
return { ok: false, code: "FORBIDDEN", message: "Admin role required" };
|
|
25
|
+
}
|
|
26
|
+
// Fail closed: mirror the activate RPC's `COALESCE(is_machine_actor, true)` — only an
|
|
27
|
+
// explicit `false` is a human actor; anything else is treated as a machine. The DB row
|
|
28
|
+
// (`admin_users.is_machine_actor`, re-derived here and in every write RPC) is the single
|
|
29
|
+
// source of truth for actor kind — never trusted from the caller.
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
userId: userData.user.id,
|
|
33
|
+
role,
|
|
34
|
+
isMachineActor: data.is_machine_actor !== false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function authorizeCommerceAdminWithUser(client, accessToken) {
|
|
38
|
+
return authorizeAdminWithUser(client, accessToken, { allowedRoles: ["admin"] });
|
|
39
|
+
}
|
|
40
|
+
export async function authorizeAdminBooleanWithUser(client, accessToken, options = {}) {
|
|
41
|
+
const authorization = await authorizeAdminWithUser(client, accessToken, options);
|
|
42
|
+
return authorization.ok;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Shared authz gate: returns the resolved admin (`userId` + DB-derived
|
|
46
|
+
* `isMachineActor`) or sends the error envelope and returns null.
|
|
47
|
+
*/
|
|
48
|
+
export async function resolveAdmin(authorizeAdmin, res, options = {}) {
|
|
49
|
+
const { sendBffError } = createIdentityResponder(options);
|
|
50
|
+
let authz;
|
|
51
|
+
try {
|
|
52
|
+
authz = await authorizeAdmin();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin authorization failed");
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (authz.ok === false) {
|
|
59
|
+
sendBffError(res, authz.code, authz.message);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return { userId: authz.userId, role: authz.role, isMachineActor: authz.isMachineActor };
|
|
63
|
+
}
|
|
64
|
+
function normalizeAdminRole(role) {
|
|
65
|
+
return role === "admin" || role === "distributor" ? role : null;
|
|
66
|
+
}
|
|
67
|
+
export function readBearerToken(req) {
|
|
68
|
+
const raw = req.headers.authorization;
|
|
69
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
70
|
+
const match = value?.match(/^Bearer\s+(.+)$/i);
|
|
71
|
+
return match?.[1]?.trim() || null;
|
|
72
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const adminUserRoleSchema: z.ZodEnum<{
|
|
3
|
+
admin: "admin";
|
|
4
|
+
distributor: "distributor";
|
|
5
|
+
}>;
|
|
6
|
+
export declare const adminPlatformMeRequestSchema: z.ZodObject<{}, z.core.$strict>;
|
|
7
|
+
export declare const adminPlatformMeResponseSchema: z.ZodObject<{
|
|
8
|
+
isAdmin: z.ZodBoolean;
|
|
9
|
+
role: z.ZodNullable<z.ZodEnum<{
|
|
10
|
+
admin: "admin";
|
|
11
|
+
distributor: "distributor";
|
|
12
|
+
}>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export declare const adminUserRoleUpdateRequestSchema: z.ZodObject<{
|
|
15
|
+
userId: z.ZodString;
|
|
16
|
+
role: z.ZodEnum<{
|
|
17
|
+
admin: "admin";
|
|
18
|
+
distributor: "distributor";
|
|
19
|
+
}>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export declare const adminUserRoleUpdateResponseSchema: z.ZodObject<{
|
|
22
|
+
userId: z.ZodString;
|
|
23
|
+
role: z.ZodEnum<{
|
|
24
|
+
admin: "admin";
|
|
25
|
+
distributor: "distributor";
|
|
26
|
+
}>;
|
|
27
|
+
saved: z.ZodLiteral<true>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
export declare const adminUserInviteRequestSchema: z.ZodObject<{
|
|
30
|
+
email: z.ZodString;
|
|
31
|
+
role: z.ZodEnum<{
|
|
32
|
+
admin: "admin";
|
|
33
|
+
distributor: "distributor";
|
|
34
|
+
}>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
export declare const adminUserInviteResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
37
|
+
principalId: z.ZodString;
|
|
38
|
+
invitationId: z.ZodString;
|
|
39
|
+
membershipState: z.ZodLiteral<"invited">;
|
|
40
|
+
role: z.ZodEnum<{
|
|
41
|
+
admin: "admin";
|
|
42
|
+
distributor: "distributor";
|
|
43
|
+
}>;
|
|
44
|
+
invitedAt: z.ZodISODateTime;
|
|
45
|
+
expiresAt: z.ZodISODateTime;
|
|
46
|
+
code: z.ZodEnum<{
|
|
47
|
+
INVITATION_CREATED: "INVITATION_CREATED";
|
|
48
|
+
ALREADY_INVITED: "ALREADY_INVITED";
|
|
49
|
+
INVITATION_REISSUED: "INVITATION_REISSUED";
|
|
50
|
+
}>;
|
|
51
|
+
message: z.ZodOptional<z.ZodString>;
|
|
52
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
53
|
+
principalId: z.ZodString;
|
|
54
|
+
membershipState: z.ZodLiteral<"active">;
|
|
55
|
+
role: z.ZodEnum<{
|
|
56
|
+
admin: "admin";
|
|
57
|
+
distributor: "distributor";
|
|
58
|
+
}>;
|
|
59
|
+
code: z.ZodEnum<{
|
|
60
|
+
MEMBERSHIP_CREATED: "MEMBERSHIP_CREATED";
|
|
61
|
+
ALREADY_ACTIVE: "ALREADY_ACTIVE";
|
|
62
|
+
}>;
|
|
63
|
+
message: z.ZodOptional<z.ZodString>;
|
|
64
|
+
}, z.core.$strip>], "membershipState">;
|
|
65
|
+
export declare const adminUserRemoveRequestSchema: z.ZodObject<{
|
|
66
|
+
userId: z.ZodString;
|
|
67
|
+
}, z.core.$strip>;
|
|
68
|
+
export declare const adminUserRemoveResponseSchema: z.ZodObject<{
|
|
69
|
+
revoked: z.ZodLiteral<true>;
|
|
70
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
z.config({ jitless: true });
|
|
3
|
+
export const adminUserRoleSchema = z.enum(["admin", "distributor"]);
|
|
4
|
+
export const adminPlatformMeRequestSchema = z.object({}).strict();
|
|
5
|
+
export const adminPlatformMeResponseSchema = z.object({
|
|
6
|
+
isAdmin: z.boolean(),
|
|
7
|
+
role: adminUserRoleSchema.nullable(),
|
|
8
|
+
});
|
|
9
|
+
export const adminUserRoleUpdateRequestSchema = z.object({
|
|
10
|
+
userId: z.string().min(1),
|
|
11
|
+
role: adminUserRoleSchema,
|
|
12
|
+
});
|
|
13
|
+
export const adminUserRoleUpdateResponseSchema = z.object({
|
|
14
|
+
userId: z.string().min(1),
|
|
15
|
+
role: adminUserRoleSchema,
|
|
16
|
+
saved: z.literal(true),
|
|
17
|
+
});
|
|
18
|
+
export const adminUserInviteRequestSchema = z.object({
|
|
19
|
+
email: z.string().trim().email().toLowerCase(),
|
|
20
|
+
role: adminUserRoleSchema,
|
|
21
|
+
});
|
|
22
|
+
export const adminUserInviteResponseSchema = z.discriminatedUnion("membershipState", [z.object({
|
|
23
|
+
principalId: z.string().min(1),
|
|
24
|
+
invitationId: z.string().min(1),
|
|
25
|
+
membershipState: z.literal("invited"),
|
|
26
|
+
role: adminUserRoleSchema,
|
|
27
|
+
invitedAt: z.iso.datetime(),
|
|
28
|
+
expiresAt: z.iso.datetime(),
|
|
29
|
+
code: z.enum(["INVITATION_CREATED", "ALREADY_INVITED", "INVITATION_REISSUED"]),
|
|
30
|
+
message: z.string().optional(),
|
|
31
|
+
}), z.object({
|
|
32
|
+
principalId: z.string().min(1),
|
|
33
|
+
membershipState: z.literal("active"),
|
|
34
|
+
role: adminUserRoleSchema,
|
|
35
|
+
code: z.enum(["MEMBERSHIP_CREATED", "ALREADY_ACTIVE"]),
|
|
36
|
+
message: z.string().optional(),
|
|
37
|
+
})]);
|
|
38
|
+
export const adminUserRemoveRequestSchema = z.object({
|
|
39
|
+
userId: z.string().min(1),
|
|
40
|
+
});
|
|
41
|
+
export const adminUserRemoveResponseSchema = z.object({
|
|
42
|
+
revoked: z.literal(true),
|
|
43
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type IdentityResponseOptions } from "./response.js";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
4
|
+
import type { MagicLinkAuthClient, MagicLinkRateLimitClient, MagicLinkDecision } from "./magicLinkPorts.js";
|
|
5
|
+
export interface AdminMagicLinkHandlerOptions extends IdentityResponseOptions {
|
|
6
|
+
requestSchema: z.ZodType<{
|
|
7
|
+
email: string;
|
|
8
|
+
}>;
|
|
9
|
+
responseSchema: z.ZodType<{
|
|
10
|
+
accepted: true;
|
|
11
|
+
}>;
|
|
12
|
+
readEnvironment: () => {
|
|
13
|
+
url: string;
|
|
14
|
+
anonKey: string;
|
|
15
|
+
serviceRoleKey: string;
|
|
16
|
+
} | null;
|
|
17
|
+
createRuntime: (env: {
|
|
18
|
+
url: string;
|
|
19
|
+
anonKey: string;
|
|
20
|
+
serviceRoleKey: string;
|
|
21
|
+
}) => {
|
|
22
|
+
authClient: MagicLinkAuthClient;
|
|
23
|
+
rateLimitClient: MagicLinkRateLimitClient;
|
|
24
|
+
platformPort: {
|
|
25
|
+
checkAdminMagicLinkEligibility(email: string): Promise<string>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
checkAttempt: (input: {
|
|
29
|
+
client: MagicLinkRateLimitClient;
|
|
30
|
+
ip: string;
|
|
31
|
+
email: string;
|
|
32
|
+
}) => Promise<MagicLinkDecision>;
|
|
33
|
+
extractClientIp: (headers: HttpRequest["headers"]) => string;
|
|
34
|
+
padResponseTime: (startedAt: number, floor: number) => Promise<void>;
|
|
35
|
+
redirectOrigins?: readonly string[];
|
|
36
|
+
resolveOrigin?: (req: HttpRequest) => string | null;
|
|
37
|
+
callbackPath: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function createAdminMagicLinkHandler(options: AdminMagicLinkHandlerOptions): (req: HttpRequest, res: HttpResponse) => Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createMagicLinkOriginResolver } from "./authVerifyUrl.js";
|
|
2
|
+
import { createIdentityResponder } from "./response.js";
|
|
3
|
+
const ADMIN_MAGIC_LINK_RESPONSE_FLOOR_MS = 400;
|
|
4
|
+
export function createAdminMagicLinkHandler(options) {
|
|
5
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder(options);
|
|
6
|
+
const readRequestOrigin = options.resolveOrigin ?? createMagicLinkOriginResolver(options.redirectOrigins ?? []);
|
|
7
|
+
const { requestSchema: adminMagicLinkRequestSchema, responseSchema: adminMagicLinkResponseSchema, readEnvironment: readSupabaseAdminServiceEnv, createRuntime: createAdminMagicLinkRuntime, checkAttempt: checkAndRecordAdminMagicLinkAttempt, extractClientIp, padResponseTime } = options;
|
|
8
|
+
return async (req, res) => {
|
|
9
|
+
if (req.method !== "POST") {
|
|
10
|
+
sendMethodNotAllowed(res, ["POST"]);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const request = adminMagicLinkRequestSchema.safeParse(req.body);
|
|
14
|
+
if (!request.success) {
|
|
15
|
+
sendBffError(res, "BAD_REQUEST", "Invalid admin magic-link request", {
|
|
16
|
+
details: request.error.flatten(),
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const startedAt = Date.now();
|
|
21
|
+
const origin = readRequestOrigin(req);
|
|
22
|
+
if (!origin) {
|
|
23
|
+
await padResponseTime(startedAt, ADMIN_MAGIC_LINK_RESPONSE_FLOOR_MS);
|
|
24
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Magic link origin is not configured", {
|
|
25
|
+
details: { reason: "magic_link_origin_not_configured" },
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const env = readSupabaseAdminServiceEnv();
|
|
30
|
+
if (!env) {
|
|
31
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin auth is not configured", {
|
|
32
|
+
details: { feature: "admin_auth", requiredEnv: "SUPABASE_SERVICE_ROLE_KEY" },
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const runtime = createAdminMagicLinkRuntime(env);
|
|
37
|
+
const decision = await checkAndRecordAdminMagicLinkAttempt({
|
|
38
|
+
client: runtime.rateLimitClient,
|
|
39
|
+
ip: extractClientIp(req.headers),
|
|
40
|
+
email: request.data.email,
|
|
41
|
+
});
|
|
42
|
+
if (!decision.allowed && decision.reason === "rpc_error") {
|
|
43
|
+
console.warn("admin_magic_link_rate_limit_unavailable", JSON.stringify({ reason: decision.reason }));
|
|
44
|
+
await padResponseTime(startedAt, ADMIN_MAGIC_LINK_RESPONSE_FLOOR_MS);
|
|
45
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Magic link service temporarily unavailable");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (decision.allowed) {
|
|
49
|
+
let eligibility;
|
|
50
|
+
try {
|
|
51
|
+
eligibility = await runtime.platformPort.checkAdminMagicLinkEligibility(request.data.email);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
console.warn("admin_magic_link_lookup_failed", JSON.stringify({ outcome: "lookup_error" }));
|
|
55
|
+
await padResponseTime(startedAt, ADMIN_MAGIC_LINK_RESPONSE_FLOOR_MS);
|
|
56
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Magic link service temporarily unavailable");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (eligibility === "eligible") {
|
|
60
|
+
const outcome = await sendMagicLink(runtime.authClient, request.data.email, origin);
|
|
61
|
+
console.info("admin_magic_link_outcome", JSON.stringify({ outcome: outcome.status, code: outcome.code ?? null }));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
console.warn("admin_magic_link_outcome", JSON.stringify({ outcome: eligibility }));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.warn("admin_magic_link_outcome", JSON.stringify({ outcome: "rate_limited", reason: decision.reason ?? "unknown" }));
|
|
69
|
+
}
|
|
70
|
+
await padResponseTime(startedAt, ADMIN_MAGIC_LINK_RESPONSE_FLOOR_MS);
|
|
71
|
+
sendBffSuccess(res, adminMagicLinkResponseSchema.parse({ accepted: true }));
|
|
72
|
+
};
|
|
73
|
+
async function sendMagicLink(client, email, origin) {
|
|
74
|
+
try {
|
|
75
|
+
const { error } = await client.auth.signInWithOtp({
|
|
76
|
+
email,
|
|
77
|
+
options: {
|
|
78
|
+
shouldCreateUser: false,
|
|
79
|
+
emailRedirectTo: `${origin}${options.callbackPath}`,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (error) {
|
|
83
|
+
console.warn("admin_magic_link_send_failed", JSON.stringify({ code: error.name ?? "AuthError" }));
|
|
84
|
+
return { status: "auth_error", code: error.name ?? "AuthError" };
|
|
85
|
+
}
|
|
86
|
+
return { status: "sent" };
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const code = error instanceof Error ? error.name : "unknown";
|
|
90
|
+
console.warn("admin_magic_link_send_threw", JSON.stringify({ error: code }));
|
|
91
|
+
return { status: "threw", code };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OperatorInvitationStore } from "./operatorInvitationPorts.js";
|
|
2
|
+
import { type IdentityResponseOptions } from "./response.js";
|
|
3
|
+
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
4
|
+
import type { PlatformUserAuthenticationResult } from "./platformAdminAuth.js";
|
|
5
|
+
export interface AdminPlatformMeDeps extends IdentityResponseOptions {
|
|
6
|
+
invitationStore?: Pick<OperatorInvitationStore, "acceptOperatorInvitation">;
|
|
7
|
+
platformPort: {
|
|
8
|
+
getAdminMe(userId: string): Promise<{
|
|
9
|
+
isAdmin: boolean;
|
|
10
|
+
role: "admin" | "distributor" | null;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
authenticateUser: (req: HttpRequest) => Promise<PlatformUserAuthenticationResult>;
|
|
14
|
+
}
|
|
15
|
+
export declare function createAdminPlatformMeHandler({ platformPort, invitationStore, authenticateUser, responseRequestId, }: AdminPlatformMeDeps): (req: HttpRequest, res: HttpResponse) => Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createIdentityResponder } from "./response.js";
|
|
2
|
+
import { adminPlatformMeRequestSchema, adminPlatformMeResponseSchema } from "./adminContracts.js";
|
|
3
|
+
export function createAdminPlatformMeHandler({ platformPort, invitationStore, authenticateUser, responseRequestId, }) {
|
|
4
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder({ responseRequestId });
|
|
5
|
+
return async function handler(req, res) {
|
|
6
|
+
if (req.method !== "GET") {
|
|
7
|
+
sendMethodNotAllowed(res, ["GET"]);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
let authentication;
|
|
11
|
+
try {
|
|
12
|
+
authentication = await authenticateUser(req);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Platform user authentication failed");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (authentication.ok === false) {
|
|
19
|
+
sendBffError(res, authentication.code, authentication.message);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const request = adminPlatformMeRequestSchema.safeParse(req.query);
|
|
23
|
+
if (!request.success) {
|
|
24
|
+
sendBffError(res, "BAD_REQUEST", "Invalid platform me request", {
|
|
25
|
+
details: request.error.flatten(),
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
if (invitationStore) {
|
|
31
|
+
const outcome = await invitationStore.acceptOperatorInvitation(authentication.userId);
|
|
32
|
+
if (outcome.code === "INVITATION_EXPIRED" || outcome.code === "INVITATION_REVOKED") {
|
|
33
|
+
sendBffError(res, "FORBIDDEN", outcome.code, { details: { identityCode: outcome.code } });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const result = await platformPort.getAdminMe(authentication.userId);
|
|
38
|
+
const response = adminPlatformMeResponseSchema.safeParse(result);
|
|
39
|
+
if (!response.success) {
|
|
40
|
+
sendBffError(res, "INVALID_RESPONSE", "Platform me returned invalid response");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
sendBffSuccess(res, response.data);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Platform me read failed");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type IdentityResponseOptions } from "./response.js";
|
|
2
|
+
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
3
|
+
import type { AdminAuthPort, AdminPrincipalRole } from "./ports.js";
|
|
4
|
+
export interface AdminAuthBinding {
|
|
5
|
+
readonly identity: string;
|
|
6
|
+
run<T>(accessToken: string | null, work: (auth: AdminAuthPort) => Promise<T>): Promise<T>;
|
|
7
|
+
}
|
|
8
|
+
export declare function createAdminBffPreflight(options: IdentityResponseOptions & {
|
|
9
|
+
allowedRoles: (route: string) => readonly AdminPrincipalRole[] | null;
|
|
10
|
+
resolveBinding: () => {
|
|
11
|
+
binding?: AdminAuthBinding;
|
|
12
|
+
error?: string;
|
|
13
|
+
};
|
|
14
|
+
machineWriteRoutes?: readonly string[];
|
|
15
|
+
managedEnvironmentMessage?: string;
|
|
16
|
+
}): (req: HttpRequest, res: HttpResponse, route: string) => Promise<boolean>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createIdentityResponder } from "./response.js";
|
|
2
|
+
import { readBearerToken } from "./adminAuthorization.js";
|
|
3
|
+
import { refusesMachineActorRequest, sendMachineActorWriteRefused } from "./machineActorWriteFence.js";
|
|
4
|
+
export function createAdminBffPreflight(options) {
|
|
5
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder(options);
|
|
6
|
+
return async (req, res, route) => {
|
|
7
|
+
const allowedRoles = options.allowedRoles(route);
|
|
8
|
+
if (!allowedRoles)
|
|
9
|
+
return true;
|
|
10
|
+
const resolved = options.resolveBinding();
|
|
11
|
+
if (!resolved.binding) {
|
|
12
|
+
if (resolved.error === "managed_admin_auth_env_required") {
|
|
13
|
+
sendBffError(res, "INTERNAL", options.managedEnvironmentMessage ?? "Identity environment is not configured");
|
|
14
|
+
}
|
|
15
|
+
else
|
|
16
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin authorization failed");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const accessToken = readBearerToken(req);
|
|
20
|
+
try {
|
|
21
|
+
const authz = await resolved.binding.run(accessToken, (auth) => auth.authorize(accessToken, { allowedRoles }));
|
|
22
|
+
if (authz.ok === false) {
|
|
23
|
+
sendBffError(res, authz.code, authz.message);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (resolved.binding.identity !== "node-postgres"
|
|
27
|
+
&& refusesMachineActorRequest(route, req.method, authz.isMachineActor, options.machineWriteRoutes)) {
|
|
28
|
+
sendMachineActorWriteRefused(res, options);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Admin authorization failed");
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|