@memberjunction/server 6.2.0-edge.5 → 6.2.0-edge.6
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/README.md +27 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +3 -3
- package/dist/agentSessions/SessionJanitor.js +3 -3
- package/dist/agentSessions/SessionManager.d.ts +15 -5
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +34 -12
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +2 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +51 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +10 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +37 -1
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/localDevAuth.d.ts +79 -0
- package/dist/auth/localDevAuth.d.ts.map +1 -0
- package/dist/auth/localDevAuth.js +439 -0
- package/dist/auth/localDevAuth.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +12 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.d.ts +1 -1
- package/dist/auth/sessionUserClone.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.js +3 -2
- package/dist/auth/sessionUserClone.js.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
- package/dist/broadcast/index.d.ts +6 -0
- package/dist/broadcast/index.d.ts.map +1 -0
- package/dist/broadcast/index.js +6 -0
- package/dist/broadcast/index.js.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
- package/dist/cache.d.ts +3 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +496 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -8
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +14 -0
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +635 -14
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +3366 -418
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +154 -79
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PubSubManager.d.ts +10 -0
- package/dist/generic/PubSubManager.d.ts.map +1 -1
- package/dist/generic/PubSubManager.js +44 -1
- package/dist/generic/PubSubManager.js.map +1 -1
- package/dist/generic/PushStatusResolver.d.ts +39 -6
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +93 -11
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +65 -25
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +201 -101
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +11 -3
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +79 -13
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
- package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
- package/dist/generic/actionOutputFieldSecurity.js +47 -0
- package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecurityTransport.d.ts +30 -0
- package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
- package/dist/generic/fieldSecurityTransport.js +30 -0
- package/dist/generic/fieldSecurityTransport.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +122 -66
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +50 -1
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +15 -6
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +88 -11
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +125 -22
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +19 -3
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +201 -19
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/host-identity.d.ts +1 -1
- package/dist/realtimeWidget/host-identity.js +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +20 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +17 -1
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +19 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +63 -12
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +1 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +40 -17
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
- package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +1 -0
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +5 -0
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +13 -4
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +45 -7
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +31 -3
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
- package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.js +5 -3
- package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +20 -6
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -6
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RSUResolver.d.ts.map +1 -1
- package/dist/resolvers/RSUResolver.js +12 -0
- package/dist/resolvers/RSUResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +16 -2
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +82 -28
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +209 -64
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +4 -4
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +3 -1
- package/dist/resolvers/RunTestResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +4 -0
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +1 -0
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +60 -15
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +2 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts +6 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +19 -5
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +8 -5
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.js +7 -7
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts +6 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +22 -5
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/serverRoleConfigUnits.d.ts +29 -0
- package/dist/serverRoleConfigUnits.d.ts.map +1 -0
- package/dist/serverRoleConfigUnits.js +33 -0
- package/dist/serverRoleConfigUnits.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +42 -15
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/types.d.ts +23 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +98 -98
- package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
- package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
- package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
- package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
- package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
- package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
- package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
- package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
- package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
- package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +37 -4
- package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
- package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
- package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +68 -13
- package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
- package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +17 -27
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +8 -2
- package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
- package/src/__tests__/RunTemplateResolver.test.ts +35 -2
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
- package/src/__tests__/SessionManager.test.ts +95 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
- package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
- package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
- package/src/__tests__/WidgetSessionService.test.ts +583 -4
- package/src/__tests__/apiRootBoot.test.ts +1 -1
- package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
- package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
- package/src/__tests__/fieldSecurityFixtures.ts +91 -0
- package/src/__tests__/globalCors.test.ts +3 -3
- package/src/__tests__/localDevAuth.test.ts +196 -0
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
- package/src/__tests__/realtimeRecordingHandler.test.ts +19 -2
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
- package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
- package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
- package/src/__tests__/resolverBase.fls.test.ts +146 -97
- package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
- package/src/__tests__/runParamsTimeZone.test.ts +179 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
- package/src/__tests__/runViewProperties.test.ts +114 -0
- package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
- package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
- package/src/__tests__/sessionUserClone.test.ts +5 -1
- package/src/__tests__/startupSweepOrder.test.ts +40 -0
- package/src/__tests__/unifiedAuth.test.ts +28 -0
- package/src/__tests__/widgetGuestElevation.test.ts +2 -0
- package/src/agentSessions/SessionJanitor.ts +3 -3
- package/src/agentSessions/SessionManager.ts +35 -12
- package/src/auth/AuthProviderCatalogRouter.ts +3 -0
- package/src/auth/index.ts +56 -12
- package/src/auth/initializeProviders.ts +43 -1
- package/src/auth/localDevAuth.ts +528 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
- package/src/auth/magicLink/types.ts +10 -0
- package/src/auth/sessionUserClone.ts +3 -2
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
- package/src/broadcast/index.ts +5 -0
- package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
- package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
- package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
- package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
- package/src/cache.ts +2 -1
- package/src/config.ts +69 -0
- package/src/context.ts +32 -8
- package/src/coreRoutes.ts +15 -0
- package/src/generated/graphql-schemas/__mj.ts +2239 -102
- package/src/generic/CacheInvalidationResolver.ts +135 -78
- package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
- package/src/generic/PubSubManager.ts +54 -1
- package/src/generic/PushStatusResolver.ts +108 -11
- package/src/generic/ResolverBase.ts +237 -107
- package/src/generic/RunViewResolver.ts +80 -15
- package/src/generic/actionOutputFieldSecurity.ts +75 -0
- package/src/generic/fieldSecurityTransport.ts +62 -0
- package/src/index.ts +127 -68
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/logging/variablesLoggingMiddleware.ts +59 -3
- package/src/realtimeSessions/README.md +7 -3
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +20 -6
- package/src/realtimeSessions/sessionAccess.ts +181 -25
- package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
- package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
- package/src/realtimeSessions/verificationWorkflow.ts +3 -1
- package/src/realtimeWidget/WidgetRouter.ts +22 -4
- package/src/realtimeWidget/WidgetSessionService.ts +252 -17
- package/src/realtimeWidget/host-identity.ts +1 -1
- package/src/realtimeWidget/widgetCore.ts +30 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
- package/src/resolvers/ActionResolver.ts +78 -15
- package/src/resolvers/AdhocQueryResolver.ts +1 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
- package/src/resolvers/AgentSessionResolver.ts +46 -18
- package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
- package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
- package/src/resolvers/FileCategoryResolver.ts +1 -0
- package/src/resolvers/FileResolver.ts +14 -4
- package/src/resolvers/IntegrationDiscoveryResolver.ts +49 -7
- package/src/resolvers/MergeRecordsResolver.ts +23 -2
- package/src/resolvers/PipelineProgressResolver.ts +12 -3
- package/src/resolvers/QueryResolver.ts +20 -6
- package/src/resolvers/QuerySystemUserResolver.ts +15 -6
- package/src/resolvers/RSUResolver.ts +12 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +104 -33
- package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
- package/src/resolvers/RunAIAgentResolver.ts +258 -82
- package/src/resolvers/RunTemplateResolver.ts +6 -4
- package/src/resolvers/RunTestResolver.ts +3 -1
- package/src/resolvers/SyncDataResolver.ts +4 -0
- package/src/resolvers/TestQuerySQLResolver.ts +1 -0
- package/src/resolvers/TransactionGroupResolver.ts +66 -15
- package/src/resolvers/UserViewResolver.ts +2 -1
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
- package/src/rest/EntityCRUDHandler.ts +22 -5
- package/src/rest/RESTEndpointHandler.ts +8 -5
- package/src/rest/RealtimeRecordingHandler.ts +13 -10
- package/src/rest/ViewOperationsHandler.ts +24 -6
- package/src/serverRoleConfigUnits.ts +48 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +52 -20
- package/src/types.ts +23 -1
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Local Development Mock/Test JWT Authentication (`local`).
|
|
3
|
+
*
|
|
4
|
+
* Provides a zero-external-IdP local development login flow backed by MJ's
|
|
5
|
+
* in-memory RS256 `MagicLinkKeyManager` and JWKS endpoint.
|
|
6
|
+
*
|
|
7
|
+
* HARD SECURITY INVARIANTS:
|
|
8
|
+
* 1. Fail loud in production: If `MJ_DEV_AUTH_ENABLED` (or `configInfo.devAuth?.enabled`)
|
|
9
|
+
* is enabled while `NODE_ENV === 'production'`, server startup throws a fatal error
|
|
10
|
+
* and refuses to boot.
|
|
11
|
+
* 2. Strict localhost-only guard: Every request to `/auth/dev-login` and `/auth/dev-users`
|
|
12
|
+
* verifies that:
|
|
13
|
+
* - `NODE_ENV !== 'production'` and local dev auth is enabled.
|
|
14
|
+
* - `X-Forwarded-For` (if present) contains only loopback addresses.
|
|
15
|
+
* - `Host` header is `localhost`, `127.0.0.1`, or `::1`.
|
|
16
|
+
* - `Origin` / `Referer` headers (if present) resolve to `localhost`, `127.0.0.1`, or `::1`.
|
|
17
|
+
* - Socket remote address is loopback (`127.0.0.1`, `::1`, `::ffff:127.0.0.1`) or local
|
|
18
|
+
* Docker bridge/host-gateway when no external `X-Forwarded-For` is present.
|
|
19
|
+
* Any request failing these checks is denied with `403 Forbidden` and logged.
|
|
20
|
+
*
|
|
21
|
+
* @module @memberjunction/server/auth/localDevAuth
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as fs from 'node:fs';
|
|
25
|
+
import { Router, json, type Request, type Response } from 'express';
|
|
26
|
+
import { rateLimit } from 'express-rate-limit';
|
|
27
|
+
import {
|
|
28
|
+
AuthProviderConfig,
|
|
29
|
+
DatabaseProviderBase,
|
|
30
|
+
IMetadataProvider,
|
|
31
|
+
LogError,
|
|
32
|
+
LogStatusEx,
|
|
33
|
+
Metadata,
|
|
34
|
+
UserInfo,
|
|
35
|
+
} from '@memberjunction/core';
|
|
36
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
37
|
+
import { AuthProviderFactory, LocalDevProvider } from '@memberjunction/auth-providers';
|
|
38
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
39
|
+
import { configInfo } from '../config.js';
|
|
40
|
+
import { ApiRoute } from '../apiRoot.js';
|
|
41
|
+
import { MagicLinkKeyManager } from './magicLink/MagicLinkKeys.js';
|
|
42
|
+
import { MAGIC_LINK_JWKS_PATH } from './magicLink/MagicLinkRouter.js';
|
|
43
|
+
import { AssertLocalDevAuthAllowed, IsLocalDevAuthEnabled } from './initializeProviders.js';
|
|
44
|
+
|
|
45
|
+
export { AssertLocalDevAuthAllowed, IsLocalDevAuthEnabled };
|
|
46
|
+
|
|
47
|
+
/** Claims embedded in a local development JWT. */
|
|
48
|
+
interface LocalDevJWTClaims extends Record<string, unknown> {
|
|
49
|
+
iss: string;
|
|
50
|
+
aud: string;
|
|
51
|
+
sub: string;
|
|
52
|
+
email: string;
|
|
53
|
+
given_name: string;
|
|
54
|
+
family_name: string;
|
|
55
|
+
name: string;
|
|
56
|
+
preferred_username: string;
|
|
57
|
+
iat: number;
|
|
58
|
+
exp: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Result of the localhost-only request guard. */
|
|
62
|
+
export interface LocalhostGuardResult {
|
|
63
|
+
Allowed: boolean;
|
|
64
|
+
Reason?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const LOCALHOST_HOSTNAMES = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
|
|
68
|
+
|
|
69
|
+
function TrimTrailingSlashes(value: string): string {
|
|
70
|
+
let end = value.length;
|
|
71
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) {
|
|
72
|
+
end--;
|
|
73
|
+
}
|
|
74
|
+
return value.slice(0, end);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Checks whether an IP address is a loopback address, or (when `allowDockerBridge` is true)
|
|
79
|
+
* a local Docker bridge / Docker Desktop host-gateway address.
|
|
80
|
+
*/
|
|
81
|
+
export function IsLoopbackAddress(rawIp: string | undefined, allowDockerBridge: boolean): boolean {
|
|
82
|
+
if (!rawIp) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const normalized = rawIp.trim().toLowerCase().replace(/^::ffff:/, '');
|
|
86
|
+
if (normalized === '127.0.0.1' || normalized === '::1' || normalized === 'localhost') {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (allowDockerBridge) {
|
|
93
|
+
// Docker Desktop macOS/Windows host-gateway IPs (192.168.65.0/24) and local
|
|
94
|
+
// Docker bridge gateway (.1) addresses when a host browser connects to a
|
|
95
|
+
// port-forwarded container bound to 127.0.0.1 on the host.
|
|
96
|
+
if (/^192\.168\.65\.\d{1,3}$/.test(normalized)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.1$/.test(normalized)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (normalized === '10.0.2.2') {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isRunningInsideContainer(env: NodeJS.ProcessEnv): boolean {
|
|
110
|
+
if (env.MJ_DEV_AUTH_ALLOW_DOCKER_BRIDGE === 'true') {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
if (env.MJ_DEV_AUTH_ALLOW_DOCKER_BRIDGE === 'false') {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Extracts the hostname (without port) from a raw HTTP `Host` header value. */
|
|
120
|
+
function extractHostnameFromHostHeader(hostHeader: string | undefined): string {
|
|
121
|
+
if (!hostHeader) {
|
|
122
|
+
return '';
|
|
123
|
+
}
|
|
124
|
+
const trimmed = hostHeader.trim().toLowerCase();
|
|
125
|
+
if (trimmed.startsWith('[')) {
|
|
126
|
+
const closeIdx = trimmed.indexOf(']');
|
|
127
|
+
return closeIdx >= 0 ? trimmed.slice(1, closeIdx) : trimmed;
|
|
128
|
+
}
|
|
129
|
+
const colonIdx = trimmed.indexOf(':');
|
|
130
|
+
return colonIdx >= 0 ? trimmed.slice(0, colonIdx) : trimmed;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Extracts the hostname from an `Origin` or `Referer` URL header. */
|
|
134
|
+
function extractHostnameFromUrlHeader(urlHeader: string): string | null {
|
|
135
|
+
try {
|
|
136
|
+
const parsed = new URL(urlHeader);
|
|
137
|
+
return parsed.hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Verifies that an incoming HTTP request to a local-dev auth endpoint originated
|
|
145
|
+
* strictly from localhost (`localhost`, `127.0.0.1`, or `::1`).
|
|
146
|
+
*/
|
|
147
|
+
export function VerifyLocalhostRequest(
|
|
148
|
+
req: Request,
|
|
149
|
+
env: NodeJS.ProcessEnv = process.env
|
|
150
|
+
): LocalhostGuardResult {
|
|
151
|
+
if (env.NODE_ENV?.trim().toLowerCase() === 'production') {
|
|
152
|
+
return {
|
|
153
|
+
Allowed: false,
|
|
154
|
+
Reason: 'Local dev auth is strictly forbidden when NODE_ENV=production.',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 1. Inspect X-Forwarded-For / Forwarded / X-Real-IP / CF-Connecting-IP if present — every hop must be pure loopback
|
|
159
|
+
const xffRaw = req.headers['x-forwarded-for'];
|
|
160
|
+
const xffValue = Array.isArray(xffRaw) ? xffRaw.join(',') : xffRaw;
|
|
161
|
+
if (xffValue && xffValue.trim().length > 0) {
|
|
162
|
+
const hops = xffValue.split(',').map((h) => h.trim()).filter(Boolean);
|
|
163
|
+
for (const hop of hops) {
|
|
164
|
+
if (!IsLoopbackAddress(hop, false)) {
|
|
165
|
+
return {
|
|
166
|
+
Allowed: false,
|
|
167
|
+
Reason: `Non-loopback X-Forwarded-For address '${hop}' rejected.`,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const proxyHeader of ['x-real-ip', 'cf-connecting-ip'] as const) {
|
|
174
|
+
const rawHeader = req.headers[proxyHeader];
|
|
175
|
+
const headerVal = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;
|
|
176
|
+
if (headerVal && headerVal.trim().length > 0 && !IsLoopbackAddress(headerVal, false)) {
|
|
177
|
+
return {
|
|
178
|
+
Allowed: false,
|
|
179
|
+
Reason: `Non-loopback ${proxyHeader} address '${headerVal}' rejected.`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const forwardedHeader = req.headers['forwarded'];
|
|
185
|
+
const forwardedVal = Array.isArray(forwardedHeader) ? forwardedHeader.join(',') : forwardedHeader;
|
|
186
|
+
if (forwardedVal && forwardedVal.trim().length > 0) {
|
|
187
|
+
const forMatches = forwardedVal.matchAll(/for=\s*"?\[?([^;,"\]]+)\]?"?/gi);
|
|
188
|
+
for (const match of forMatches) {
|
|
189
|
+
const candidateIp = match[1]?.split(':')[0]?.trim();
|
|
190
|
+
if (candidateIp && !IsLoopbackAddress(candidateIp, false)) {
|
|
191
|
+
return {
|
|
192
|
+
Allowed: false,
|
|
193
|
+
Reason: `Non-loopback Forwarded address '${candidateIp}' rejected.`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 1b. Reject browser cross-site requests via Sec-Fetch-Site
|
|
200
|
+
const secFetchSite = typeof req.headers['sec-fetch-site'] === 'string' ? req.headers['sec-fetch-site'].trim().toLowerCase() : undefined;
|
|
201
|
+
if (secFetchSite === 'cross-site') {
|
|
202
|
+
return {
|
|
203
|
+
Allowed: false,
|
|
204
|
+
Reason: `Cross-site request (Sec-Fetch-Site: ${secFetchSite}) rejected.`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 2. Inspect Host header — must be localhost, 127.0.0.1, or ::1
|
|
209
|
+
const hostHeader = typeof req.headers.host === 'string' ? req.headers.host : req.hostname;
|
|
210
|
+
const hostName = extractHostnameFromHostHeader(hostHeader);
|
|
211
|
+
if (!hostName || !LOCALHOST_HOSTNAMES.has(hostName)) {
|
|
212
|
+
return {
|
|
213
|
+
Allowed: false,
|
|
214
|
+
Reason: `Non-localhost Host header '${hostHeader ?? 'missing'}' rejected.`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 3. Inspect Origin header if present
|
|
219
|
+
const originHeader = typeof req.headers.origin === 'string' ? req.headers.origin : undefined;
|
|
220
|
+
if (originHeader) {
|
|
221
|
+
const originHost = extractHostnameFromUrlHeader(originHeader);
|
|
222
|
+
if (!originHost || !LOCALHOST_HOSTNAMES.has(originHost)) {
|
|
223
|
+
return {
|
|
224
|
+
Allowed: false,
|
|
225
|
+
Reason: `Non-localhost Origin header '${originHeader}' rejected.`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 4. Inspect Referer header if present
|
|
231
|
+
const refererHeader = typeof req.headers.referer === 'string' ? req.headers.referer : undefined;
|
|
232
|
+
if (refererHeader) {
|
|
233
|
+
const refererHost = extractHostnameFromUrlHeader(refererHeader);
|
|
234
|
+
if (!refererHost || !LOCALHOST_HOSTNAMES.has(refererHost)) {
|
|
235
|
+
return {
|
|
236
|
+
Allowed: false,
|
|
237
|
+
Reason: `Non-localhost Referer header '${refererHeader}' rejected.`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 5. Inspect socket remote address (allow Docker local bridge ONLY when running in a container and no proxy header is present)
|
|
243
|
+
const hasProxyHeader = Boolean(
|
|
244
|
+
(xffValue && xffValue.trim().length > 0) ||
|
|
245
|
+
(forwardedVal && forwardedVal.trim().length > 0) ||
|
|
246
|
+
req.headers['x-real-ip'] ||
|
|
247
|
+
req.headers['cf-connecting-ip']
|
|
248
|
+
);
|
|
249
|
+
const allowDockerBridge = !hasProxyHeader && isRunningInsideContainer(env);
|
|
250
|
+
const remoteAddress = req.socket?.remoteAddress || req.ip;
|
|
251
|
+
if (!IsLoopbackAddress(remoteAddress, allowDockerBridge)) {
|
|
252
|
+
return {
|
|
253
|
+
Allowed: false,
|
|
254
|
+
Reason: `Non-loopback socket remote address '${remoteAddress ?? 'unknown'}' rejected.`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return { Allowed: true };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Registers the `local` auth provider in `AuthProviderFactory` and `configInfo.authProviders`
|
|
263
|
+
* using the server's configured public URL and the shared `MagicLinkKeyManager` JWKS endpoint.
|
|
264
|
+
*/
|
|
265
|
+
export function RegisterLocalDevAuthProvider(publicUrl?: string): void {
|
|
266
|
+
AssertLocalDevAuthAllowed();
|
|
267
|
+
MagicLinkKeyManager.Instance.Initialize(configInfo.magicLink?.rsaPrivateKey);
|
|
268
|
+
|
|
269
|
+
const base = publicUrl ? TrimTrailingSlashes(publicUrl) : undefined;
|
|
270
|
+
const issuer = base ? `${base}/local-dev` : ApiRoute('/local-dev', { absolute: true });
|
|
271
|
+
const jwksUri = base ? `${base}${MAGIC_LINK_JWKS_PATH}` : ApiRoute(MAGIC_LINK_JWKS_PATH, { absolute: true });
|
|
272
|
+
const providerConfig: AuthProviderConfig = {
|
|
273
|
+
name: 'local',
|
|
274
|
+
type: 'local',
|
|
275
|
+
issuer,
|
|
276
|
+
audience: LocalDevProvider.DEFAULT_AUDIENCE,
|
|
277
|
+
jwksUri,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const factory = AuthProviderFactory.Instance;
|
|
282
|
+
const existingProvider = factory.getByName('local');
|
|
283
|
+
if (existingProvider && existingProvider.issuer === issuer && existingProvider.jwksUri === jwksUri) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
configInfo.authProviders = configInfo.authProviders ?? [];
|
|
288
|
+
const existingIdx = configInfo.authProviders.findIndex((p) => p.type === 'local' || p.name === 'local');
|
|
289
|
+
if (existingIdx >= 0) {
|
|
290
|
+
configInfo.authProviders[existingIdx] = providerConfig;
|
|
291
|
+
} else {
|
|
292
|
+
configInfo.authProviders.push(providerConfig);
|
|
293
|
+
}
|
|
294
|
+
factory.register(AuthProviderFactory.createProvider(providerConfig));
|
|
295
|
+
LogStatusEx({
|
|
296
|
+
message: `[LocalDevAuth] Registered local dev auth provider (issuer: ${providerConfig.issuer}, jwks: ${providerConfig.jwksUri})`,
|
|
297
|
+
verboseOnly: true,
|
|
298
|
+
});
|
|
299
|
+
} catch (e) {
|
|
300
|
+
LogError(`[LocalDevAuth] Failed to register local dev auth provider: ${e instanceof Error ? e.message : String(e)}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Resolves the active local MJ user to sign in as for local development.
|
|
306
|
+
*/
|
|
307
|
+
export async function ResolveLocalDevUser(
|
|
308
|
+
requestedEmail?: string,
|
|
309
|
+
provider?: IMetadataProvider
|
|
310
|
+
): Promise<UserInfo | null> {
|
|
311
|
+
const effectiveProvider = provider ?? Metadata.Provider;
|
|
312
|
+
if ((!UserCache.Instance.Users || UserCache.Instance.Users.length === 0) && effectiveProvider) {
|
|
313
|
+
try {
|
|
314
|
+
await UserCache.Instance.Refresh(effectiveProvider as DatabaseProviderBase);
|
|
315
|
+
} catch (err) {
|
|
316
|
+
LogError(`[LocalDevAuth] Failed to refresh UserCache: ${err instanceof Error ? err.message : String(err)}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const allUsers = UserCache.Instance.Users ?? [];
|
|
321
|
+
const activeUsers = allUsers.filter(
|
|
322
|
+
(u) =>
|
|
323
|
+
u.IsActive &&
|
|
324
|
+
!UUIDsEqual(u.ID, UserCache.Instance.SYSTEM_USER_ID) &&
|
|
325
|
+
u.Name?.trim().toLowerCase() !== 'system'
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const targetEmail =
|
|
329
|
+
requestedEmail?.trim() ||
|
|
330
|
+
process.env.MJ_DEV_AUTH_DEFAULT_EMAIL?.trim() ||
|
|
331
|
+
configInfo?.devAuth?.defaultEmail?.trim();
|
|
332
|
+
if (targetEmail) {
|
|
333
|
+
const matched = activeUsers.find((u) => u.Email?.trim().toLowerCase() === targetEmail.toLowerCase());
|
|
334
|
+
if (matched) {
|
|
335
|
+
return matched;
|
|
336
|
+
}
|
|
337
|
+
// If an explicit email was requested on the call and doesn't exist, return null
|
|
338
|
+
if (requestedEmail?.trim()) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 1. Default MJ installer admin email
|
|
344
|
+
const defaultAdmin = activeUsers.find((u) => u.Email?.trim().toLowerCase() === 'admin@memberjunction.org');
|
|
345
|
+
if (defaultAdmin) {
|
|
346
|
+
return defaultAdmin;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 2. First active user holding the Developer role
|
|
350
|
+
const developerUser = activeUsers.find(
|
|
351
|
+
(u) =>
|
|
352
|
+
u.Email?.trim().toLowerCase() !== 'not.set@nowhere.com' &&
|
|
353
|
+
u.UserRoles?.some((r) => r.Role?.trim().toLowerCase() === 'developer')
|
|
354
|
+
);
|
|
355
|
+
if (developerUser) {
|
|
356
|
+
return developerUser;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 3. First active Owner user with a real email
|
|
360
|
+
const ownerUser = activeUsers.find(
|
|
361
|
+
(u) =>
|
|
362
|
+
u.Email?.trim().toLowerCase() !== 'not.set@nowhere.com' &&
|
|
363
|
+
u.Type?.trim().toLowerCase() === 'owner'
|
|
364
|
+
);
|
|
365
|
+
if (ownerUser) {
|
|
366
|
+
return ownerUser;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return activeUsers[0] ?? null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Mints a signed RS256 local-dev JWT for the given `UserInfo`.
|
|
374
|
+
*/
|
|
375
|
+
export function MintLocalDevJwt(
|
|
376
|
+
user: UserInfo,
|
|
377
|
+
publicUrl?: string,
|
|
378
|
+
ttlSeconds = 8 * 3600
|
|
379
|
+
): { token: string; expiresAt: string; claims: LocalDevJWTClaims } {
|
|
380
|
+
AssertLocalDevAuthAllowed();
|
|
381
|
+
MagicLinkKeyManager.Instance.Initialize(configInfo.magicLink?.rsaPrivateKey);
|
|
382
|
+
|
|
383
|
+
const base = publicUrl ? TrimTrailingSlashes(publicUrl) : undefined;
|
|
384
|
+
const issuer = base ? `${base}/local-dev` : ApiRoute('/local-dev', { absolute: true });
|
|
385
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
386
|
+
const expSeconds = nowSeconds + ttlSeconds;
|
|
387
|
+
const firstName = user.FirstName?.trim() || 'Admin';
|
|
388
|
+
const lastName = user.LastName?.trim() || 'User';
|
|
389
|
+
const fullName =
|
|
390
|
+
[user.FirstName?.trim(), user.LastName?.trim()].filter(Boolean).join(' ') ||
|
|
391
|
+
user.Name?.trim() ||
|
|
392
|
+
user.Email;
|
|
393
|
+
|
|
394
|
+
const claims: LocalDevJWTClaims = {
|
|
395
|
+
iss: issuer,
|
|
396
|
+
aud: LocalDevProvider.DEFAULT_AUDIENCE,
|
|
397
|
+
sub: `local-dev|${user.ID}`,
|
|
398
|
+
email: user.Email,
|
|
399
|
+
given_name: firstName,
|
|
400
|
+
family_name: lastName,
|
|
401
|
+
name: fullName,
|
|
402
|
+
preferred_username: user.Email,
|
|
403
|
+
iat: nowSeconds,
|
|
404
|
+
exp: expSeconds,
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const token = MagicLinkKeyManager.Instance.Sign(claims);
|
|
408
|
+
return {
|
|
409
|
+
token,
|
|
410
|
+
expiresAt: new Date(expSeconds * 1000).toISOString(),
|
|
411
|
+
claims,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Attaches `/dev-login` and `/dev-users` routes onto the `/auth` router.
|
|
417
|
+
*
|
|
418
|
+
* When local dev auth is disabled, these routes respond with `404`.
|
|
419
|
+
* When enabled, every request is gated by {@link VerifyLocalhostRequest}.
|
|
420
|
+
*/
|
|
421
|
+
export function AttachLocalDevAuthRoutes(router: Router): void {
|
|
422
|
+
const devAuthLimiter = rateLimit({
|
|
423
|
+
windowMs: 60_000,
|
|
424
|
+
limit: 60,
|
|
425
|
+
standardHeaders: 'draft-7',
|
|
426
|
+
legacyHeaders: false,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
router.post('/dev-login', devAuthLimiter, json(), async (req: Request, res: Response) => {
|
|
430
|
+
if (!IsLocalDevAuthEnabled()) {
|
|
431
|
+
res.status(404).json({
|
|
432
|
+
success: false,
|
|
433
|
+
error: 'Local dev auth is not enabled. Set MJ_DEV_AUTH_ENABLED=true in local development.',
|
|
434
|
+
});
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const guard = VerifyLocalhostRequest(req);
|
|
439
|
+
if (!guard.Allowed) {
|
|
440
|
+
LogError(`[SECURITY] Blocked /auth/dev-login request: ${guard.Reason}`);
|
|
441
|
+
res.status(403).json({
|
|
442
|
+
success: false,
|
|
443
|
+
error: 'Forbidden: /auth/dev-login is restricted to localhost only.',
|
|
444
|
+
});
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
RegisterLocalDevAuthProvider();
|
|
450
|
+
|
|
451
|
+
const body = (req.body ?? {}) as { email?: string };
|
|
452
|
+
const requestedEmail = typeof body.email === 'string' ? body.email.trim() : undefined;
|
|
453
|
+
const user = await ResolveLocalDevUser(requestedEmail);
|
|
454
|
+
|
|
455
|
+
if (!user) {
|
|
456
|
+
const availableEmails = (UserCache.Instance.Users ?? [])
|
|
457
|
+
.filter((u) => u.IsActive && !UUIDsEqual(u.ID, UserCache.Instance.SYSTEM_USER_ID))
|
|
458
|
+
.map((u) => u.Email);
|
|
459
|
+
res.status(404).json({
|
|
460
|
+
success: false,
|
|
461
|
+
error: requestedEmail
|
|
462
|
+
? `No active MJ user found matching email '${requestedEmail}'.`
|
|
463
|
+
: 'No active non-System MJ user found in UserCache.',
|
|
464
|
+
availableUsers: availableEmails,
|
|
465
|
+
});
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const { token, expiresAt, claims } = MintLocalDevJwt(user);
|
|
470
|
+
res.set('Cache-Control', 'no-store');
|
|
471
|
+
res.status(200).json({
|
|
472
|
+
success: true,
|
|
473
|
+
token,
|
|
474
|
+
expiresAt,
|
|
475
|
+
user: {
|
|
476
|
+
id: user.ID,
|
|
477
|
+
email: user.Email,
|
|
478
|
+
name: claims.name,
|
|
479
|
+
firstName: claims.given_name,
|
|
480
|
+
lastName: claims.family_name,
|
|
481
|
+
roles: (user.UserRoles ?? []).map((r) => r.Role),
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
} catch (err) {
|
|
485
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
486
|
+
LogError(`[LocalDevAuth] /auth/dev-login error: ${message}`);
|
|
487
|
+
res.status(500).json({
|
|
488
|
+
success: false,
|
|
489
|
+
error: message,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
router.get('/dev-users', devAuthLimiter, async (req: Request, res: Response) => {
|
|
495
|
+
if (!IsLocalDevAuthEnabled()) {
|
|
496
|
+
res.status(404).json({
|
|
497
|
+
success: false,
|
|
498
|
+
error: 'Local dev auth is not enabled.',
|
|
499
|
+
});
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const guard = VerifyLocalhostRequest(req);
|
|
504
|
+
if (!guard.Allowed) {
|
|
505
|
+
LogError(`[SECURITY] Blocked /auth/dev-users request: ${guard.Reason}`);
|
|
506
|
+
res.status(403).json({
|
|
507
|
+
success: false,
|
|
508
|
+
error: 'Forbidden: /auth/dev-users is restricted to localhost only.',
|
|
509
|
+
});
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const users = (UserCache.Instance.Users ?? [])
|
|
514
|
+
.filter((u) => u.IsActive && !UUIDsEqual(u.ID, UserCache.Instance.SYSTEM_USER_ID) && u.Name?.trim().toLowerCase() !== 'system')
|
|
515
|
+
.map((u) => ({
|
|
516
|
+
id: u.ID,
|
|
517
|
+
email: u.Email,
|
|
518
|
+
name: u.Name,
|
|
519
|
+
firstName: u.FirstName,
|
|
520
|
+
lastName: u.LastName,
|
|
521
|
+
type: u.Type,
|
|
522
|
+
roles: (u.UserRoles ?? []).map((r) => r.Role),
|
|
523
|
+
}));
|
|
524
|
+
|
|
525
|
+
res.set('Cache-Control', 'no-store');
|
|
526
|
+
res.status(200).json({ success: true, users });
|
|
527
|
+
});
|
|
528
|
+
}
|
|
@@ -89,10 +89,10 @@ export class MagicLinkKeyManager extends BaseSingleton<MagicLinkKeyManager> {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Signs a set of magic-link claims and returns the compact JWT.
|
|
92
|
+
* Signs a set of magic-link (or local-dev) claims and returns the compact JWT.
|
|
93
93
|
* `iat`/`exp` are expected to already be set on the claims.
|
|
94
94
|
*/
|
|
95
|
-
public Sign(claims: MagicLinkJWTClaims): string {
|
|
95
|
+
public Sign(claims: MagicLinkJWTClaims | Record<string, unknown>): string {
|
|
96
96
|
this.ensureInitialized();
|
|
97
97
|
return jwt.sign(claims, this.privateKeyPem, { algorithm: 'RS256', keyid: this._kid });
|
|
98
98
|
}
|
|
@@ -58,6 +58,16 @@ export interface MagicLinkJWTClaims {
|
|
|
58
58
|
mj_anon?: boolean; // case-violation-ok-legacy-back-compat: snake_case mirrors an external payload — the remote spelling is the contract, not MJ convention
|
|
59
59
|
/** Opaque per-session id — correlates one anonymous session's activity across audit rows without a real user. */
|
|
60
60
|
mj_sid?: string; // case-violation-ok-legacy-back-compat: snake_case mirrors an external payload — the remote spelling is the contract, not MJ convention
|
|
61
|
+
/**
|
|
62
|
+
* When `mj_sid` was first minted (epoch seconds). Set on widget guest tokens and copied unchanged by every
|
|
63
|
+
* refresh, so the server can refuse to renew a session past its hard end (`MAX_GUEST_SESSION_SECONDS`).
|
|
64
|
+
*/
|
|
65
|
+
mj_sid_iat?: number; // case-violation-ok-legacy-back-compat: snake_case mirrors an external payload — the remote spelling is the contract, not MJ convention
|
|
66
|
+
/**
|
|
67
|
+
* Confirmation (RFC 7800 / RFC 9449): the RFC 7638 thumbprint of the key the widget registered at mint.
|
|
68
|
+
* Refreshing a widget guest token requires a proof signed by that key, so a copied token cannot be renewed.
|
|
69
|
+
*/
|
|
70
|
+
cnf?: { jkt: string }; // case-violation-ok-legacy-back-compat: the claim name is fixed by RFC 7800, part of the wire shape
|
|
61
71
|
/**
|
|
62
72
|
* Public web widget instance id (additive — set only for widget guest sessions
|
|
63
73
|
* minted by WidgetSessionService). Binds the synthesized guest principal to one
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* concurrent session of the same user THE INSTANT it is written; this is
|
|
8
8
|
* same-moment cross-request aliasing, not a cache-staleness problem. The
|
|
9
9
|
* magic-link path has always cloned for exactly this reason (see
|
|
10
|
-
*
|
|
10
|
+
* BuildMagicLinkSessionUser in context.ts); this helper makes that rule a
|
|
11
11
|
* single shared primitive so every per-session stamp goes through the same
|
|
12
12
|
* clone.
|
|
13
13
|
*
|
|
@@ -30,7 +30,7 @@ import { Metadata, UserInfo } from '@memberjunction/core';
|
|
|
30
30
|
* state on the clone; never stamp the input itself.
|
|
31
31
|
*/
|
|
32
32
|
export function CloneUserForSessionContext(user: UserInfo): UserInfo {
|
|
33
|
-
const md = Metadata.Provider; // global-provider-ok: server-side session construction under the server's single default provider, same as
|
|
33
|
+
const md = Metadata.Provider; // global-provider-ok: server-side session construction under the server's single default provider, same as BuildMagicLinkSessionUser
|
|
34
34
|
const clone = new UserInfo(md, {
|
|
35
35
|
...user,
|
|
36
36
|
_UserRoles: undefined,
|
|
@@ -44,6 +44,7 @@ export function CloneUserForSessionContext(user: UserInfo): UserInfo {
|
|
|
44
44
|
clone.ReturningVisitorContext = user.ReturningVisitorContext;
|
|
45
45
|
clone.WidgetGuestContext = user.WidgetGuestContext;
|
|
46
46
|
clone.IsMagicLinkAnonymous = user.IsMagicLinkAnonymous;
|
|
47
|
+
clone.AnonymousVisitorID = user.AnonymousVisitorID;
|
|
47
48
|
clone.APIKeyActingContext = user.APIKeyActingContext;
|
|
48
49
|
clone.APIKeyRowFilters = user.APIKeyRowFilters;
|
|
49
50
|
return clone;
|