@opengeni/api-router 0.30.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +3 -0
- package/dist/admission-trace.d.ts +8 -0
- package/dist/api-websocket.d.ts +18 -0
- package/dist/app.d.ts +50 -2
- package/dist/app.js +12 -1
- package/dist/artifact-catalog-cursor.d.ts +27 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
- package/dist/auth/managed-auth.d.ts +102 -3
- package/dist/auth/managed-email.d.ts +29 -0
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/organization-user-setup.d.ts +80 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/chunk-6TZ5LG3O.js +123948 -0
- package/dist/chunk-6TZ5LG3O.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-subscription-account-conflicts.d.ts +1 -0
- package/dist/claude-subscription-account-usage.d.ts +39 -0
- package/dist/claude-subscription-oauth.d.ts +25 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +44 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +32 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-ownership.d.ts +54 -0
- package/dist/controller-data-plane.d.ts +32 -0
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +10 -18
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/github-access.d.ts +31 -2
- package/dist/http/acting-person.d.ts +24 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +27 -1
- package/dist/http/cors.d.ts +17 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/interaction-control-error.d.ts +13 -0
- package/dist/http/keyed-admission.d.ts +12 -0
- package/dist/http/local-browser-boundary.d.ts +72 -0
- package/dist/http/metrics-listener.d.ts +22 -0
- package/dist/http/path-identifier.d.ts +17 -0
- package/dist/http/registered-route-label.d.ts +16 -0
- package/dist/http/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +52 -0
- package/dist/http/sse.d.ts +17 -6
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +1091 -162
- package/dist/index.js.map +1 -1
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +29 -3
- package/dist/integrations/connect-authority.d.ts +1 -0
- package/dist/integrations/connect-callback-return.d.ts +6 -0
- package/dist/integrations/fiken.d.ts +16 -2
- package/dist/integrations/github-app-connect.d.ts +47 -0
- package/dist/integrations/github-installation-proof.d.ts +3 -0
- package/dist/integrations/github-lens-connect.d.ts +116 -0
- package/dist/integrations/github-skill-source.d.ts +1 -5
- package/dist/integrations/google-drive.d.ts +24 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +2691 -0
- package/dist/integrations/oauth-client.d.ts +178 -18
- package/dist/integrations/oauth-profiles.d.ts +222 -0
- package/dist/integrations/oauth-return-path.d.ts +1 -0
- package/dist/integrations/personal-github-repositories.d.ts +84 -0
- package/dist/integrations/personal-github.d.ts +42 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +19 -1
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +367 -12
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-install.d.ts +20 -0
- package/dist/integrations/slack-interactions.d.ts +107 -9
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/social-oauth.d.ts +7 -0
- package/dist/interaction-agent-access.d.ts +15 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/interaction-metrics.d.ts +2 -0
- package/dist/mcp/action-catalog-types.d.ts +9 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/contract-input.d.ts +11 -0
- package/dist/mcp/documents.d.ts +7 -5
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-input.d.ts +1189 -0
- package/dist/mcp/scheduled-task-view.d.ts +22 -4
- package/dist/mcp/server.d.ts +255 -3
- package/dist/mcp/session-event-view.d.ts +94 -0
- package/dist/mcp/session-view.d.ts +66 -2
- package/dist/mcp/session-wait.d.ts +174 -0
- package/dist/mcp-oauth.d.ts +52 -0
- package/dist/model-catalog.d.ts +11 -31
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +52 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +25 -0
- package/dist/routes/api-keys.d.ts +17 -0
- package/dist/routes/artifact-catalog.d.ts +6 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/billing.d.ts +49 -0
- package/dist/routes/browser-sessions.d.ts +59 -2
- package/dist/routes/claude-subscription-accounts.d.ts +3 -0
- package/dist/routes/claude-subscription-oauth.d.ts +3 -0
- package/dist/routes/client-errors.d.ts +146 -0
- package/dist/routes/codex.d.ts +76 -2
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/computer-sessions.d.ts +14 -0
- package/dist/routes/connect.d.ts +5 -0
- package/dist/routes/editable-artifacts.d.ts +2 -0
- package/dist/routes/external-identity-links.d.ts +6 -0
- package/dist/routes/feedback.d.ts +3 -0
- package/dist/routes/github-browser-pages.d.ts +12 -0
- package/dist/routes/github.d.ts +2 -0
- package/dist/routes/insights-response-cache.d.ts +10 -0
- package/dist/routes/insights-usage.d.ts +12 -0
- package/dist/routes/insights.d.ts +30 -1
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/routes/managed-auth-session-sets.d.ts +26 -0
- package/dist/routes/managed-onboarding.d.ts +29 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-connection-access.d.ts +3 -0
- package/dist/routes/organization-integration-policy.d.ts +3 -0
- package/dist/routes/organization-integrations.d.ts +6 -0
- package/dist/routes/organization-mcp-connections.d.ts +3 -0
- package/dist/routes/organization-model-providers.d.ts +3 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/organization-sessions.d.ts +26 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +9 -0
- package/dist/routes/sessions.d.ts +44 -6
- package/dist/routes/skill-content.d.ts +4 -0
- package/dist/routes/skill-install-authority.d.ts +6 -0
- package/dist/routes/subscription-account-pools.d.ts +34 -0
- package/dist/routes/subscription-pool-access.d.ts +16 -0
- package/dist/routes/supergrok.d.ts +6 -1
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +29 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +19 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +9 -1
- package/dist/sandbox/machines.d.ts +5 -2
- package/dist/sandbox/metrics-ingestion.d.ts +28 -20
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
- package/dist/sandbox/viewer.d.ts +26 -1
- package/dist/sandbox-file-artifacts.d.ts +50 -0
- package/dist/scheduled-task-deletion.d.ts +13 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-MPS2OZD3.js +292 -0
- package/dist/session-event-view-MPS2OZD3.js.map +1 -0
- package/dist/site-session-origin.d.ts +13 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/providers/xai-subscription.d.ts +1 -0
- package/dist/transcription/service.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +33 -0
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-delete-observability.d.ts +10 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +123 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/package.json +23 -19
- package/src/access-grant-rls.ts +75 -0
- package/src/admission-trace.ts +14 -0
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +1967 -252
- package/src/artifact-catalog-cursor.ts +60 -0
- package/src/auth/managed-auth-attempt-context.ts +70 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +222 -0
- package/src/auth/managed-auth.ts +536 -60
- package/src/auth/managed-email.ts +174 -0
- package/src/auth/managed-sign-in-notifications.ts +81 -0
- package/src/auth/new-signups-gate.ts +49 -0
- package/src/auth/organization-user-setup.ts +307 -0
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-subscription-account-conflicts.ts +12 -0
- package/src/claude-subscription-account-usage.ts +48 -0
- package/src/claude-subscription-oauth.ts +362 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +97 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +194 -30
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-ownership.ts +199 -0
- package/src/controller-data-plane.ts +81 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +28 -3
- package/src/editable-artifact-production.ts +23 -7
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +27 -21
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/fatal-process-boundary.ts +246 -0
- package/src/gateway-realtime.ts +1 -1
- package/src/github-access.ts +147 -5
- package/src/http/acting-person.ts +71 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +115 -1
- package/src/http/auth.ts +48 -0
- package/src/http/cors.ts +67 -0
- package/src/http/deprecation.ts +118 -0
- package/src/http/interaction-control-error.ts +164 -0
- package/src/http/keyed-admission.ts +29 -0
- package/src/http/local-browser-boundary.ts +343 -0
- package/src/http/metrics-listener.ts +57 -0
- package/src/http/path-identifier.ts +54 -0
- package/src/http/registered-route-label.ts +34 -0
- package/src/http/request-body.ts +127 -0
- package/src/http/request-source.ts +178 -0
- package/src/http/sse.ts +486 -132
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +157 -52
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +98 -11
- package/src/integrations/atlassian.ts +474 -136
- package/src/integrations/connect-authority.ts +2 -0
- package/src/integrations/connect-callback-return.ts +86 -0
- package/src/integrations/fiken.ts +354 -64
- package/src/integrations/github-app-connect.ts +435 -0
- package/src/integrations/github-installation-proof.ts +60 -0
- package/src/integrations/github-lens-connect.ts +76 -0
- package/src/integrations/github-skill-source.ts +2 -142
- package/src/integrations/google-drive.ts +717 -225
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +48 -0
- package/src/integrations/oauth-client.ts +1356 -459
- package/src/integrations/oauth-profiles.ts +537 -0
- package/src/integrations/oauth-return-path.ts +18 -0
- package/src/integrations/personal-github-repositories.ts +674 -0
- package/src/integrations/personal-github.ts +825 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +250 -22
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +963 -239
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +83 -0
- package/src/integrations/slack-interactions.ts +3167 -474
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/social-oauth.ts +213 -15
- package/src/interaction-agent-access.ts +48 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/interaction-metrics.ts +30 -0
- package/src/mcp/action-catalog-types.ts +9 -0
- package/src/mcp/action-catalog.gen.ts +6696 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/company-profile-agent-admin.ts +198 -0
- package/src/mcp/contract-input.ts +141 -0
- package/src/mcp/documents.ts +65 -377
- package/src/mcp/editable-artifacts.ts +12 -6
- package/src/mcp/files.ts +80 -43
- package/src/mcp/first-party-tool-permissions.ts +305 -0
- package/src/mcp/knowledge-entries.ts +322 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-input.ts +76 -0
- package/src/mcp/scheduled-task-view.ts +44 -3
- package/src/mcp/server.ts +3030 -1267
- package/src/mcp/session-event-view.ts +383 -0
- package/src/mcp/session-view.ts +84 -3
- package/src/mcp/session-wait.ts +665 -0
- package/src/mcp-oauth.ts +1123 -0
- package/src/model-catalog.ts +91 -333
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +397 -0
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +364 -74
- package/src/routes/api-keys.ts +336 -10
- package/src/routes/artifact-catalog.ts +279 -0
- package/src/routes/automations.ts +768 -0
- package/src/routes/billing.ts +461 -46
- package/src/routes/browser-sessions.ts +780 -112
- package/src/routes/canonical-human-identities.ts +90 -14
- package/src/routes/capabilities.ts +107 -6
- package/src/routes/channels.ts +20 -3
- package/src/routes/claude-subscription-accounts.ts +334 -0
- package/src/routes/claude-subscription-oauth.ts +142 -0
- package/src/routes/client-errors.ts +362 -0
- package/src/routes/codex.ts +833 -116
- package/src/routes/company-brain.ts +322 -0
- package/src/routes/company-profile.ts +65 -1
- package/src/routes/computer-sessions.ts +557 -106
- package/src/routes/connect.ts +1938 -0
- package/src/routes/connections.ts +1039 -349
- package/src/routes/documents.ts +488 -287
- package/src/routes/editable-artifacts.ts +79 -9
- package/src/routes/enrollments.ts +135 -39
- package/src/routes/environments.ts +154 -58
- package/src/routes/external-identity-links.ts +201 -0
- package/src/routes/feedback.ts +113 -0
- package/src/routes/files.ts +267 -33
- package/src/routes/github-browser-pages.ts +162 -0
- package/src/routes/github.ts +446 -144
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +153 -19
- package/src/routes/install.ts +133 -38
- package/src/routes/integration-facets.ts +107 -33
- package/src/routes/interaction-resources.ts +30 -3
- package/src/routes/knowledge.ts +488 -0
- package/src/routes/machines.ts +313 -15
- package/src/routes/managed-auth-session-sets.ts +1218 -0
- package/src/routes/managed-onboarding.ts +337 -0
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-connection-access.ts +208 -0
- package/src/routes/organization-integration-policy.ts +116 -0
- package/src/routes/organization-integrations.ts +326 -0
- package/src/routes/organization-mcp-connections.ts +178 -0
- package/src/routes/organization-memberships.ts +1019 -27
- package/src/routes/organization-model-providers.ts +293 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +350 -0
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +370 -0
- package/src/routes/plugins.ts +251 -22
- package/src/routes/pr-review-github.ts +881 -0
- package/src/routes/pr-review.ts +622 -0
- package/src/routes/preference-registry.ts +12 -93
- package/src/routes/rigs.ts +167 -50
- package/src/routes/scheduled-tasks.ts +346 -41
- package/src/routes/session-artifact-associations.ts +87 -0
- package/src/routes/session-history-imports.ts +280 -0
- package/src/routes/sessions.ts +2306 -264
- package/src/routes/skill-content.ts +297 -0
- package/src/routes/skill-install-authority.ts +64 -0
- package/src/routes/skills.ts +62 -9
- package/src/routes/social.ts +46 -18
- package/src/routes/subscription-account-pools.ts +436 -0
- package/src/routes/subscription-pool-access.ts +126 -0
- package/src/routes/supergrok.ts +219 -303
- package/src/routes/transcription-recordings.ts +38 -3
- package/src/routes/transcriptions.ts +4 -0
- package/src/routes/usage-allowances.ts +378 -0
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/video-generation.ts +14 -10
- package/src/routes/workspace-artifacts.ts +275 -69
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-learning.ts +159 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +38 -25
- package/src/routes/workspaces.ts +1004 -127
- package/src/sandbox/auth-callout.ts +96 -19
- package/src/sandbox/channel-a.ts +323 -24
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +81 -6
- package/src/sandbox/machines.ts +218 -76
- package/src/sandbox/metrics-ingestion.ts +411 -62
- package/src/sandbox/rematerialize.ts +121 -19
- package/src/sandbox/session-attach-variable-sets.ts +131 -0
- package/src/sandbox/viewer.ts +280 -25
- package/src/sandbox-file-artifacts.ts +413 -0
- package/src/scheduled-task-deletion.ts +138 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +6 -3
- package/src/site-session-origin.ts +48 -0
- package/src/site-uploads.ts +224 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/providers/codex-subscription.ts +2 -0
- package/src/transcription/providers/openai.ts +1 -0
- package/src/transcription/providers/xai-subscription.ts +49 -3
- package/src/transcription/segmenter.ts +8 -2
- package/src/transcription/service.ts +43 -6
- package/src/work-discovery-observability.ts +121 -0
- package/src/workspace-artifact-content.ts +144 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-delete-observability.ts +75 -0
- package/src/workspace-deletion.ts +73 -0
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +870 -0
- package/src/workspace-webhook-dispatch.ts +190 -0
- package/src/xai-subscription-auth.ts +9 -2
- package/dist/chunk-WCXHI3FF.js +0 -49920
- package/dist/chunk-WCXHI3FF.js.map +0 -1
- package/dist/routes/packs.d.ts +0 -3
- package/src/routes/packs.ts +0 -729
package/src/auth/managed-auth.ts
CHANGED
|
@@ -1,18 +1,49 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { canonicalPublicOrigin, managedUserEmailAllowed, type Settings } from "@opengeni/config";
|
|
2
|
+
import { MANAGED_AUTH_NEW_SIGNUPS_PAUSED_CODE } from "@opengeni/contracts";
|
|
3
|
+
import {
|
|
4
|
+
configureManagedUserAdmission,
|
|
5
|
+
type ManagedAuth,
|
|
6
|
+
type ManagedEmailMessage,
|
|
7
|
+
type ManagedEmailTransport,
|
|
8
|
+
} from "@opengeni/core";
|
|
3
9
|
import type { Database } from "@opengeni/db";
|
|
4
10
|
import { ensureManagedAccessForUser } from "@opengeni/db";
|
|
5
11
|
import {
|
|
6
12
|
ensureCanonicalHumanIdentityForAuthUser,
|
|
7
13
|
getCanonicalHumanIdentityProjection,
|
|
14
|
+
getCanonicalHumanExactLoginBindingForAuthUser,
|
|
8
15
|
synchronizeCanonicalHumanLoginBindings,
|
|
9
16
|
} from "@opengeni/db/canonical-human-identities";
|
|
17
|
+
import type { Observability } from "@opengeni/observability";
|
|
10
18
|
import { betterAuth } from "better-auth";
|
|
11
|
-
import { createEmailVerificationToken } from "better-auth/api";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
19
|
+
import { APIError, createEmailVerificationToken } from "better-auth/api";
|
|
20
|
+
import { hashPassword, verifyPassword } from "better-auth/crypto";
|
|
21
|
+
import { sql } from "drizzle-orm";
|
|
22
|
+
import { Pool, type PoolConfig } from "pg";
|
|
14
23
|
|
|
24
|
+
import { TRUSTED_CLIENT_ADDRESS_HEADER } from "../http/request-source";
|
|
15
25
|
import { decideCanonicalHumanSessionAdmission } from "./canonical-human-session-admission";
|
|
26
|
+
import {
|
|
27
|
+
createManagedAuthEmailThrottleHook,
|
|
28
|
+
MANAGED_AUTH_CLIENT_RATE_LIMIT_RULES,
|
|
29
|
+
} from "./managed-auth-rate-limits";
|
|
30
|
+
import { deliverManagedSignInNotification } from "./managed-sign-in-notifications";
|
|
31
|
+
import {
|
|
32
|
+
createManagedAuthNewSignupsGate,
|
|
33
|
+
type ManagedAuthNewSignupsGate,
|
|
34
|
+
} from "./new-signups-gate";
|
|
35
|
+
import { createSignupFunnelMetrics } from "./signup-funnel-metrics";
|
|
36
|
+
import {
|
|
37
|
+
currentManagedAuthProviderId,
|
|
38
|
+
currentManagedAuthAttemptId,
|
|
39
|
+
recordCurrentManagedAuthSession,
|
|
40
|
+
shouldDiscardCurrentManagedAuthProviderSession,
|
|
41
|
+
currentManagedSignInConnectIntent,
|
|
42
|
+
} from "./managed-auth-attempt-context";
|
|
43
|
+
|
|
44
|
+
// Only used when no Better Auth secret is configured (local development);
|
|
45
|
+
// the email throttle key is a content-free digest either way.
|
|
46
|
+
const MANAGED_AUTH_EMAIL_THROTTLE_FALLBACK_KEY = "opengeni:managed-auth:email-throttle:local";
|
|
16
47
|
|
|
17
48
|
// `ManagedAuth` (the Better Auth `Auth<any>` alias) is owned by @opengeni/core
|
|
18
49
|
// (`managed-auth-type.ts`) — `dependencies.ts`/`access` reference it as a
|
|
@@ -20,19 +51,214 @@ import { decideCanonicalHumanSessionAdmission } from "./canonical-human-session-
|
|
|
20
51
|
// importers (`app.ts`) keep the same import path.
|
|
21
52
|
export type { ManagedAuth };
|
|
22
53
|
|
|
23
|
-
export function
|
|
54
|
+
export function managedAuthRequiresEmailVerification(
|
|
55
|
+
settings: Pick<Settings, "environment">,
|
|
56
|
+
): boolean {
|
|
57
|
+
return settings.environment !== "local";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function managedAuthUserCreateOverride(
|
|
61
|
+
settings: Pick<Settings, "environment">,
|
|
62
|
+
user: { emailVerified: boolean } & Record<string, unknown>,
|
|
63
|
+
): { data: typeof user } | undefined {
|
|
64
|
+
if (managedAuthRequiresEmailVerification(settings)) return undefined;
|
|
65
|
+
return { data: { ...user, emailVerified: true } };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const MANAGED_AUTH_NEW_SIGNUPS_PAUSED_MESSAGE =
|
|
69
|
+
"We're at capacity for new accounts right now. Please try again later.";
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Typed refusal for a new account while the deployment has paused sign-ups.
|
|
73
|
+
* Existing accounts are unaffected; the body mirrors Better Auth's
|
|
74
|
+
* `{ code, message }` error shape so browser and SDK callers parse it alike.
|
|
75
|
+
*/
|
|
76
|
+
export function managedAuthNewSignupsPausedResponse(): Response {
|
|
77
|
+
return Response.json(
|
|
78
|
+
{
|
|
79
|
+
code: MANAGED_AUTH_NEW_SIGNUPS_PAUSED_CODE,
|
|
80
|
+
message: MANAGED_AUTH_NEW_SIGNUPS_PAUSED_MESSAGE,
|
|
81
|
+
},
|
|
82
|
+
{ status: 403, headers: { "cache-control": "no-store" } },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Thrown from the user-create hook while the runtime switch has paused new
|
|
88
|
+
* sign-ups. Better Auth returns it as `403 { code: "NEW_SIGNUPS_PAUSED" }` on
|
|
89
|
+
* email sign-up, and its OAuth callback turns the message into the
|
|
90
|
+
* `error=signup_disabled` redirect it also uses for a static provider refusal.
|
|
91
|
+
*/
|
|
92
|
+
export class ManagedAuthNewSignupsPausedError extends APIError {
|
|
93
|
+
constructor() {
|
|
94
|
+
super("FORBIDDEN", { code: MANAGED_AUTH_NEW_SIGNUPS_PAUSED_CODE, message: "signup disabled" });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function managedAuthUserCreateAdmission(
|
|
99
|
+
settings: Pick<Settings, "environment" | "allowedUserEmails"> &
|
|
100
|
+
Partial<Pick<Settings, "managedAuthNewSignupsEnabled">>,
|
|
101
|
+
user: { emailVerified: boolean } & Record<string, unknown>,
|
|
102
|
+
providerId: string,
|
|
103
|
+
): { data: typeof user } | false | undefined {
|
|
104
|
+
// Backstop for every Better Auth user-create path. Invited-user account
|
|
105
|
+
// setup does not create users through Better Auth, so it is unaffected.
|
|
106
|
+
if (settings.managedAuthNewSignupsEnabled === false) return false;
|
|
107
|
+
if (!managedUserEmailAllowed(settings.allowedUserEmails, user.email)) return false;
|
|
108
|
+
if (
|
|
109
|
+
managedAuthRequiresEmailVerification(settings) &&
|
|
110
|
+
providerId !== "credential" &&
|
|
111
|
+
!user.emailVerified
|
|
112
|
+
) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return managedAuthUserCreateOverride(settings, user);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Per-provider Better Auth options while sign-ups are paused. A social sign-in
|
|
120
|
+
* for an existing human still signs in (and may link a verified provider);
|
|
121
|
+
* an unknown human is redirected back with `error=signup_disabled`.
|
|
122
|
+
* `disableSignUp` is the hard switch: `disableImplicitSignUp` alone can be
|
|
123
|
+
* overridden by a client-supplied `requestSignUp`.
|
|
124
|
+
*/
|
|
125
|
+
export function managedAuthSocialSignupOptions(
|
|
126
|
+
settings: Pick<Settings, "managedAuthNewSignupsEnabled">,
|
|
127
|
+
): { disableImplicitSignUp?: true; disableSignUp?: true } {
|
|
128
|
+
return settings.managedAuthNewSignupsEnabled
|
|
129
|
+
? {}
|
|
130
|
+
: { disableImplicitSignUp: true, disableSignUp: true };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Keep Better Auth password policy and storage format behind this boundary. */
|
|
134
|
+
export async function hashManagedAuthPassword(password: string): Promise<string> {
|
|
135
|
+
return await hashPassword(password);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function verifyManagedAuthPassword(password: string, hash: string): Promise<boolean> {
|
|
139
|
+
return await verifyPassword({ password, hash });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
type ManagedAuthPoolObservability = Pick<Observability, "warn" | "incrementCounter">;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Bounded pool settings for Better Auth's dedicated `pg` pool. A connection
|
|
146
|
+
* attempt fails after 10 s instead of queueing requests indefinitely while the
|
|
147
|
+
* database is unreachable, and connections recycle like the main postgres-js
|
|
148
|
+
* pool so a pooler- or failover-replaced backend is not held forever.
|
|
149
|
+
*/
|
|
150
|
+
export const MANAGED_AUTH_DATABASE_POOL_OPTIONS = {
|
|
151
|
+
max: 10,
|
|
152
|
+
connectionTimeoutMillis: 10_000,
|
|
153
|
+
idleTimeoutMillis: 30_000,
|
|
154
|
+
maxLifetimeSeconds: 1_800,
|
|
155
|
+
keepAlive: true,
|
|
156
|
+
} as const satisfies PoolConfig;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Better Auth's `pg` pool. When the server closes a connection (a restart,
|
|
160
|
+
* failover, or maintenance window) `pg` emits `error`: on the pool for an idle
|
|
161
|
+
* client, and on the client itself for one checked out by Better Auth's query
|
|
162
|
+
* layer. Without listeners either becomes an uncaught exception and the API's
|
|
163
|
+
* fatal process boundary exits. Both listeners record one content-free signal
|
|
164
|
+
* and keep serving. The pool has already discarded a failed idle client and
|
|
165
|
+
* opens a fresh connection for the next checkout; a checked-out client's
|
|
166
|
+
* in-flight query already rejects to its caller, and the pool discards the
|
|
167
|
+
* dead client on release. An idle client's error also reaches its own
|
|
168
|
+
* listener, so that listener records only while the client is checked out,
|
|
169
|
+
* and at most once per checkout.
|
|
170
|
+
*/
|
|
171
|
+
export function createManagedAuthDatabasePool(
|
|
172
|
+
databaseUrl: string,
|
|
173
|
+
observability?: ManagedAuthPoolObservability,
|
|
174
|
+
): Pool {
|
|
175
|
+
const pool = new Pool({ connectionString: databaseUrl, ...MANAGED_AUTH_DATABASE_POOL_OPTIONS });
|
|
176
|
+
const checkedOut = new WeakSet<object>();
|
|
177
|
+
pool.on("acquire", (client) => checkedOut.add(client));
|
|
178
|
+
pool.on("release", (_error, client) => checkedOut.delete(client));
|
|
179
|
+
pool.on("connect", (client) => {
|
|
180
|
+
client.on("error", () => {
|
|
181
|
+
// One lost connection can emit twice (the server's termination
|
|
182
|
+
// message, then the socket end), so count it once.
|
|
183
|
+
if (checkedOut.delete(client)) {
|
|
184
|
+
recordManagedAuthPoolError(observability, "checked_out_connection_failed");
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
pool.on("error", () => recordManagedAuthPoolError(observability, "idle_connection_discarded"));
|
|
189
|
+
return pool;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export const MANAGED_AUTH_DATABASE_POOL_ERRORS_METRIC = {
|
|
193
|
+
name: "opengeni_managed_auth_database_pool_errors_total",
|
|
194
|
+
help: "Managed-auth database connections the server closed or lost, by pool state.",
|
|
195
|
+
} as const;
|
|
196
|
+
|
|
197
|
+
function recordManagedAuthPoolError(
|
|
198
|
+
observability: ManagedAuthPoolObservability | undefined,
|
|
199
|
+
outcome: "idle_connection_discarded" | "checked_out_connection_failed",
|
|
200
|
+
): void {
|
|
201
|
+
try {
|
|
202
|
+
observability?.incrementCounter({
|
|
203
|
+
...MANAGED_AUTH_DATABASE_POOL_ERRORS_METRIC,
|
|
204
|
+
labels: { outcome },
|
|
205
|
+
});
|
|
206
|
+
observability?.warn("Managed auth database connection failed", {
|
|
207
|
+
dependency: "managed_auth_database",
|
|
208
|
+
outcome,
|
|
209
|
+
});
|
|
210
|
+
} catch {
|
|
211
|
+
// An observer failure must not turn a recovered pool error into a crash.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function createManagedAuth(
|
|
216
|
+
settings: Settings,
|
|
217
|
+
db: Database,
|
|
218
|
+
managedEmailTransport: ManagedEmailTransport,
|
|
219
|
+
options: {
|
|
220
|
+
observability?: ManagedAuthPoolObservability;
|
|
221
|
+
newSignupsGate?: ManagedAuthNewSignupsGate;
|
|
222
|
+
} = {},
|
|
223
|
+
): ManagedAuth | null {
|
|
24
224
|
if (settings.productAccessMode !== "managed") {
|
|
25
225
|
return null;
|
|
26
226
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
227
|
+
// Content-free sign-up funnel counters; recording never fails an auth flow.
|
|
228
|
+
const funnel = options.observability
|
|
229
|
+
? createSignupFunnelMetrics(options.observability)
|
|
230
|
+
: undefined;
|
|
231
|
+
const requireEmailVerification = managedAuthRequiresEmailVerification(settings);
|
|
232
|
+
const pool = createManagedAuthDatabasePool(settings.databaseUrl, options.observability);
|
|
233
|
+
// The deployment ceiling is static Better Auth configuration; the runtime
|
|
234
|
+
// switch is checked per user create in the hook below.
|
|
235
|
+
const newSignupsSocialProviderOptions = managedAuthSocialSignupOptions(settings);
|
|
236
|
+
const newSignupsGate =
|
|
237
|
+
options.newSignupsGate ??
|
|
238
|
+
createManagedAuthNewSignupsGate({ db, settings, observability: options.observability });
|
|
239
|
+
const auth = betterAuth({
|
|
240
|
+
appName: "Opengeni",
|
|
30
241
|
baseURL: betterAuthBaseUrl(settings),
|
|
31
242
|
basePath: "/v1/auth",
|
|
32
243
|
secret: settings.betterAuthSecret,
|
|
33
244
|
database: pool,
|
|
34
245
|
trustedOrigins: betterAuthTrustedOrigins(settings),
|
|
246
|
+
hooks: {
|
|
247
|
+
before: createManagedAuthEmailThrottleHook(
|
|
248
|
+
db,
|
|
249
|
+
settings.betterAuthSecret ?? MANAGED_AUTH_EMAIL_THROTTLE_FALLBACK_KEY,
|
|
250
|
+
),
|
|
251
|
+
},
|
|
35
252
|
advanced: {
|
|
253
|
+
// Better Auth reads client addresses only from request headers. The API
|
|
254
|
+
// strips this header from every inbound request and re-stamps it on
|
|
255
|
+
// managed-auth routes with the trusted source address (transport peer,
|
|
256
|
+
// or the forwarded client when OPENGENI_API_TRUSTED_PROXY_HOPS declares
|
|
257
|
+
// a proxy chain), so callers cannot choose their rate-limit bucket or
|
|
258
|
+
// recorded session address.
|
|
259
|
+
ipAddress: {
|
|
260
|
+
ipAddressHeaders: [TRUSTED_CLIENT_ADDRESS_HEADER],
|
|
261
|
+
},
|
|
36
262
|
useSecureCookies: settings.publicBaseUrl?.startsWith("https://") ?? false,
|
|
37
263
|
...(settings.betterAuthCookieDomain
|
|
38
264
|
? {
|
|
@@ -53,6 +279,7 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
53
279
|
fields: {
|
|
54
280
|
lastRequest: "last_request",
|
|
55
281
|
},
|
|
282
|
+
customRules: MANAGED_AUTH_CLIENT_RATE_LIMIT_RULES,
|
|
56
283
|
},
|
|
57
284
|
user: {
|
|
58
285
|
modelName: "auth_users",
|
|
@@ -93,6 +320,25 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
93
320
|
returned: false,
|
|
94
321
|
bigint: true,
|
|
95
322
|
},
|
|
323
|
+
loginBindingId: {
|
|
324
|
+
type: "string",
|
|
325
|
+
fieldName: "login_binding_id",
|
|
326
|
+
input: false,
|
|
327
|
+
returned: false,
|
|
328
|
+
},
|
|
329
|
+
loginBindingRevision: {
|
|
330
|
+
type: "number",
|
|
331
|
+
fieldName: "login_binding_revision",
|
|
332
|
+
input: false,
|
|
333
|
+
returned: false,
|
|
334
|
+
bigint: true,
|
|
335
|
+
},
|
|
336
|
+
managedAuthLoginTransactionId: {
|
|
337
|
+
type: "string",
|
|
338
|
+
fieldName: "managed_auth_login_transaction_id",
|
|
339
|
+
input: false,
|
|
340
|
+
returned: false,
|
|
341
|
+
},
|
|
96
342
|
},
|
|
97
343
|
},
|
|
98
344
|
account: {
|
|
@@ -110,8 +356,42 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
110
356
|
updatedAt: "updated_at",
|
|
111
357
|
},
|
|
112
358
|
accountLinking: {
|
|
113
|
-
enabled:
|
|
359
|
+
enabled: true,
|
|
360
|
+
requireLocalEmailVerified: true,
|
|
361
|
+
trustedProviders: [],
|
|
362
|
+
allowDifferentEmails: false,
|
|
363
|
+
},
|
|
364
|
+
additionalFields: {
|
|
365
|
+
managedLinkIntentId: {
|
|
366
|
+
type: "string",
|
|
367
|
+
fieldName: "managed_link_intent_id",
|
|
368
|
+
input: false,
|
|
369
|
+
returned: false,
|
|
370
|
+
},
|
|
114
371
|
},
|
|
372
|
+
encryptOAuthTokens: true,
|
|
373
|
+
storeStateStrategy: "database",
|
|
374
|
+
},
|
|
375
|
+
socialProviders: {
|
|
376
|
+
...(settings.managedAuthGoogleClientId && settings.managedAuthGoogleClientSecret
|
|
377
|
+
? {
|
|
378
|
+
google: {
|
|
379
|
+
clientId: settings.managedAuthGoogleClientId,
|
|
380
|
+
clientSecret: settings.managedAuthGoogleClientSecret,
|
|
381
|
+
prompt: "select_account" as const,
|
|
382
|
+
...newSignupsSocialProviderOptions,
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
: {}),
|
|
386
|
+
...(settings.managedAuthGithubClientId && settings.managedAuthGithubClientSecret
|
|
387
|
+
? {
|
|
388
|
+
github: {
|
|
389
|
+
clientId: settings.managedAuthGithubClientId,
|
|
390
|
+
clientSecret: settings.managedAuthGithubClientSecret,
|
|
391
|
+
...newSignupsSocialProviderOptions,
|
|
392
|
+
},
|
|
393
|
+
}
|
|
394
|
+
: {}),
|
|
115
395
|
},
|
|
116
396
|
verification: {
|
|
117
397
|
modelName: "auth_verifications",
|
|
@@ -124,50 +404,94 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
124
404
|
},
|
|
125
405
|
emailAndPassword: {
|
|
126
406
|
enabled: true,
|
|
127
|
-
|
|
407
|
+
// Sign-in, password reset, and verification stay enabled; only new
|
|
408
|
+
// account creation is refused while sign-ups are paused.
|
|
409
|
+
disableSignUp: !settings.managedAuthNewSignupsEnabled,
|
|
410
|
+
// Local managed mode exists so the complete human/org tenancy flow can
|
|
411
|
+
// be exercised without first configuring a transactional-email vendor.
|
|
412
|
+
// Every non-local deployment retains the verified-email boundary.
|
|
413
|
+
requireEmailVerification,
|
|
128
414
|
revokeSessionsOnPasswordReset: true,
|
|
129
415
|
onExistingUserSignUp: async ({ user }) => {
|
|
130
|
-
if (!user.emailVerified) {
|
|
416
|
+
if (requireEmailVerification && !user.emailVerified) {
|
|
131
417
|
const url = await verificationUrl(settings, user.email);
|
|
132
|
-
await
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
html: `<p>Verify your OpenGeni email:</p><p><a href="${escapeHtml(url)}">Verify email</a></p>`,
|
|
137
|
-
});
|
|
418
|
+
await sendManagedAuthEmail(
|
|
419
|
+
managedEmailTransport,
|
|
420
|
+
emailVerificationMessage(user.email, url),
|
|
421
|
+
);
|
|
138
422
|
}
|
|
139
423
|
},
|
|
140
424
|
sendResetPassword: async ({ user, url }) => {
|
|
141
|
-
await
|
|
425
|
+
await sendManagedAuthEmail(managedEmailTransport, {
|
|
426
|
+
kind: "password_reset",
|
|
142
427
|
to: user.email,
|
|
143
|
-
subject: "Reset your
|
|
144
|
-
text: `Reset your
|
|
145
|
-
html: `<p>Reset your
|
|
428
|
+
subject: "Reset your Opengeni password",
|
|
429
|
+
text: `Reset your Opengeni password: ${url}`,
|
|
430
|
+
html: `<p>Reset your Opengeni password:</p><p><a href="${escapeHtml(url)}">Reset password</a></p>`,
|
|
146
431
|
});
|
|
147
432
|
},
|
|
148
433
|
},
|
|
149
434
|
emailVerification: {
|
|
150
|
-
sendOnSignUp:
|
|
435
|
+
sendOnSignUp: requireEmailVerification,
|
|
436
|
+
// The first successful verification link click signs the user in (a
|
|
437
|
+
// reused link creates no session). Only legacy mode: session-set modes
|
|
438
|
+
// bind sign-in to an isolated browser transaction, and the provider
|
|
439
|
+
// session this would create is discarded there by design.
|
|
440
|
+
autoSignInAfterVerification: settings.managedAuthSessionSetMode === "legacy",
|
|
151
441
|
sendVerificationEmail: async ({ user, url }) => {
|
|
152
|
-
await
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
html: `<p>Verify your OpenGeni email:</p><p><a href="${escapeHtml(url)}">Verify email</a></p>`,
|
|
157
|
-
});
|
|
442
|
+
await sendManagedAuthEmail(
|
|
443
|
+
managedEmailTransport,
|
|
444
|
+
emailVerificationMessage(user.email, url),
|
|
445
|
+
);
|
|
158
446
|
},
|
|
159
447
|
afterEmailVerification: async (user) => {
|
|
448
|
+
funnel?.recordEmailVerified();
|
|
160
449
|
await ensureManagedAccessForUser(db, {
|
|
161
450
|
userId: user.id,
|
|
162
451
|
email: user.email,
|
|
163
452
|
name: user.name,
|
|
453
|
+
emailVerified: true,
|
|
454
|
+
provisionFallbackOrganization: false,
|
|
164
455
|
});
|
|
165
456
|
},
|
|
166
457
|
},
|
|
167
458
|
databaseHooks: {
|
|
459
|
+
account: {
|
|
460
|
+
create: {
|
|
461
|
+
before: async (account) => ({
|
|
462
|
+
data: {
|
|
463
|
+
...account,
|
|
464
|
+
...(currentManagedSignInConnectIntent()
|
|
465
|
+
? { managedLinkIntentId: currentManagedSignInConnectIntent() }
|
|
466
|
+
: {}),
|
|
467
|
+
},
|
|
468
|
+
}),
|
|
469
|
+
after: async (account) => {
|
|
470
|
+
if (
|
|
471
|
+
!["google", "github"].includes(account.providerId) ||
|
|
472
|
+
currentManagedSignInConnectIntent()
|
|
473
|
+
)
|
|
474
|
+
return;
|
|
475
|
+
await deliverManagedSignInNotification(db, managedEmailTransport, account.id);
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
168
479
|
session: {
|
|
169
480
|
create: {
|
|
170
481
|
before: async (session) => {
|
|
482
|
+
if (settings.allowedUserEmails !== undefined) {
|
|
483
|
+
const result = await pool.query<{ email: string; email_verified: boolean }>(
|
|
484
|
+
"select email, email_verified from auth_users where id = $1",
|
|
485
|
+
[session.userId],
|
|
486
|
+
);
|
|
487
|
+
const user = result.rows[0];
|
|
488
|
+
if (
|
|
489
|
+
!user?.email_verified ||
|
|
490
|
+
!managedUserEmailAllowed(settings.allowedUserEmails, user.email)
|
|
491
|
+
)
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
const providerId = currentManagedAuthProviderId();
|
|
171
495
|
await ensureCanonicalHumanIdentityForAuthUser(db, session.userId);
|
|
172
496
|
const preflightProjection = await getCanonicalHumanIdentityProjection(
|
|
173
497
|
db,
|
|
@@ -179,8 +503,13 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
179
503
|
binding: null,
|
|
180
504
|
});
|
|
181
505
|
if (!preflight.allowed) {
|
|
506
|
+
const exactRecoveryBinding = await getCanonicalHumanExactLoginBindingForAuthUser(db, {
|
|
507
|
+
authUserId: session.userId,
|
|
508
|
+
providerId,
|
|
509
|
+
});
|
|
182
510
|
const recoveryBinding = preflightProjection.loginBindings.find(
|
|
183
|
-
(binding) =>
|
|
511
|
+
(binding) =>
|
|
512
|
+
binding.id === exactRecoveryBinding.id && binding.status === "recovery_pending",
|
|
184
513
|
);
|
|
185
514
|
const recoveryAdmission = decideCanonicalHumanSessionAdmission({
|
|
186
515
|
intent: "recovery_completion",
|
|
@@ -199,17 +528,27 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
199
528
|
return {
|
|
200
529
|
data: {
|
|
201
530
|
...session,
|
|
531
|
+
...(shouldDiscardCurrentManagedAuthProviderSession()
|
|
532
|
+
? { expiresAt: new Date(0) }
|
|
533
|
+
: {}),
|
|
202
534
|
identityId: preflightProjection.activeIdentity.id,
|
|
203
535
|
identityRevision: preflightProjection.activeIdentity.identityRevision,
|
|
204
536
|
authRevision: preflightProjection.activeIdentity.authRevision,
|
|
537
|
+
loginBindingId: recoveryBinding!.id,
|
|
538
|
+
loginBindingRevision: recoveryBinding!.revision,
|
|
539
|
+
managedAuthLoginTransactionId: currentManagedAuthAttemptId(),
|
|
205
540
|
},
|
|
206
541
|
};
|
|
207
542
|
}
|
|
208
543
|
|
|
209
544
|
await synchronizeCanonicalHumanLoginBindings(db, session.userId);
|
|
210
545
|
const projection = await getCanonicalHumanIdentityProjection(db, session.userId);
|
|
546
|
+
const exactBinding = await getCanonicalHumanExactLoginBindingForAuthUser(db, {
|
|
547
|
+
authUserId: session.userId,
|
|
548
|
+
providerId,
|
|
549
|
+
});
|
|
211
550
|
const activeBinding = projection.loginBindings.find(
|
|
212
|
-
(binding) => binding.id ===
|
|
551
|
+
(binding) => binding.id === exactBinding.id,
|
|
213
552
|
);
|
|
214
553
|
const admission = decideCanonicalHumanSessionAdmission({
|
|
215
554
|
intent: "ordinary_session",
|
|
@@ -229,27 +568,174 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
|
|
|
229
568
|
return {
|
|
230
569
|
data: {
|
|
231
570
|
...session,
|
|
571
|
+
...(shouldDiscardCurrentManagedAuthProviderSession()
|
|
572
|
+
? { expiresAt: new Date(0) }
|
|
573
|
+
: {}),
|
|
232
574
|
identityId: projection.activeIdentity.id,
|
|
233
575
|
identityRevision: projection.activeIdentity.identityRevision,
|
|
234
576
|
authRevision: projection.activeIdentity.authRevision,
|
|
577
|
+
loginBindingId: exactBinding.id,
|
|
578
|
+
loginBindingRevision: exactBinding.revision,
|
|
579
|
+
managedAuthLoginTransactionId: currentManagedAuthAttemptId(),
|
|
235
580
|
},
|
|
236
581
|
};
|
|
237
582
|
},
|
|
583
|
+
after: async (session) => {
|
|
584
|
+
funnel?.recordSignIn();
|
|
585
|
+
recordCurrentManagedAuthSession(session.id);
|
|
586
|
+
if (!shouldDiscardCurrentManagedAuthProviderSession()) return;
|
|
587
|
+
await db.execute(sql`delete from auth_sessions where id = ${session.id}`);
|
|
588
|
+
},
|
|
238
589
|
},
|
|
239
590
|
},
|
|
240
591
|
user: {
|
|
241
592
|
create: {
|
|
242
|
-
|
|
593
|
+
before: async (user) => {
|
|
594
|
+
const admission = managedAuthUserCreateAdmission(
|
|
595
|
+
settings,
|
|
596
|
+
user,
|
|
597
|
+
currentManagedAuthProviderId(),
|
|
598
|
+
);
|
|
599
|
+
if (admission === false) return false;
|
|
600
|
+
if (!(await newSignupsGate.signupsOpen())) throw new ManagedAuthNewSignupsPausedError();
|
|
601
|
+
return admission;
|
|
602
|
+
},
|
|
603
|
+
after: async (user, context) => {
|
|
604
|
+
await funnel?.recordSignUp(context);
|
|
605
|
+
if (!user.emailVerified) return;
|
|
243
606
|
await ensureManagedAccessForUser(db, {
|
|
244
607
|
userId: user.id,
|
|
245
608
|
email: user.email,
|
|
246
609
|
name: user.name,
|
|
610
|
+
emailVerified: true,
|
|
611
|
+
provisionFallbackOrganization: false,
|
|
247
612
|
});
|
|
248
613
|
},
|
|
249
614
|
},
|
|
250
615
|
},
|
|
251
616
|
},
|
|
252
617
|
}) as ManagedAuth;
|
|
618
|
+
configureManagedUserAdmission(auth, settings.allowedUserEmails);
|
|
619
|
+
return auth;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export type ManagedAuthOAuthAttempt = {
|
|
623
|
+
transactionId: string;
|
|
624
|
+
provider: "google" | "github";
|
|
625
|
+
authorityHash: string;
|
|
626
|
+
transactionSecretHash: string;
|
|
627
|
+
expectedGeneration: string;
|
|
628
|
+
expectedActorEpoch: string;
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Resolve OpenGeni's server-only login transaction proof from Better Auth's
|
|
633
|
+
* database-backed OAuth state before the provider callback consumes it.
|
|
634
|
+
*/
|
|
635
|
+
export async function resolveManagedAuthOAuthAttempt(
|
|
636
|
+
auth: ManagedAuth,
|
|
637
|
+
request: Request,
|
|
638
|
+
provider: "google" | "github",
|
|
639
|
+
publicBaseUrl: string,
|
|
640
|
+
): Promise<ManagedAuthOAuthAttempt | null> {
|
|
641
|
+
const expectedOrigin = canonicalPublicOrigin(publicBaseUrl);
|
|
642
|
+
if (!expectedOrigin) return null;
|
|
643
|
+
const state = new URL(request.url).searchParams.get("state");
|
|
644
|
+
if (!state) return null;
|
|
645
|
+
const verification = await (await auth.$context).internalAdapter.findVerificationValue(state);
|
|
646
|
+
if (!verification?.value) return null;
|
|
647
|
+
let parsed: unknown;
|
|
648
|
+
try {
|
|
649
|
+
parsed = JSON.parse(verification.value);
|
|
650
|
+
} catch {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
654
|
+
const stateData = parsed as Record<string, unknown>;
|
|
655
|
+
const proof = stateData.opengeniManagedAuth;
|
|
656
|
+
if (!proof || typeof proof !== "object") return null;
|
|
657
|
+
const value = proof as Record<string, unknown>;
|
|
658
|
+
if (
|
|
659
|
+
value.version !== 1 ||
|
|
660
|
+
value.provider !== provider ||
|
|
661
|
+
typeof value.transactionId !== "string" ||
|
|
662
|
+
typeof value.authorityHash !== "string" ||
|
|
663
|
+
typeof value.transactionSecretHash !== "string" ||
|
|
664
|
+
typeof value.expectedGeneration !== "string" ||
|
|
665
|
+
typeof value.expectedActorEpoch !== "string" ||
|
|
666
|
+
!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
|
|
667
|
+
value.transactionId,
|
|
668
|
+
) ||
|
|
669
|
+
!/^[0-9a-f]{64}$/u.test(value.authorityHash) ||
|
|
670
|
+
!/^[0-9a-f]{64}$/u.test(value.transactionSecretHash) ||
|
|
671
|
+
!/^[1-9][0-9]*$/u.test(value.expectedGeneration) ||
|
|
672
|
+
!/^[1-9][0-9]*$/u.test(value.expectedActorEpoch)
|
|
673
|
+
) {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
const callbackURL = typeof stateData.callbackURL === "string" ? stateData.callbackURL : null;
|
|
677
|
+
const errorURL = typeof stateData.errorURL === "string" ? stateData.errorURL : null;
|
|
678
|
+
if (
|
|
679
|
+
!callbackURL ||
|
|
680
|
+
!errorURL ||
|
|
681
|
+
!managedAuthOAuthReturnMatches(callbackURL, expectedOrigin, value.transactionId, "complete") ||
|
|
682
|
+
!managedAuthOAuthReturnMatches(errorURL, expectedOrigin, value.transactionId, "error")
|
|
683
|
+
) {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
return {
|
|
687
|
+
transactionId: value.transactionId,
|
|
688
|
+
provider,
|
|
689
|
+
authorityHash: value.authorityHash,
|
|
690
|
+
transactionSecretHash: value.transactionSecretHash,
|
|
691
|
+
expectedGeneration: value.expectedGeneration,
|
|
692
|
+
expectedActorEpoch: value.expectedActorEpoch,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export async function isolatedManagedAuthOAuthCallbackRequest(
|
|
697
|
+
auth: ManagedAuth,
|
|
698
|
+
request: Request,
|
|
699
|
+
): Promise<{ request: Request; stateCookieName: string }> {
|
|
700
|
+
const context = await auth.$context;
|
|
701
|
+
const stateCookieName = context.createAuthCookie("state").name;
|
|
702
|
+
const headers = new Headers(request.headers);
|
|
703
|
+
const stateCookie = cookiePair(headers.get("cookie"), stateCookieName);
|
|
704
|
+
if (stateCookie) headers.set("cookie", stateCookie);
|
|
705
|
+
else headers.delete("cookie");
|
|
706
|
+
headers.delete("authorization");
|
|
707
|
+
headers.delete("x-forwarded-user");
|
|
708
|
+
return { request: new Request(request, { headers }), stateCookieName };
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export function managedAuthOAuthReturnMatches(
|
|
712
|
+
raw: string,
|
|
713
|
+
expectedOrigin: string,
|
|
714
|
+
transactionId: string,
|
|
715
|
+
outcome: "complete" | "error",
|
|
716
|
+
): boolean {
|
|
717
|
+
try {
|
|
718
|
+
const url = new URL(raw);
|
|
719
|
+
return (
|
|
720
|
+
url.origin === expectedOrigin &&
|
|
721
|
+
url.pathname === "/account-auth" &&
|
|
722
|
+
url.searchParams.size === 2 &&
|
|
723
|
+
url.searchParams.get("transaction") === transactionId &&
|
|
724
|
+
url.searchParams.get("social") === outcome &&
|
|
725
|
+
!url.hash
|
|
726
|
+
);
|
|
727
|
+
} catch {
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function cookiePair(header: string | null, name: string): string | null {
|
|
733
|
+
for (const part of header?.split(";") ?? []) {
|
|
734
|
+
const separator = part.indexOf("=");
|
|
735
|
+
if (separator < 0 || part.slice(0, separator).trim() !== name) continue;
|
|
736
|
+
return `${name}=${part.slice(separator + 1).trim()}`;
|
|
737
|
+
}
|
|
738
|
+
return null;
|
|
253
739
|
}
|
|
254
740
|
|
|
255
741
|
function betterAuthBaseUrl(settings: Settings) {
|
|
@@ -275,35 +761,25 @@ function betterAuthTrustedOrigins(settings: Settings): string[] {
|
|
|
275
761
|
return [...origins];
|
|
276
762
|
}
|
|
277
763
|
|
|
278
|
-
async function
|
|
279
|
-
|
|
280
|
-
input:
|
|
281
|
-
to: string;
|
|
282
|
-
subject: string;
|
|
283
|
-
text: string;
|
|
284
|
-
html: string;
|
|
285
|
-
},
|
|
764
|
+
export async function sendManagedAuthEmail(
|
|
765
|
+
transport: ManagedEmailTransport,
|
|
766
|
+
input: Omit<ManagedEmailMessage, "from">,
|
|
286
767
|
): Promise<void> {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
html: input.html,
|
|
303
|
-
});
|
|
304
|
-
if (result.error) {
|
|
305
|
-
throw new Error(result.error.message);
|
|
306
|
-
}
|
|
768
|
+
const result = await transport.send({ ...input, from: transport.sender });
|
|
769
|
+
if (result.status !== "sent") throw new Error(`managed email ${result.status}`);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Verification can sign the clicker in (autoSignInAfterVerification), so an
|
|
773
|
+
// unsolicited verification email must say plainly that it can be ignored.
|
|
774
|
+
function emailVerificationMessage(to: string, url: string): Omit<ManagedEmailMessage, "from"> {
|
|
775
|
+
const ignore = "If you did not create an Opengeni account, ignore this email.";
|
|
776
|
+
return {
|
|
777
|
+
kind: "email_verification",
|
|
778
|
+
to,
|
|
779
|
+
subject: "Verify your Opengeni email",
|
|
780
|
+
text: `Verify your Opengeni email: ${url}\n\n${ignore}`,
|
|
781
|
+
html: `<p>Verify your Opengeni email:</p><p><a href="${escapeHtml(url)}">Verify email</a></p><p>${ignore}</p>`,
|
|
782
|
+
};
|
|
307
783
|
}
|
|
308
784
|
|
|
309
785
|
async function verificationUrl(settings: Settings, email: string): Promise<string> {
|