@memberjunction/server 6.2.0-edge.4 → 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 +47 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +91 -30
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +311 -81
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +17 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +44 -10
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/apiRoot.d.ts +32 -0
- package/dist/apiRoot.d.ts.map +1 -0
- package/dist/apiRoot.js +67 -0
- package/dist/apiRoot.js.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +27 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/LoginScreenEngine.d.ts +22 -0
- package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
- package/dist/auth/LoginScreenEngine.js +57 -0
- package/dist/auth/LoginScreenEngine.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +52 -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/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
- package/dist/auth/magicLink/MagicLinkRouter.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 +632 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +104 -4
- 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 +29 -0
- package/dist/coreRoutes.d.ts.map +1 -0
- package/dist/coreRoutes.js +156 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +5125 -434
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +175 -60
- 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 +94 -12
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +168 -69
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +464 -405
- 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 +91 -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/globalCors.d.ts +33 -0
- package/dist/globalCors.d.ts.map +1 -0
- package/dist/globalCors.js +58 -0
- package/dist/globalCors.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -222
- 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/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
- 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/index.d.ts +2 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -1
- package/dist/realtimeSessions/index.js +2 -0
- package/dist/realtimeSessions/index.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +127 -20
- 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/ArtifactFileResolver.d.ts +5 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.js +9 -7
- package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +24 -3
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +57 -8
- package/dist/resolvers/DatasetResolver.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/EntityRecordNameResolver.d.ts +2 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +3 -2
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +46 -3
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
- 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 +87 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +292 -105
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +117 -38
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +6 -3
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
- 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 +2 -0
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +33 -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 +18 -10
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
- 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 +241 -67
- 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/SearchKnowledgeStreamResolver.d.ts +12 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
- package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
- package/dist/resolvers/SqlLoggingConfigResolver.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/TaskGraphFrameResolver.d.ts +2 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +66 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +150 -27
- 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/resolvers/agent-run-data-guard.d.ts +31 -0
- package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
- package/dist/resolvers/agent-run-data-guard.js +72 -0
- package/dist/resolvers/agent-run-data-guard.js.map +1 -0
- 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 +49 -0
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
- package/dist/rest/RealtimeRecordingHandler.js +383 -0
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts +9 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +43 -17
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +6 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +6 -0
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +12 -10
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +14 -17
- package/dist/serverExtensionReservedRoots.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/MJStorageBlobStore.d.ts +8 -1
- package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
- package/dist/services/MJStorageBlobStore.js +25 -19
- package/dist/services/MJStorageBlobStore.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +2 -2
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +5 -3
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -5
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +157 -9
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +59 -2
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts.map +1 -1
- package/dist/sharedCache.js +3 -0
- package/dist/sharedCache.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 +99 -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__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
- package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
- package/src/__tests__/LoginScreenEngine.test.ts +86 -0
- package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -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__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
- package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +243 -7
- package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- 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 +69 -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 +20 -28
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
- 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__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +365 -10
- package/src/__tests__/SessionManager.test.ts +122 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
- package/src/__tests__/WidgetSessionService.test.ts +583 -4
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
- package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/fieldSecurityFixtures.ts +91 -0
- package/src/__tests__/globalCors.test.ts +217 -0
- package/src/__tests__/localDevAuth.test.ts +196 -0
- package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
- package/src/__tests__/multiTenancy.security.test.ts +51 -0
- package/src/__tests__/multiTenancy.test.ts +1 -1
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
- package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
- 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__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
- package/src/__tests__/sessionUserClone.test.ts +5 -1
- package/src/__tests__/sharedCache.test.ts +5 -3
- 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 +369 -93
- package/src/agentSessions/SessionManager.ts +45 -10
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +29 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/index.ts +57 -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/MagicLinkRouter.ts +3 -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 +115 -5
- package/src/context.ts +32 -8
- package/src/coreRoutes.ts +181 -0
- package/src/generated/graphql-schemas/__mj.ts +3684 -284
- package/src/generic/CacheInvalidationResolver.ts +172 -60
- package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
- package/src/generic/PubSubManager.ts +54 -1
- package/src/generic/PushStatusResolver.ts +109 -12
- package/src/generic/ResolverBase.ts +540 -431
- package/src/generic/RunViewResolver.ts +97 -15
- package/src/generic/actionOutputFieldSecurity.ts +75 -0
- package/src/generic/fieldSecurityTransport.ts +62 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +239 -238
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/logging/variablesLoggingMiddleware.ts +59 -3
- package/src/multiTenancy/index.ts +24 -2
- package/src/realtimeSessions/README.md +7 -3
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
- package/src/realtimeSessions/index.ts +2 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
- package/src/realtimeSessions/sessionAccess.ts +183 -23
- 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/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
- package/src/resolvers/FileCategoryResolver.ts +1 -0
- package/src/resolvers/FileResolver.ts +334 -109
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
- package/src/resolvers/MergeRecordsResolver.ts +23 -2
- package/src/resolvers/PipelineProgressResolver.ts +12 -3
- package/src/resolvers/QueryResolver.ts +31 -6
- package/src/resolvers/QuerySystemUserResolver.ts +15 -6
- package/src/resolvers/RSUResolver.ts +12 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
- package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
- package/src/resolvers/RunAIAgentResolver.ts +296 -85
- package/src/resolvers/RunTemplateResolver.ts +6 -4
- package/src/resolvers/RunTestResolver.ts +3 -1
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +4 -0
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +69 -13
- package/src/resolvers/TransactionGroupResolver.ts +185 -30
- package/src/resolvers/UserViewResolver.ts +2 -1
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -0
- package/src/rest/EntityCRUDHandler.ts +22 -5
- package/src/rest/RESTEndpointHandler.ts +8 -5
- package/src/rest/RealtimeRecordingHandler.ts +467 -0
- package/src/rest/ViewOperationsHandler.ts +52 -18
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- package/src/serverRoleConfigUnits.ts +48 -0
- package/src/services/MJStorageBlobStore.ts +31 -30
- package/src/services/StartTaskGraphDispatcher.ts +2 -2
- package/src/services/TaskGraphActionRunner.ts +25 -5
- package/src/services/TaskGraphAgentRunner.ts +188 -11
- package/src/services/TaskGraphContinuationDeliverer.ts +738 -16
- package/src/sharedCache.ts +3 -0
- package/src/types.ts +23 -1
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import jwt from 'jsonwebtoken';
|
|
3
|
+
import { createPublicKey } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
const runViewMock = vi.fn(async () => ({ Success: true, Results: [] as unknown[] }));
|
|
6
|
+
const mockUsers: Array<{ ID: string; Email: string; IsActive: boolean }> = [];
|
|
7
|
+
vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
8
|
+
const actual = await importOriginal<typeof import('@memberjunction/core')>();
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
RunView: class {
|
|
12
|
+
RunView = runViewMock;
|
|
13
|
+
},
|
|
14
|
+
Metadata: {
|
|
15
|
+
get Provider() {
|
|
16
|
+
return {
|
|
17
|
+
Roles: [
|
|
18
|
+
{ ID: '00000000-0000-4000-8000-000000000099', Name: 'Widget Guest' },
|
|
19
|
+
{ ID: '00000000-0000-4000-8000-000000000088', Name: 'UI' },
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
});
|
|
2
26
|
|
|
3
27
|
// WidgetSessionService is the imperative shell for public widget sessions — importing it pulls in
|
|
4
28
|
// config.ts (which eagerly validates DB env vars at module load) plus the full magic-link /
|
|
@@ -13,8 +37,15 @@ vi.mock('@memberjunction/generic-database-provider', () => ({
|
|
|
13
37
|
// The subject reads exactly these two through `ResolveConfiguredPrincipal`. It no longer
|
|
14
38
|
// calls `UserByName`/`GetSystemUser`, and stubbing members nobody calls hides the day a
|
|
15
39
|
// test does reach `resolveLookupUser` and trips the resolver's missing-system-id guard.
|
|
16
|
-
Instance: {
|
|
17
|
-
|
|
40
|
+
Instance: {
|
|
41
|
+
get Users() {
|
|
42
|
+
return mockUsers;
|
|
43
|
+
},
|
|
44
|
+
SYSTEM_USER_ID: 'ECAFCCEC-6A37-EF11-86D4-000D3A4E707E',
|
|
45
|
+
},
|
|
46
|
+
get Users() {
|
|
47
|
+
return mockUsers;
|
|
48
|
+
},
|
|
18
49
|
},
|
|
19
50
|
}));
|
|
20
51
|
vi.mock('../auth/magicLink/MagicLinkService.js', () => ({
|
|
@@ -33,8 +64,21 @@ vi.mock('../realtimeWidget/visitorIdentity.js', () => ({
|
|
|
33
64
|
get forgetVisitor() { return this.ForgetVisitor; },
|
|
34
65
|
}));
|
|
35
66
|
|
|
36
|
-
import { WidgetSessionService } from '../realtimeWidget/WidgetSessionService.js';
|
|
67
|
+
import { WidgetSessionService, REFRESH_GRACE_SECONDS, MAX_GUEST_SESSION_SECONDS, MIN_REFRESHED_TOKEN_SECONDS } from '../realtimeWidget/WidgetSessionService.js';
|
|
68
|
+
import { CreateWidgetHandler } from '../realtimeWidget/WidgetRouter.js';
|
|
69
|
+
import express from 'express';
|
|
70
|
+
import type { Server } from 'node:http';
|
|
71
|
+
import type { AddressInfo } from 'node:net';
|
|
72
|
+
import { MagicLinkKeyManager } from '../auth/magicLink/MagicLinkKeys.js';
|
|
37
73
|
import type { WidgetConfig } from '../config.js';
|
|
74
|
+
import { CreateProofOfPossessionKey, CreateProofOfPossessionJwt, ComputeJwkThumbprint, type ProofOfPossessionKey } from '@memberjunction/global';
|
|
75
|
+
|
|
76
|
+
const REFRESH_URL = 'https://mj.example.com/widget/session/refresh';
|
|
77
|
+
|
|
78
|
+
/** A DPoP-style proof that the holder of `key` is refreshing `token`. */
|
|
79
|
+
function proofFor(key: ProofOfPossessionKey, token: string, url = REFRESH_URL): Promise<string> {
|
|
80
|
+
return CreateProofOfPossessionJwt(key, { Method: 'POST', Url: url, AccessToken: token });
|
|
81
|
+
}
|
|
38
82
|
|
|
39
83
|
/**
|
|
40
84
|
* Minimal `WidgetConfig` matching the zod schema's post-parse (defaulted) shape —
|
|
@@ -101,3 +145,538 @@ describe('WidgetSessionService — ResolvePerInstanceRateLimit', () => {
|
|
|
101
145
|
expect(service.RateLimitCacheSize).toBe(1);
|
|
102
146
|
});
|
|
103
147
|
});
|
|
148
|
+
|
|
149
|
+
describe('WidgetSessionService — RefreshGuestSession preserves mj_sid (#4931)', () => {
|
|
150
|
+
const WIDGET_ID = '11111111-1111-4111-8111-111111111111';
|
|
151
|
+
const APP_ID = '33333333-3333-4333-8333-333333333333';
|
|
152
|
+
const ROLE_ID = '00000000-0000-4000-8000-000000000099';
|
|
153
|
+
const AGENT_ID = '44444444-4444-4444-8444-444444444444';
|
|
154
|
+
|
|
155
|
+
function makeWidgetRow() {
|
|
156
|
+
return {
|
|
157
|
+
ID: WIDGET_ID,
|
|
158
|
+
PublicKey: 'pk_live_test',
|
|
159
|
+
Status: 'Active',
|
|
160
|
+
AllowedOrigins: '["https://acme.com"]',
|
|
161
|
+
Modality: 'Both',
|
|
162
|
+
GuestRoleID: ROLE_ID,
|
|
163
|
+
ApplicationID: APP_ID,
|
|
164
|
+
PinnedAgentID: AGENT_ID,
|
|
165
|
+
AuthStrategy: 'Anonymous',
|
|
166
|
+
SessionTTLMinutes: 15,
|
|
167
|
+
RateLimitPerMinute: 30,
|
|
168
|
+
RememberReturningVisitors: false,
|
|
169
|
+
EnabledChannels: null,
|
|
170
|
+
VoiceMaxSessionMinutes: 10,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let key: ProofOfPossessionKey;
|
|
175
|
+
let thumbprint: string;
|
|
176
|
+
|
|
177
|
+
beforeEach(async () => {
|
|
178
|
+
key = await CreateProofOfPossessionKey();
|
|
179
|
+
thumbprint = await ComputeJwkThumbprint(key.PublicJwk);
|
|
180
|
+
MagicLinkKeyManager.Instance.Initialize();
|
|
181
|
+
mockUsers.length = 0;
|
|
182
|
+
mockUsers.push({ ID: 'ECAFCCEC-6A37-EF11-86D4-000D3A4E707E', Email: 'system@system.org', IsActive: true });
|
|
183
|
+
runViewMock.mockReset();
|
|
184
|
+
runViewMock.mockImplementation(async (params: { ExtraFilter?: string }) => {
|
|
185
|
+
if (params.ExtraFilter?.includes('pk_live_test')) {
|
|
186
|
+
return { Success: true, Results: [makeWidgetRow()] };
|
|
187
|
+
}
|
|
188
|
+
return { Success: true, Results: [] };
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('exports REFRESH_GRACE_SECONDS = 300', () => {
|
|
193
|
+
expect(REFRESH_GRACE_SECONDS).toBe(300);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('preserves sessionId and mj_sid across RefreshGuestSession when presented with the current valid Bearer token', async () => {
|
|
197
|
+
const service = new WidgetSessionService('https://mj.example.com', makeConfig());
|
|
198
|
+
const minted = await service.MintGuestSession({
|
|
199
|
+
widgetKey: 'pk_live_test',
|
|
200
|
+
origin: 'https://acme.com',
|
|
201
|
+
ProofKey: key.PublicJwk,
|
|
202
|
+
});
|
|
203
|
+
expect(minted.success).toBe(true);
|
|
204
|
+
expect(minted.sessionId).toBeDefined();
|
|
205
|
+
expect(minted.token).toBeDefined();
|
|
206
|
+
|
|
207
|
+
const refreshed = await service.RefreshGuestSession({
|
|
208
|
+
widgetKey: 'pk_live_test',
|
|
209
|
+
origin: 'https://acme.com',
|
|
210
|
+
BearerToken: minted.token,
|
|
211
|
+
Proof: await proofFor(key, minted.token!),
|
|
212
|
+
});
|
|
213
|
+
expect(refreshed.success).toBe(true);
|
|
214
|
+
expect(refreshed.sessionId).toBe(minted.sessionId);
|
|
215
|
+
|
|
216
|
+
const publicKey = createPublicKey({ key: MagicLinkKeyManager.Instance.GetJWKS().keys[0] as object, format: 'jwk' });
|
|
217
|
+
const decoded = jwt.verify(refreshed.token!, publicKey, { algorithms: ['RS256'], audience: 'mj-magic-link' }) as jwt.JwtPayload;
|
|
218
|
+
expect(decoded.mj_sid).toBe(minted.sessionId);
|
|
219
|
+
expect(decoded.sub).toBe(`magic-link|${minted.sessionId}`);
|
|
220
|
+
expect(decoded.mj_scopes?.[0]?.resourceId).toBe(minted.sessionId);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('accepts a token that expired within REFRESH_GRACE_SECONDS (e.g. 60s ago) and preserves sessionId', async () => {
|
|
224
|
+
const service = new WidgetSessionService('https://mj.example.com', makeConfig());
|
|
225
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
226
|
+
const recentlyExpiredToken = MagicLinkKeyManager.Instance.Sign({
|
|
227
|
+
iss: 'https://mj.example.com',
|
|
228
|
+
aud: 'mj-magic-link',
|
|
229
|
+
sub: 'magic-link|sid-recent-exp',
|
|
230
|
+
email: 'anonymous@magic-link.local',
|
|
231
|
+
iat: nowSeconds - 960,
|
|
232
|
+
exp: nowSeconds - 60,
|
|
233
|
+
mj_magic_link: true,
|
|
234
|
+
mj_anon: true,
|
|
235
|
+
mj_sid: 'sid-recent-exp',
|
|
236
|
+
mj_sid_iat: nowSeconds - 960,
|
|
237
|
+
cnf: { jkt: thumbprint },
|
|
238
|
+
mj_widget_id: WIDGET_ID,
|
|
239
|
+
mj_app_id: APP_ID,
|
|
240
|
+
mj_role: 'Widget Guest',
|
|
241
|
+
mj_scopes: [{ appId: APP_ID, role: 'Widget Guest', resourceType: 'Custom', resourceId: 'sid-recent-exp' }],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const refreshed = await service.RefreshGuestSession({
|
|
245
|
+
widgetKey: 'pk_live_test',
|
|
246
|
+
origin: 'https://acme.com',
|
|
247
|
+
BearerToken: recentlyExpiredToken,
|
|
248
|
+
Proof: await proofFor(key, recentlyExpiredToken),
|
|
249
|
+
});
|
|
250
|
+
expect(refreshed.success).toBe(true);
|
|
251
|
+
expect(refreshed.sessionId).toBe('sid-recent-exp');
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('WidgetSessionService — RefreshGuestSession re-applies every mint check (#4931)', () => {
|
|
256
|
+
const WIDGET_ID = '11111111-1111-4111-8111-111111111111';
|
|
257
|
+
const APP_ID = '33333333-3333-4333-8333-333333333333';
|
|
258
|
+
const ROLE_ID = '00000000-0000-4000-8000-000000000099';
|
|
259
|
+
const ISSUER = 'https://mj.example.com';
|
|
260
|
+
const ORIGIN = 'https://acme.com';
|
|
261
|
+
const BROWSER_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15';
|
|
262
|
+
let widgetOverrides: Record<string, unknown> = {};
|
|
263
|
+
let key: ProofOfPossessionKey;
|
|
264
|
+
let thumbprint: string;
|
|
265
|
+
|
|
266
|
+
function makeWidgetRow() {
|
|
267
|
+
return {
|
|
268
|
+
ID: WIDGET_ID,
|
|
269
|
+
PublicKey: 'pk_live_test',
|
|
270
|
+
Status: 'Active',
|
|
271
|
+
AllowedOrigins: `["${ORIGIN}"]`,
|
|
272
|
+
Modality: 'Both',
|
|
273
|
+
GuestRoleID: ROLE_ID,
|
|
274
|
+
ApplicationID: APP_ID,
|
|
275
|
+
PinnedAgentID: '44444444-4444-4444-8444-444444444444',
|
|
276
|
+
AuthStrategy: 'Anonymous',
|
|
277
|
+
SessionTTLMinutes: 15,
|
|
278
|
+
RateLimitPerMinute: 30,
|
|
279
|
+
RememberReturningVisitors: false,
|
|
280
|
+
EnabledChannels: null,
|
|
281
|
+
VoiceMaxSessionMinutes: 10,
|
|
282
|
+
...widgetOverrides,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** A live widget guest token for WIDGET_ID; each claim can be overridden (or removed with `undefined`). */
|
|
287
|
+
function signGuestToken(overrides: Record<string, unknown> = {}): string {
|
|
288
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
289
|
+
const claims: Record<string, unknown> = {
|
|
290
|
+
iss: ISSUER,
|
|
291
|
+
aud: 'mj-magic-link',
|
|
292
|
+
sub: 'magic-link|sid-live',
|
|
293
|
+
email: 'anonymous@magic-link.local',
|
|
294
|
+
iat: nowSeconds,
|
|
295
|
+
exp: nowSeconds + 900,
|
|
296
|
+
mj_magic_link: true,
|
|
297
|
+
mj_anon: true,
|
|
298
|
+
mj_sid: 'sid-live',
|
|
299
|
+
mj_sid_iat: nowSeconds,
|
|
300
|
+
cnf: { jkt: thumbprint },
|
|
301
|
+
mj_widget_id: WIDGET_ID,
|
|
302
|
+
mj_app_id: APP_ID,
|
|
303
|
+
mj_role: 'Widget Guest',
|
|
304
|
+
...overrides,
|
|
305
|
+
};
|
|
306
|
+
for (const key of Object.keys(claims)) {
|
|
307
|
+
if (claims[key] === undefined) {
|
|
308
|
+
delete claims[key];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return MagicLinkKeyManager.Instance.Sign(claims);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function refresh(service: WidgetSessionService, bearerToken: string, origin = ORIGIN, proof?: string) {
|
|
315
|
+
return service.RefreshGuestSession({
|
|
316
|
+
widgetKey: 'pk_live_test',
|
|
317
|
+
origin,
|
|
318
|
+
BearerToken: bearerToken,
|
|
319
|
+
Proof: proof ?? (await proofFor(key, bearerToken)),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
beforeEach(async () => {
|
|
324
|
+
key = await CreateProofOfPossessionKey();
|
|
325
|
+
thumbprint = await ComputeJwkThumbprint(key.PublicJwk);
|
|
326
|
+
widgetOverrides = {};
|
|
327
|
+
MagicLinkKeyManager.Instance.Initialize();
|
|
328
|
+
mockUsers.length = 0;
|
|
329
|
+
mockUsers.push({ ID: 'ECAFCCEC-6A37-EF11-86D4-000D3A4E707E', Email: 'system@system.org', IsActive: true });
|
|
330
|
+
runViewMock.mockReset();
|
|
331
|
+
runViewMock.mockImplementation(async (params: { ExtraFilter?: string }) =>
|
|
332
|
+
params.ExtraFilter?.includes('pk_live_test') ? { Success: true, Results: [makeWidgetRow()] } : { Success: true, Results: [] },
|
|
333
|
+
);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('accepts a valid token passed as BearerToken and keeps its mj_sid', async () => {
|
|
337
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
338
|
+
const refreshed = await refresh(service, signGuestToken());
|
|
339
|
+
expect(refreshed).toMatchObject({ success: true, sessionId: 'sid-live' });
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('refuses a valid token from an origin the widget does not allow', async () => {
|
|
343
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
344
|
+
const refreshed = await refresh(service, signGuestToken(), 'https://evil.example');
|
|
345
|
+
expect(refreshed.success).toBe(false);
|
|
346
|
+
expect(refreshed.errorCode).toBe('origin_not_allowed');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('refuses a HostIdentity widget refresh that carries no host assertion', async () => {
|
|
350
|
+
widgetOverrides = { AuthStrategy: 'HostIdentity', HostPublicKey: null };
|
|
351
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
352
|
+
const refreshed = await refresh(service, signGuestToken());
|
|
353
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'host_assertion_invalid' });
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('refuses when the widget guest role cannot be resolved', async () => {
|
|
357
|
+
widgetOverrides = { GuestRoleID: '99999999-9999-4999-8999-999999999999' };
|
|
358
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
359
|
+
const refreshed = await refresh(service, signGuestToken());
|
|
360
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'server_error' });
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('refuses a token without the mj_magic_link claim', async () => {
|
|
364
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
365
|
+
const refreshed = await refresh(service, signGuestToken({ mj_magic_link: undefined }));
|
|
366
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('refuses a token signed with the right key for another audience', async () => {
|
|
370
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
371
|
+
const refreshed = await refresh(service, signGuestToken({ aud: 'some-other-audience' }));
|
|
372
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('refuses a token signed with the right key by another issuer', async () => {
|
|
376
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
377
|
+
const refreshed = await refresh(service, signGuestToken({ iss: 'https://other-deployment.example' }));
|
|
378
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('guest role isolation (MJ#6043): every anonymous guest is one shared user, so only a scope-keyed role may be a guest role', () => {
|
|
382
|
+
const UI_ROLE_ID = '00000000-0000-4000-8000-000000000088';
|
|
383
|
+
|
|
384
|
+
it('refuses to mint when the widget guest role is not scope-isolated and the deployment has not allowed it', async () => {
|
|
385
|
+
widgetOverrides = { GuestRoleID: UI_ROLE_ID };
|
|
386
|
+
const service = new WidgetSessionService('https://mj.example.com', makeConfig());
|
|
387
|
+
const minted = await service.MintGuestSession({ widgetKey: 'pk_live_test', origin: 'https://acme.com', ProofKey: key.PublicJwk });
|
|
388
|
+
expect(minted).toMatchObject({ success: false, errorCode: 'guest_role_not_isolated' });
|
|
389
|
+
expect(minted.token).toBeUndefined();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('mints when the deployment explicitly lists that role as grantable to anonymous visitors', async () => {
|
|
393
|
+
widgetOverrides = { GuestRoleID: UI_ROLE_ID };
|
|
394
|
+
const service = new WidgetSessionService('https://mj.example.com', makeConfig(), { GrantableRoleNames: ['ui'] });
|
|
395
|
+
const minted = await service.MintGuestSession({ widgetKey: 'pk_live_test', origin: 'https://acme.com', ProofKey: key.PublicJwk });
|
|
396
|
+
expect(minted.success).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe('each refusal is for exactly one defect in an otherwise valid token and proof', () => {
|
|
401
|
+
it('refuses a refresh whose widget guest role is not scope-isolated (MJ#6043), so a token minted before the policy changed cannot be renewed', async () => {
|
|
402
|
+
widgetOverrides = { GuestRoleID: '00000000-0000-4000-8000-000000000088' /* UI */ };
|
|
403
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
404
|
+
const refreshed = await refresh(service, signGuestToken());
|
|
405
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'guest_role_not_isolated' });
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Every case below is the positive control ('accepts a valid token passed as BearerToken') with one thing
|
|
409
|
+
// changed, and carries a valid proof made by the key the token is bound to. A token missing some other piece
|
|
410
|
+
// would be refused before the check under test ran, and the test would pass with that check deleted.
|
|
411
|
+
const OTHER_WIDGET_ID = '22222222-2222-4222-8222-222222222222';
|
|
412
|
+
|
|
413
|
+
/** `token` with its payload rewritten and its original signature kept. */
|
|
414
|
+
function withForgedPayload(token: string, changes: Record<string, unknown>): string {
|
|
415
|
+
const [header, payload, signature] = token.split('.');
|
|
416
|
+
const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as Record<string, unknown>;
|
|
417
|
+
const forged = Buffer.from(JSON.stringify({ ...claims, ...changes }), 'utf8').toString('base64url');
|
|
418
|
+
return `${header}.${forged}.${signature}`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
it('refuses a missing token, even with a valid proof', async () => {
|
|
422
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
423
|
+
const refreshed = await service.RefreshGuestSession({
|
|
424
|
+
widgetKey: 'pk_live_test',
|
|
425
|
+
origin: ORIGIN,
|
|
426
|
+
Proof: await proofFor(key, signGuestToken()),
|
|
427
|
+
});
|
|
428
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('refuses a token whose payload was changed after signing', async () => {
|
|
432
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
433
|
+
const refreshed = await refresh(service, withForgedPayload(signGuestToken(), { mj_sid: 'sid-forged' }));
|
|
434
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('refuses a token that expired one second past REFRESH_GRACE_SECONDS', async () => {
|
|
438
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
439
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
440
|
+
const token = signGuestToken({ iat: nowSeconds - 1201, exp: nowSeconds - (REFRESH_GRACE_SECONDS + 1), mj_sid_iat: nowSeconds - 1201 });
|
|
441
|
+
expect(await refresh(service, token)).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('refuses a token without exp', async () => {
|
|
445
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
446
|
+
expect(await refresh(service, signGuestToken({ exp: undefined }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('refuses a token minted for another widget', async () => {
|
|
450
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
451
|
+
expect(await refresh(service, signGuestToken({ mj_widget_id: OTHER_WIDGET_ID }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('refuses a non-anonymous magic-link token (mj_anon false or absent)', async () => {
|
|
455
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
456
|
+
expect(await refresh(service, signGuestToken({ mj_anon: false }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
457
|
+
expect(await refresh(service, signGuestToken({ mj_anon: undefined }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('refuses a token whose mj_sid is blank', async () => {
|
|
461
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
462
|
+
expect(await refresh(service, signGuestToken({ mj_sid: ' ' }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('refuses a proof signed two minutes ago, and accepts one signed 30 seconds ago (REFRESH_PROOF_MAX_SKEW_SECONDS)', async () => {
|
|
466
|
+
// Fixed ages, not ones derived from REFRESH_PROOF_MAX_SKEW_SECONDS: a test that moved with the constant could
|
|
467
|
+
// not catch it widening.
|
|
468
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
469
|
+
const token = signGuestToken();
|
|
470
|
+
const proofSignedSecondsAgo = (age: number) =>
|
|
471
|
+
CreateProofOfPossessionJwt(key, { Method: 'POST', Url: REFRESH_URL, AccessToken: token, NowSeconds: Math.floor(Date.now() / 1000) - age });
|
|
472
|
+
expect(await refresh(service, token, ORIGIN, await proofSignedSecondsAgo(120))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
473
|
+
expect(await refresh(service, token, ORIGIN, await proofSignedSecondsAgo(30))).toMatchObject({ success: true, sessionId: 'sid-live' });
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
describe('hard end of a guest session (MAX_GUEST_SESSION_SECONDS)', () => {
|
|
478
|
+
const START = Date.parse('2026-10-23T00:00:00Z');
|
|
479
|
+
|
|
480
|
+
afterEach(() => {
|
|
481
|
+
vi.useRealTimers();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
function decode(token: string): jwt.JwtPayload {
|
|
485
|
+
return jwt.decode(token) as jwt.JwtPayload;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
it('is 12 hours', () => {
|
|
489
|
+
expect(MAX_GUEST_SESSION_SECONDS).toBe(12 * 3600);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('stamps the first mint time as mj_sid_iat and keeps it unchanged on refresh', async () => {
|
|
493
|
+
vi.useFakeTimers();
|
|
494
|
+
vi.setSystemTime(START);
|
|
495
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
496
|
+
const minted = await service.MintGuestSession({ widgetKey: 'pk_live_test', origin: ORIGIN, ProofKey: key.PublicJwk });
|
|
497
|
+
expect(decode(minted.token!).mj_sid_iat).toBe(START / 1000);
|
|
498
|
+
|
|
499
|
+
vi.setSystemTime(START + 14 * 60_000);
|
|
500
|
+
const refreshed = await refresh(service, minted.token!);
|
|
501
|
+
expect(refreshed.success).toBe(true);
|
|
502
|
+
expect(decode(refreshed.token!).mj_sid_iat).toBe(START / 1000);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('never issues a token that outlives the hard end', async () => {
|
|
506
|
+
vi.useFakeTimers();
|
|
507
|
+
vi.setSystemTime(START + (MAX_GUEST_SESSION_SECONDS - 300) * 1000);
|
|
508
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
509
|
+
const refreshed = await refresh(service, signGuestToken({ mj_sid_iat: START / 1000 }));
|
|
510
|
+
expect(refreshed.success).toBe(true);
|
|
511
|
+
expect(decode(refreshed.token!).exp).toBe(START / 1000 + MAX_GUEST_SESSION_SECONDS);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('refuses a refresh whose token would expire inside the client refresh lead (no storm of instant refreshes before the end)', async () => {
|
|
515
|
+
vi.useFakeTimers();
|
|
516
|
+
// The widget refreshes 60 s before exp; a token issued with less life than MIN_REFRESHED_TOKEN_SECONDS would
|
|
517
|
+
// be due for refresh at once, every time, until the rate limiter answered 429 instead of the 403 that restarts.
|
|
518
|
+
vi.setSystemTime(START + (MAX_GUEST_SESSION_SECONDS - 60) * 1000);
|
|
519
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
520
|
+
expect(await refresh(service, signGuestToken({ mj_sid_iat: START / 1000 }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
521
|
+
expect(MIN_REFRESHED_TOKEN_SECONDS).toBeGreaterThan(60);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('refuses a refresh exactly at the hard end (never signs a token with exp <= iat)', async () => {
|
|
525
|
+
vi.useFakeTimers();
|
|
526
|
+
vi.setSystemTime(START + MAX_GUEST_SESSION_SECONDS * 1000);
|
|
527
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
528
|
+
expect(await refresh(service, signGuestToken({ mj_sid_iat: START / 1000 }))).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('refuses a refresh past the hard end', async () => {
|
|
532
|
+
vi.useFakeTimers();
|
|
533
|
+
vi.setSystemTime(START + (MAX_GUEST_SESSION_SECONDS + 1) * 1000);
|
|
534
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
535
|
+
const refreshed = await refresh(service, signGuestToken({ mj_sid_iat: START / 1000 }));
|
|
536
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('refuses a token without mj_sid_iat (minted before the hard end existed)', async () => {
|
|
540
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
541
|
+
const refreshed = await refresh(service, signGuestToken({ mj_sid_iat: undefined }));
|
|
542
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('stops a token captured once from being renewed past the hard end, however often it is refreshed', async () => {
|
|
546
|
+
vi.useFakeTimers();
|
|
547
|
+
vi.setSystemTime(START);
|
|
548
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
549
|
+
let token = (await service.MintGuestSession({ widgetKey: 'pk_live_test', origin: ORIGIN, ProofKey: key.PublicJwk })).token!;
|
|
550
|
+
let accepted = 0;
|
|
551
|
+
let refusedAtMinute: number | null = null;
|
|
552
|
+
for (let minute = 14; minute <= 7 * 24 * 60 && refusedAtMinute === null; minute += 14) {
|
|
553
|
+
vi.setSystemTime(START + minute * 60_000);
|
|
554
|
+
const r = await refresh(service, token);
|
|
555
|
+
if (r.success) {
|
|
556
|
+
token = r.token!;
|
|
557
|
+
accepted++;
|
|
558
|
+
} else {
|
|
559
|
+
refusedAtMinute = minute;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
expect(refusedAtMinute).not.toBeNull();
|
|
563
|
+
expect(refusedAtMinute!).toBeLessThanOrEqual(MAX_GUEST_SESSION_SECONDS / 60 + REFRESH_GRACE_SECONDS / 60 + 14);
|
|
564
|
+
expect(accepted).toBeLessThanOrEqual(Math.ceil(MAX_GUEST_SESSION_SECONDS / 60 / 14));
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
describe('refresh is bound to the key the widget registered at mint', () => {
|
|
569
|
+
it('records the key thumbprint as cnf.jkt at mint and keeps it on refresh', async () => {
|
|
570
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
571
|
+
const minted = await service.MintGuestSession({ widgetKey: 'pk_live_test', origin: ORIGIN, ProofKey: key.PublicJwk });
|
|
572
|
+
expect((jwt.decode(minted.token!) as jwt.JwtPayload).cnf).toEqual({ jkt: thumbprint });
|
|
573
|
+
const refreshed = await refresh(service, minted.token!);
|
|
574
|
+
expect(refreshed.success).toBe(true);
|
|
575
|
+
expect((jwt.decode(refreshed.token!) as jwt.JwtPayload).cnf).toEqual({ jkt: thumbprint });
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('refuses a mint whose key is not a public P-256 JWK', async () => {
|
|
579
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
580
|
+
const minted = await service.MintGuestSession({
|
|
581
|
+
widgetKey: 'pk_live_test',
|
|
582
|
+
origin: ORIGIN,
|
|
583
|
+
ProofKey: { ...key.PublicJwk, d: 'private-part' } as unknown as typeof key.PublicJwk,
|
|
584
|
+
});
|
|
585
|
+
expect(minted).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('refuses a copied token presented without a proof', async () => {
|
|
589
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
590
|
+
const token = signGuestToken();
|
|
591
|
+
const refreshed = await service.RefreshGuestSession({ widgetKey: 'pk_live_test', origin: ORIGIN, BearerToken: token });
|
|
592
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it('refuses a proof signed by another key (the copier\'s own)', async () => {
|
|
596
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
597
|
+
const token = signGuestToken();
|
|
598
|
+
const attackerKey = await CreateProofOfPossessionKey();
|
|
599
|
+
const refreshed = await refresh(service, token, ORIGIN, await proofFor(attackerKey, token));
|
|
600
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('refuses a proof made for another token, or for another endpoint', async () => {
|
|
604
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
605
|
+
const token = signGuestToken();
|
|
606
|
+
expect(await refresh(service, token, ORIGIN, await proofFor(key, signGuestToken({ mj_sid: 'sid-other' })))).toMatchObject({ success: false });
|
|
607
|
+
expect(await refresh(service, token, ORIGIN, await proofFor(key, token, 'https://mj.example.com/widget/session'))).toMatchObject({ success: false });
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('refuses a token minted without a key (no cnf), even with a valid proof', async () => {
|
|
611
|
+
const service = new WidgetSessionService(ISSUER, makeConfig());
|
|
612
|
+
const refreshed = await refresh(service, signGuestToken({ cnf: undefined }));
|
|
613
|
+
expect(refreshed).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
describe('through the real POST /session/refresh route', () => {
|
|
618
|
+
let server: Server | undefined;
|
|
619
|
+
|
|
620
|
+
async function startRouter(): Promise<string> {
|
|
621
|
+
const app = express();
|
|
622
|
+
app.use('/widget', CreateWidgetHandler(ISSUER, makeConfig()).publicRouter);
|
|
623
|
+
await new Promise<void>((resolve) => {
|
|
624
|
+
server = app.listen(0, '127.0.0.1', resolve);
|
|
625
|
+
});
|
|
626
|
+
return `http://127.0.0.1:${(server!.address() as AddressInfo).port}/widget/session/refresh`;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function post(url: string, body: Record<string, unknown>, authorization?: string, dpop?: string) {
|
|
630
|
+
const headers: Record<string, string> = { 'content-type': 'application/json', origin: ORIGIN, 'user-agent': BROWSER_UA };
|
|
631
|
+
if (authorization) {
|
|
632
|
+
headers.authorization = authorization;
|
|
633
|
+
}
|
|
634
|
+
if (dpop) {
|
|
635
|
+
headers.dpop = dpop;
|
|
636
|
+
}
|
|
637
|
+
return fetch(url, { method: 'POST', headers, body: JSON.stringify(body) });
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
afterEach(async () => {
|
|
641
|
+
await new Promise<void>((resolve) => (server ? server.close(() => resolve()) : resolve()));
|
|
642
|
+
server = undefined;
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it('reads the token from the Authorization header and keeps its mj_sid', async () => {
|
|
646
|
+
const url = await startRouter();
|
|
647
|
+
const token = signGuestToken();
|
|
648
|
+
const res = await post(url, { widgetKey: 'pk_live_test' }, `Bearer ${token}`, await proofFor(key, token, url));
|
|
649
|
+
expect(res.status).toBe(200);
|
|
650
|
+
expect(await res.json()).toMatchObject({ success: true, sessionId: 'sid-live' });
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it('reads the proof key from the mint body', async () => {
|
|
654
|
+
const url = await startRouter();
|
|
655
|
+
const res = await post(url.replace('/session/refresh', '/session'), { widgetKey: 'pk_live_test', proofKey: key.PublicJwk });
|
|
656
|
+
expect(res.status).toBe(200);
|
|
657
|
+
const body = (await res.json()) as { token: string };
|
|
658
|
+
expect((jwt.decode(body.token) as jwt.JwtPayload).cnf).toEqual({ jkt: thumbprint });
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('returns 403 for a bare token without the Bearer scheme, even with a valid proof', async () => {
|
|
662
|
+
const url = await startRouter();
|
|
663
|
+
const token = signGuestToken();
|
|
664
|
+
const res = await post(url, { widgetKey: 'pk_live_test' }, token, await proofFor(key, token, url));
|
|
665
|
+
expect(res.status).toBe(403);
|
|
666
|
+
expect(await res.json()).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it('returns 403 without a DPoP header', async () => {
|
|
670
|
+
const url = await startRouter();
|
|
671
|
+
const res = await post(url, { widgetKey: 'pk_live_test' }, `Bearer ${signGuestToken()}`);
|
|
672
|
+
expect(res.status).toBe(403);
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
it('returns 403 without an Authorization header, even when the body carries a token', async () => {
|
|
676
|
+
const url = await startRouter();
|
|
677
|
+
const res = await post(url, { widgetKey: 'pk_live_test', BearerToken: signGuestToken(), bearerToken: signGuestToken() });
|
|
678
|
+
expect(res.status).toBe(403);
|
|
679
|
+
expect(await res.json()).toMatchObject({ success: false, errorCode: 'not_found' });
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
});
|