skybridge 0.0.0-dev.fbea14c → 0.0.0-dev.fbefbcd
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 +154 -0
- package/bin/run.js +0 -4
- package/dist/cli/build-helpers.d.ts +11 -0
- package/dist/cli/build-helpers.js +107 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.js +130 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/build-steps.d.ts +2 -0
- package/dist/cli/build-steps.js +73 -0
- package/dist/cli/build-steps.js.map +1 -0
- package/dist/cli/build-steps.test.js +52 -0
- package/dist/cli/build-steps.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +18 -0
- package/dist/cli/detect-port.js +50 -0
- package/dist/cli/detect-port.js.map +1 -0
- package/dist/cli/header.d.ts +1 -1
- package/dist/cli/header.js +1 -1
- package/dist/cli/header.js.map +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +5 -0
- package/dist/cli/resolve-skybridge-config.js +18 -0
- package/dist/cli/resolve-skybridge-config.js.map +1 -0
- package/dist/cli/run-command.js.map +1 -1
- package/dist/cli/run-plain.d.ts +18 -0
- package/dist/cli/run-plain.js +89 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/telemetry.d.ts +7 -0
- package/dist/cli/telemetry.js +123 -0
- package/dist/cli/telemetry.js.map +1 -0
- package/dist/cli/tunnel-control-server.d.ts +9 -0
- package/dist/cli/tunnel-control-server.js +31 -0
- package/dist/cli/tunnel-control-server.js.map +1 -0
- package/dist/cli/tunnel-control-server.test.d.ts +1 -0
- package/dist/cli/tunnel-control-server.test.js +39 -0
- package/dist/cli/tunnel-control-server.test.js.map +1 -0
- package/dist/cli/tunnel-handler.d.ts +3 -0
- package/dist/cli/tunnel-handler.js +48 -0
- package/dist/cli/tunnel-handler.js.map +1 -0
- package/dist/cli/tunnel-handler.test.d.ts +1 -0
- package/dist/cli/tunnel-handler.test.js +105 -0
- package/dist/cli/tunnel-handler.test.js.map +1 -0
- package/dist/cli/tunnel.d.ts +57 -0
- package/dist/cli/tunnel.js +154 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/tunnel.test.d.ts +1 -0
- package/dist/cli/tunnel.test.js +190 -0
- package/dist/cli/tunnel.test.js.map +1 -0
- package/dist/cli/types.d.ts +5 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +3 -2
- package/dist/cli/use-execute-steps.js +6 -1
- package/dist/cli/use-execute-steps.js.map +1 -1
- package/dist/cli/use-messages.d.ts +3 -0
- package/dist/cli/use-messages.js +11 -0
- package/dist/cli/use-messages.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +16 -0
- package/dist/cli/use-nodemon.js +84 -0
- package/dist/cli/use-nodemon.js.map +1 -0
- package/dist/cli/use-open-browser.d.ts +1 -0
- package/dist/cli/use-open-browser.js +44 -0
- package/dist/cli/use-open-browser.js.map +1 -0
- package/dist/cli/use-tunnel.d.ts +14 -0
- package/dist/cli/use-tunnel.js +131 -0
- package/dist/cli/use-tunnel.js.map +1 -0
- package/dist/cli/use-typescript-check.d.ts +15 -0
- package/dist/cli/use-typescript-check.js +97 -0
- package/dist/cli/use-typescript-check.js.map +1 -0
- package/dist/commands/build.d.ts +0 -3
- package/dist/commands/build.js +3 -16
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +7 -1
- package/dist/commands/dev.js +117 -9
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.d.ts +3 -1
- package/dist/commands/start.js +37 -15
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/telemetry/disable.d.ts +5 -0
- package/dist/commands/telemetry/disable.js +14 -0
- package/dist/commands/telemetry/disable.js.map +1 -0
- package/dist/commands/telemetry/enable.d.ts +5 -0
- package/dist/commands/telemetry/enable.js +14 -0
- package/dist/commands/telemetry/enable.js.map +1 -0
- package/dist/commands/telemetry/status.d.ts +5 -0
- package/dist/commands/telemetry/status.js +14 -0
- package/dist/commands/telemetry/status.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.d.ts +11 -0
- package/dist/server/asset-base-url-transform-plugin.js +48 -0
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.js +134 -0
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
- package/dist/server/auth/discovery.d.ts +32 -0
- package/dist/server/auth/discovery.js +56 -0
- package/dist/server/auth/discovery.js.map +1 -0
- package/dist/server/auth/discovery.test.d.ts +1 -0
- package/dist/server/auth/discovery.test.js +93 -0
- package/dist/server/auth/discovery.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +18 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/providers/auth0.d.ts +18 -0
- package/dist/server/auth/providers/auth0.js +31 -0
- package/dist/server/auth/providers/auth0.js.map +1 -0
- package/dist/server/auth/providers/auth0.test.d.ts +1 -0
- package/dist/server/auth/providers/auth0.test.js +48 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/authplane.d.ts +47 -0
- package/dist/server/auth/providers/authplane.js +57 -0
- package/dist/server/auth/providers/authplane.js.map +1 -0
- package/dist/server/auth/providers/authplane.test.d.ts +1 -0
- package/dist/server/auth/providers/authplane.test.js +138 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +14 -0
- package/dist/server/auth/providers/clerk.js +16 -0
- package/dist/server/auth/providers/clerk.js.map +1 -0
- package/dist/server/auth/providers/clerk.test.d.ts +1 -0
- package/dist/server/auth/providers/clerk.test.js +28 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +32 -0
- package/dist/server/auth/providers/custom.js +49 -0
- package/dist/server/auth/providers/custom.js.map +1 -0
- package/dist/server/auth/providers/custom.test.d.ts +1 -0
- package/dist/server/auth/providers/custom.test.js +156 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +15 -0
- package/dist/server/auth/providers/descope.js +44 -0
- package/dist/server/auth/providers/descope.js.map +1 -0
- package/dist/server/auth/providers/descope.test.d.ts +1 -0
- package/dist/server/auth/providers/descope.test.js +60 -0
- package/dist/server/auth/providers/descope.test.js.map +1 -0
- package/dist/server/auth/providers/shared.d.ts +2 -0
- package/dist/server/auth/providers/shared.js +6 -0
- package/dist/server/auth/providers/shared.js.map +1 -0
- package/dist/server/auth/providers/shared.test.d.ts +1 -0
- package/dist/server/auth/providers/shared.test.js +10 -0
- package/dist/server/auth/providers/shared.test.js.map +1 -0
- package/dist/server/auth/providers/stytch.d.ts +12 -0
- package/dist/server/auth/providers/stytch.js +13 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +11 -0
- package/dist/server/auth/providers/workos.js +12 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/security-schemes.d.ts +23 -0
- package/dist/server/auth/security-schemes.js +64 -0
- package/dist/server/auth/security-schemes.js.map +1 -0
- package/dist/server/auth/security-schemes.test.d.ts +1 -0
- package/dist/server/auth/security-schemes.test.js +99 -0
- package/dist/server/auth/security-schemes.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +6 -0
- package/dist/server/auth/setup.js +92 -0
- package/dist/server/auth/setup.js.map +1 -0
- package/dist/server/auth/setup.test.d.ts +1 -0
- package/dist/server/auth/setup.test.js +474 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +12 -0
- package/dist/server/auth/verify.js +38 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +100 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +67 -0
- package/dist/server/content-helpers.js +79 -0
- package/dist/server/content-helpers.js.map +1 -0
- package/dist/server/content-helpers.test.d.ts +1 -0
- package/dist/server/content-helpers.test.js +70 -0
- package/dist/server/content-helpers.test.js.map +1 -0
- package/dist/server/express.d.ts +11 -0
- package/dist/server/express.js +101 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.test.d.ts +1 -0
- package/dist/server/express.test.js +491 -0
- package/dist/server/express.test.js.map +1 -0
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/host.d.ts +2 -0
- package/dist/server/host.js +11 -0
- package/dist/server/host.js.map +1 -0
- package/dist/server/host.test.d.ts +1 -0
- package/dist/server/host.test.js +13 -0
- package/dist/server/host.test.js.map +1 -0
- package/dist/server/index.d.ts +16 -3
- package/dist/server/index.js +13 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/inferUtilityTypes.d.ts +6 -6
- package/dist/server/inferUtilityTypes.js.map +1 -1
- package/dist/server/metric.d.ts +14 -0
- package/dist/server/metric.js +62 -0
- package/dist/server/metric.js.map +1 -0
- package/dist/server/middleware.d.ts +137 -0
- package/dist/server/middleware.js +93 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/middleware.test-d.d.ts +1 -0
- package/dist/server/middleware.test-d.js +75 -0
- package/dist/server/middleware.test-d.js.map +1 -0
- package/dist/server/middleware.test.d.ts +1 -0
- package/dist/server/middleware.test.js +493 -0
- package/dist/server/middleware.test.js.map +1 -0
- package/dist/server/requestOrigin.d.ts +7 -0
- package/dist/server/requestOrigin.js +25 -0
- package/dist/server/requestOrigin.js.map +1 -0
- package/dist/server/server.d.ts +414 -58
- package/dist/server/server.js +689 -96
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.d.ts +1 -0
- package/dist/server/skills-integration.test.js +63 -0
- package/dist/server/skills-integration.test.js.map +1 -0
- package/dist/server/skills.d.ts +23 -0
- package/dist/server/skills.js +178 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/skills.test.d.ts +1 -0
- package/dist/server/skills.test.js +210 -0
- package/dist/server/skills.test.js.map +1 -0
- package/dist/server/templateHelper.d.ts +5 -7
- package/dist/server/templateHelper.js +3 -22
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.d.ts +4 -0
- package/dist/server/templates.generated.js +47 -0
- package/dist/server/templates.generated.js.map +1 -0
- package/dist/server/tunnel-proxy-router.d.ts +7 -0
- package/dist/server/tunnel-proxy-router.js +110 -0
- package/dist/server/tunnel-proxy-router.js.map +1 -0
- package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
- package/dist/server/tunnel-proxy-router.test.js +229 -0
- package/dist/server/tunnel-proxy-router.test.js.map +1 -0
- package/dist/server/view-name.test-d.d.ts +1 -0
- package/dist/server/view-name.test-d.js +8 -0
- package/dist/server/view-name.test-d.js.map +1 -0
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +171 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/server/viewsDevServer.d.ts +14 -0
- package/dist/server/viewsDevServer.js +48 -0
- package/dist/server/viewsDevServer.js.map +1 -0
- package/dist/test/utils.d.ts +13 -21
- package/dist/test/utils.js +42 -37
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.d.ts +1 -0
- package/dist/test/view.test.js +536 -0
- package/dist/test/view.test.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/web/bridges/adaptor.d.ts +51 -0
- package/dist/web/bridges/adaptor.js +341 -0
- package/dist/web/bridges/adaptor.js.map +1 -0
- package/dist/web/bridges/adaptor.test.d.ts +1 -0
- package/dist/web/bridges/adaptor.test.js +224 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +7 -2
- package/dist/web/bridges/apps-sdk/bridge.js +15 -3
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +1 -2
- package/dist/web/bridges/apps-sdk/index.js +0 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +39 -27
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +7 -0
- package/dist/web/bridges/get-adaptor.js +9 -7
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
- package/dist/web/bridges/get-adaptor.test.js +32 -0
- package/dist/web/bridges/get-adaptor.test.js.map +1 -0
- package/dist/web/bridges/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +27 -31
- package/dist/web/bridges/mcp-app/bridge.js +111 -198
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +0 -1
- package/dist/web/bridges/mcp-app/index.js +0 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +2 -0
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +17 -3
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +14 -2
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -41
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +143 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +153 -13
- package/dist/web/bridges/types.js +14 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/types.test.d.ts +1 -0
- package/dist/web/bridges/types.test.js +19 -0
- package/dist/web/bridges/types.test.js.map +1 -0
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/components/modal-provider.d.ts +4 -0
- package/dist/web/components/modal-provider.js +46 -0
- package/dist/web/components/modal-provider.js.map +1 -0
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +43 -3
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +31 -25
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +35 -2
- package/dist/web/data-llm.js +31 -3
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +50 -35
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +22 -18
- package/dist/web/generate-helpers.js +22 -18
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +30 -28
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/generate-helpers.test.js.map +1 -1
- package/dist/web/helpers/state.d.ts +2 -2
- package/dist/web/helpers/state.js +11 -11
- package/dist/web/helpers/state.js.map +1 -1
- package/dist/web/helpers/state.test.js +9 -9
- package/dist/web/helpers/state.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +8 -2
- package/dist/web/hooks/index.js +7 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +7 -2
- package/dist/web/hooks/test/utils.js +18 -3
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +62 -27
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +23 -3
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test-d.js +8 -0
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
- package/dist/web/hooks/use-display-mode.test.js +56 -20
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +5 -0
- package/dist/web/hooks/use-download.js +8 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +103 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +35 -6
- package/dist/web/hooks/use-files.js +37 -2
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +36 -3
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-host-info.d.ts +22 -0
- package/dist/web/hooks/use-host-info.js +30 -0
- package/dist/web/hooks/use-host-info.js.map +1 -0
- package/dist/web/hooks/use-host-info.test.d.ts +1 -0
- package/dist/web/hooks/use-host-info.test.js +59 -0
- package/dist/web/hooks/use-host-info.test.js.map +1 -0
- package/dist/web/hooks/use-layout.d.ts +3 -1
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +62 -29
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +20 -1
- package/dist/web/hooks/use-open-external.js +17 -1
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +38 -13
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +47 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +18 -3
- package/dist/web/hooks/use-request-modal.js +25 -8
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +15 -1
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +47 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -1
- package/dist/web/hooks/use-send-follow-up-message.js +19 -2
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +18 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +25 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.js +63 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
- package/dist/web/hooks/use-tool-info.d.ts +53 -2
- package/dist/web/hooks/use-tool-info.js +30 -7
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -29
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +43 -33
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +20 -2
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +101 -27
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +25 -0
- package/dist/web/hooks/use-view-state.js +42 -0
- package/dist/web/hooks/use-view-state.js.map +1 -0
- package/dist/web/hooks/use-view-state.test.d.ts +1 -0
- package/dist/web/hooks/use-view-state.test.js +212 -0
- package/dist/web/hooks/use-view-state.test.js.map +1 -0
- package/dist/web/index.d.ts +1 -2
- package/dist/web/index.js +1 -2
- package/dist/web/index.js.map +1 -1
- package/dist/web/mount-view.d.ts +20 -0
- package/dist/web/mount-view.js +46 -0
- package/dist/web/mount-view.js.map +1 -0
- package/dist/web/plugin/data-llm.test.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +38 -1
- package/dist/web/plugin/plugin.js +168 -18
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/plugin/scan-views.d.ts +16 -0
- package/dist/web/plugin/scan-views.js +88 -0
- package/dist/web/plugin/scan-views.js.map +1 -0
- package/dist/web/plugin/scan-views.test.d.ts +1 -0
- package/dist/web/plugin/scan-views.test.js +99 -0
- package/dist/web/plugin/scan-views.test.js.map +1 -0
- package/dist/web/plugin/transform-data-llm.js +1 -1
- package/dist/web/plugin/transform-data-llm.js.map +1 -1
- package/dist/web/plugin/transform-data-llm.test.js.map +1 -1
- package/dist/web/plugin/validate-view.d.ts +1 -0
- package/dist/web/plugin/validate-view.js +9 -0
- package/dist/web/plugin/validate-view.js.map +1 -0
- package/dist/web/plugin/validate-view.test.d.ts +1 -0
- package/dist/web/plugin/validate-view.test.js +24 -0
- package/dist/web/plugin/validate-view.test.js.map +1 -0
- package/dist/web/proxy.js +0 -1
- package/dist/web/proxy.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +59 -30
- package/tsconfig.base.json +33 -0
- package/dist/server/templates/development.hbs +0 -66
- package/dist/server/templates/production.hbs +0 -7
- package/dist/server/widgetsDevServer.d.ts +0 -12
- package/dist/server/widgetsDevServer.js +0 -47
- package/dist/server/widgetsDevServer.js.map +0 -1
- package/dist/test/widget.test.js +0 -255
- package/dist/test/widget.test.js.map +0 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -14
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -39
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -19
- package/dist/web/bridges/mcp-app/adaptor.js +0 -145
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
- package/dist/web/hooks/use-widget-state.d.ts +0 -4
- package/dist/web/hooks/use-widget-state.js +0 -32
- package/dist/web/hooks/use-widget-state.js.map +0 -1
- package/dist/web/hooks/use-widget-state.test.js +0 -61
- package/dist/web/hooks/use-widget-state.test.js.map +0 -1
- package/dist/web/mount-widget.d.ts +0 -1
- package/dist/web/mount-widget.js +0 -19
- package/dist/web/mount-widget.js.map +0 -1
- /package/dist/{test/widget.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
- /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/build-steps.test.d.ts} +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
/**
|
|
3
|
+
* Rejects anything that cannot serve as an OAuth identifier: RFC 8707 §2
|
|
4
|
+
* requires an absolute URI and forbids a fragment, and the discovery and
|
|
5
|
+
* protected-resource metadata URLs are both built from the scheme and host.
|
|
6
|
+
*/
|
|
7
|
+
function parseIdentifier(value, option) {
|
|
8
|
+
let parsed;
|
|
9
|
+
try {
|
|
10
|
+
parsed = new URL(value);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error(`authplaneProvider: \`${option}\` must be an absolute URL, got ${JSON.stringify(value)}`);
|
|
14
|
+
}
|
|
15
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
16
|
+
throw new Error(`authplaneProvider: \`${option}\` must use the http or https scheme, got ${JSON.stringify(value)}`);
|
|
17
|
+
}
|
|
18
|
+
if (!parsed.host) {
|
|
19
|
+
throw new Error(`authplaneProvider: \`${option}\` must include a host, got ${JSON.stringify(value)}`);
|
|
20
|
+
}
|
|
21
|
+
if (parsed.hash) {
|
|
22
|
+
throw new Error(`authplaneProvider: \`${option}\` must not include a fragment, got ${JSON.stringify(value)}`);
|
|
23
|
+
}
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* OAuth provider for Authplane. `issuer` is the authorization server's issuer
|
|
28
|
+
* identifier and `resource` is this server's resource identifier, which also
|
|
29
|
+
* supplies the expected token audience.
|
|
30
|
+
*
|
|
31
|
+
* Dynamic Client Registration is supported natively, so no registration proxy
|
|
32
|
+
* is needed: clients register with Authplane directly and this server stays out
|
|
33
|
+
* of the authorization path. Pass `serverUrl` to advertise this server as the
|
|
34
|
+
* authorization server instead (see {@link customProvider}).
|
|
35
|
+
*/
|
|
36
|
+
export function authplaneProvider(opts) {
|
|
37
|
+
const { issuer, resource, audience, ...rest } = opts;
|
|
38
|
+
parseIdentifier(issuer, "issuer");
|
|
39
|
+
const parsedResource = parseIdentifier(resource, "resource");
|
|
40
|
+
// The advertised resource is the URL-normalised form of this value. Where
|
|
41
|
+
// normalisation would change the string — a bare origin gaining a root path,
|
|
42
|
+
// an uppercase host, an explicit default port — the configured and advertised
|
|
43
|
+
// identifiers would differ, and the audience check compares them exactly.
|
|
44
|
+
// Require the advertised form up front so the two always match.
|
|
45
|
+
if (parsedResource.href !== resource) {
|
|
46
|
+
throw new Error(`authplaneProvider: \`resource\` must be given in the form it will be advertised. ` +
|
|
47
|
+
`${JSON.stringify(resource)} is advertised as ${JSON.stringify(parsedResource.href)}. ` +
|
|
48
|
+
`Use ${JSON.stringify(parsedResource.href)}, or a path-qualified URL such as "https://acme.example.com/mcp", and register the same value in Authplane.`);
|
|
49
|
+
}
|
|
50
|
+
return customProvider({
|
|
51
|
+
issuer,
|
|
52
|
+
audience: audience ?? resource,
|
|
53
|
+
baseUrl: resource,
|
|
54
|
+
...rest,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=authplane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authplane.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/authplane.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AAqCzE;;;;GAIG;AACH,SAAS,eAAe,CAAC,KAAa,EAAE,MAAc;IACpD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,wBAAwB,MAAM,mCAAmC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACzF,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,wBAAwB,MAAM,6CAA6C,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACnG,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,wBAAwB,MAAM,+BAA+B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACrF,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,wBAAwB,MAAM,uCAAuC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAC7F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAA8B;IAE9B,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAErD,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClC,MAAM,cAAc,GAAG,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAE7D,0EAA0E;IAC1E,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,gEAAgE;IAChE,IAAI,cAAc,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,mFAAmF;YACjF,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,qBAAqB,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI;YACvF,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,6GAA6G,CAC1J,CAAC;IACJ,CAAC;IAED,OAAO,cAAc,CAAC;QACpB,MAAM;QACN,QAAQ,EAAE,QAAQ,IAAI,QAAQ;QAC9B,OAAO,EAAE,QAAQ;QACjB,GAAG,IAAI;KACR,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\n\n/** Options accepted by {@link authplaneProvider}. */\nexport type AuthplaneProviderOptions = {\n /**\n * The authorization server's issuer identifier (RFC 8414 §2) — your\n * Authplane deployment, e.g. `https://auth.acme.com` (or\n * `http://localhost:9000` in local development).\n */\n issuer: string;\n /**\n * This server's resource identifier (RFC 9728 §1.2): the public URL clients\n * reach, advertised as the `resource` field of its protected-resource\n * metadata. Required, unlike the other providers.\n *\n * Authplane binds the access token's `aud` to the RFC 8707 `resource`\n * parameter the client sends, and the client takes that value from the\n * advertised metadata. Setting it explicitly gives the deployment one fixed\n * identifier, which is what the audience is checked against.\n *\n * Resource identifiers are compared by exact string match, so give it in the\n * form it will be advertised and register that same string in Authplane. RFC\n * 8707 §2 asks for the most specific URI available, e.g.\n * `https://acme.example.com/mcp`.\n */\n resource: string;\n /**\n * Expected token `aud`. Defaults to `resource`.\n *\n * RFC 8707 §2 lets an authorization server use the resource identifier\n * verbatim as the audience or map it to another value; set this only for a\n * resource configured in Authplane with such an override, and pass that\n * value verbatim.\n */\n audience?: string;\n} & Omit<CustomProviderOptions, \"issuer\" | \"audience\" | \"baseUrl\">;\n\n/**\n * Rejects anything that cannot serve as an OAuth identifier: RFC 8707 §2\n * requires an absolute URI and forbids a fragment, and the discovery and\n * protected-resource metadata URLs are both built from the scheme and host.\n */\nfunction parseIdentifier(value: string, option: string): URL {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n throw new Error(\n `authplaneProvider: \\`${option}\\` must be an absolute URL, got ${JSON.stringify(value)}`,\n );\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n throw new Error(\n `authplaneProvider: \\`${option}\\` must use the http or https scheme, got ${JSON.stringify(value)}`,\n );\n }\n if (!parsed.host) {\n throw new Error(\n `authplaneProvider: \\`${option}\\` must include a host, got ${JSON.stringify(value)}`,\n );\n }\n if (parsed.hash) {\n throw new Error(\n `authplaneProvider: \\`${option}\\` must not include a fragment, got ${JSON.stringify(value)}`,\n );\n }\n return parsed;\n}\n\n/**\n * OAuth provider for Authplane. `issuer` is the authorization server's issuer\n * identifier and `resource` is this server's resource identifier, which also\n * supplies the expected token audience.\n *\n * Dynamic Client Registration is supported natively, so no registration proxy\n * is needed: clients register with Authplane directly and this server stays out\n * of the authorization path. Pass `serverUrl` to advertise this server as the\n * authorization server instead (see {@link customProvider}).\n */\nexport function authplaneProvider(\n opts: AuthplaneProviderOptions,\n): Promise<OAuthConfig> {\n const { issuer, resource, audience, ...rest } = opts;\n\n parseIdentifier(issuer, \"issuer\");\n const parsedResource = parseIdentifier(resource, \"resource\");\n\n // The advertised resource is the URL-normalised form of this value. Where\n // normalisation would change the string — a bare origin gaining a root path,\n // an uppercase host, an explicit default port — the configured and advertised\n // identifiers would differ, and the audience check compares them exactly.\n // Require the advertised form up front so the two always match.\n if (parsedResource.href !== resource) {\n throw new Error(\n `authplaneProvider: \\`resource\\` must be given in the form it will be advertised. ` +\n `${JSON.stringify(resource)} is advertised as ${JSON.stringify(parsedResource.href)}. ` +\n `Use ${JSON.stringify(parsedResource.href)}, or a path-qualified URL such as \"https://acme.example.com/mcp\", and register the same value in Authplane.`,\n );\n }\n\n return customProvider({\n issuer,\n audience: audience ?? resource,\n baseUrl: resource,\n ...rest,\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { authplaneProvider } from "./authplane.js";
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
const ISSUER = "https://auth.acme.com";
|
|
6
|
+
function discoveryDoc(issuer, scopes) {
|
|
7
|
+
return {
|
|
8
|
+
issuer,
|
|
9
|
+
authorization_endpoint: `${issuer}/authorize`,
|
|
10
|
+
token_endpoint: `${issuer}/token`,
|
|
11
|
+
registration_endpoint: `${issuer}/register`,
|
|
12
|
+
response_types_supported: ["code"],
|
|
13
|
+
scopes_supported: scopes,
|
|
14
|
+
jwks_uri: `${issuer}/.well-known/jwks.json`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function mockDiscovery(docs) {
|
|
18
|
+
return vi
|
|
19
|
+
.spyOn(globalThis, "fetch")
|
|
20
|
+
.mockImplementation(async (input) => {
|
|
21
|
+
const url = String(input);
|
|
22
|
+
const entry = Object.entries(docs).find(([issuer]) => url === `${issuer}/.well-known/openid-configuration`);
|
|
23
|
+
if (entry === undefined) {
|
|
24
|
+
return new Response(null, { status: 404 });
|
|
25
|
+
}
|
|
26
|
+
return new Response(JSON.stringify(discoveryDoc(...entry)), {
|
|
27
|
+
headers: { "content-type": "application/json" },
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
describe("authplaneProvider", () => {
|
|
32
|
+
it("derives the audience from the resource the server advertises", async () => {
|
|
33
|
+
mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
34
|
+
const config = await authplaneProvider({
|
|
35
|
+
issuer: ISSUER,
|
|
36
|
+
resource: "https://coffee.example.com/mcp",
|
|
37
|
+
});
|
|
38
|
+
expect(config.verify.audience).toBe("https://coffee.example.com/mcp");
|
|
39
|
+
expect(config.verify.issuer).toBe(ISSUER);
|
|
40
|
+
expect(config.verify.jwksUri).toBe(`${ISSUER}/.well-known/jwks.json`);
|
|
41
|
+
expect(config.baseUrl).toBe("https://coffee.example.com/mcp");
|
|
42
|
+
});
|
|
43
|
+
it.each([
|
|
44
|
+
"https://coffee.example.com/mcp",
|
|
45
|
+
"https://coffee.example.com/v2/mcp",
|
|
46
|
+
"https://coffee.example.com/",
|
|
47
|
+
"http://localhost:3000/mcp",
|
|
48
|
+
"https://coffee.example.com:8443/mcp",
|
|
49
|
+
])("passes %s through as both resource and audience", async (resource) => {
|
|
50
|
+
mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
51
|
+
const config = await authplaneProvider({ issuer: ISSUER, resource });
|
|
52
|
+
expect(config.baseUrl).toBe(resource);
|
|
53
|
+
expect(config.verify.audience).toBe(resource);
|
|
54
|
+
});
|
|
55
|
+
// The metadata router serialises the resource identifier through `URL` before
|
|
56
|
+
// advertising it. Anything that serialisation would rewrite is refused here,
|
|
57
|
+
// so the advertised identifier is always the configured one.
|
|
58
|
+
it.each([
|
|
59
|
+
// Pathless origin — gains a root path.
|
|
60
|
+
["https://coffee.example.com", "https://coffee.example.com/"],
|
|
61
|
+
["http://localhost:3000", "http://localhost:3000/"],
|
|
62
|
+
// Uppercase host — lowercased.
|
|
63
|
+
["https://COFFEE.EXAMPLE.COM/mcp", "https://coffee.example.com/mcp"],
|
|
64
|
+
// Explicit default port — dropped.
|
|
65
|
+
["https://coffee.example.com:443/mcp", "https://coffee.example.com/mcp"],
|
|
66
|
+
])("rejects %s and names %s instead", async (resource, published) => {
|
|
67
|
+
const fetchSpy = mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
68
|
+
expect(() => authplaneProvider({ issuer: ISSUER, resource })).toThrow(`is advertised as ${JSON.stringify(published)}`);
|
|
69
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
it("lets an explicit audience override the default", async () => {
|
|
72
|
+
mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
73
|
+
const config = await authplaneProvider({
|
|
74
|
+
issuer: ISSUER,
|
|
75
|
+
resource: "https://coffee.example.com/mcp",
|
|
76
|
+
audience: "urn:acme:coffee",
|
|
77
|
+
});
|
|
78
|
+
expect(config.verify.audience).toBe("urn:acme:coffee");
|
|
79
|
+
});
|
|
80
|
+
it.each([
|
|
81
|
+
["coffee.example.com/mcp", /must be an absolute URL/],
|
|
82
|
+
["ftp://coffee.example.com/mcp", /must use the http or https scheme/],
|
|
83
|
+
["https://coffee.example.com/mcp#frag", /must not include a fragment/],
|
|
84
|
+
])("rejects the malformed resource %s", async (resource, message) => {
|
|
85
|
+
const fetchSpy = mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
86
|
+
expect(() => authplaneProvider({ issuer: ISSUER, resource })).toThrow(message);
|
|
87
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
it("rejects a malformed issuer before any discovery", async () => {
|
|
90
|
+
const fetchSpy = mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
91
|
+
expect(() => authplaneProvider({
|
|
92
|
+
issuer: "auth.acme.com",
|
|
93
|
+
resource: "https://coffee.example.com/mcp",
|
|
94
|
+
})).toThrow(/`issuer` must be an absolute URL/);
|
|
95
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
it("accepts an issuer carrying a trailing slash", async () => {
|
|
98
|
+
// Discovery resolves the issuer itself; the provider passes it through
|
|
99
|
+
// rather than rewriting the operator's value.
|
|
100
|
+
mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
101
|
+
const config = await authplaneProvider({
|
|
102
|
+
issuer: `${ISSUER}/`,
|
|
103
|
+
resource: "https://coffee.example.com/mcp",
|
|
104
|
+
});
|
|
105
|
+
expect(config.verify.issuer).toBe(ISSUER);
|
|
106
|
+
});
|
|
107
|
+
it("accepts a local http issuer for development", async () => {
|
|
108
|
+
const local = "http://localhost:9000";
|
|
109
|
+
mockDiscovery({ [local]: ["checkout"] });
|
|
110
|
+
const config = await authplaneProvider({
|
|
111
|
+
issuer: local,
|
|
112
|
+
resource: "http://localhost:3000/mcp",
|
|
113
|
+
});
|
|
114
|
+
expect(config.verify.issuer).toBe(local);
|
|
115
|
+
expect(config.verify.audience).toBe("http://localhost:3000/mcp");
|
|
116
|
+
});
|
|
117
|
+
it("forwards scopes and the required-scope floor", async () => {
|
|
118
|
+
mockDiscovery({ [ISSUER]: ["checkout", "profile"] });
|
|
119
|
+
const config = await authplaneProvider({
|
|
120
|
+
issuer: ISSUER,
|
|
121
|
+
resource: "https://coffee.example.com/mcp",
|
|
122
|
+
scopes: ["checkout"],
|
|
123
|
+
requiredScopes: ["checkout"],
|
|
124
|
+
});
|
|
125
|
+
expect(config.scopesSupported).toEqual(["checkout"]);
|
|
126
|
+
expect(config.requiredScopes).toEqual(["checkout"]);
|
|
127
|
+
});
|
|
128
|
+
it("advertises the registration endpoint for dynamic client registration", async () => {
|
|
129
|
+
mockDiscovery({ [ISSUER]: ["checkout"] });
|
|
130
|
+
const config = await authplaneProvider({
|
|
131
|
+
issuer: ISSUER,
|
|
132
|
+
resource: "https://coffee.example.com/mcp",
|
|
133
|
+
});
|
|
134
|
+
expect(config.oauthMetadata.issuer).toBe(ISSUER);
|
|
135
|
+
expect(config.oauthMetadata.registration_endpoint).toBe(`${ISSUER}/register`);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
//# sourceMappingURL=authplane.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authplane.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/authplane.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { authplaneProvider } from \"./authplane.js\";\n\nafterEach(() => vi.restoreAllMocks());\n\nconst ISSUER = \"https://auth.acme.com\";\n\nfunction discoveryDoc(issuer: string, scopes: string[]) {\n return {\n issuer,\n authorization_endpoint: `${issuer}/authorize`,\n token_endpoint: `${issuer}/token`,\n registration_endpoint: `${issuer}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: scopes,\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n };\n}\n\nfunction mockDiscovery(docs: Record<string, string[]>) {\n return vi\n .spyOn(globalThis, \"fetch\")\n .mockImplementation(async (input: RequestInfo | URL) => {\n const url = String(input);\n const entry = Object.entries(docs).find(\n ([issuer]) => url === `${issuer}/.well-known/openid-configuration`,\n );\n if (entry === undefined) {\n return new Response(null, { status: 404 });\n }\n return new Response(JSON.stringify(discoveryDoc(...entry)), {\n headers: { \"content-type\": \"application/json\" },\n });\n });\n}\n\ndescribe(\"authplaneProvider\", () => {\n it(\"derives the audience from the resource the server advertises\", async () => {\n mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n const config = await authplaneProvider({\n issuer: ISSUER,\n resource: \"https://coffee.example.com/mcp\",\n });\n\n expect(config.verify.audience).toBe(\"https://coffee.example.com/mcp\");\n expect(config.verify.issuer).toBe(ISSUER);\n expect(config.verify.jwksUri).toBe(`${ISSUER}/.well-known/jwks.json`);\n expect(config.baseUrl).toBe(\"https://coffee.example.com/mcp\");\n });\n\n it.each([\n \"https://coffee.example.com/mcp\",\n \"https://coffee.example.com/v2/mcp\",\n \"https://coffee.example.com/\",\n \"http://localhost:3000/mcp\",\n \"https://coffee.example.com:8443/mcp\",\n ])(\"passes %s through as both resource and audience\", async (resource) => {\n mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n const config = await authplaneProvider({ issuer: ISSUER, resource });\n\n expect(config.baseUrl).toBe(resource);\n expect(config.verify.audience).toBe(resource);\n });\n\n // The metadata router serialises the resource identifier through `URL` before\n // advertising it. Anything that serialisation would rewrite is refused here,\n // so the advertised identifier is always the configured one.\n it.each([\n // Pathless origin — gains a root path.\n [\"https://coffee.example.com\", \"https://coffee.example.com/\"],\n [\"http://localhost:3000\", \"http://localhost:3000/\"],\n // Uppercase host — lowercased.\n [\"https://COFFEE.EXAMPLE.COM/mcp\", \"https://coffee.example.com/mcp\"],\n // Explicit default port — dropped.\n [\"https://coffee.example.com:443/mcp\", \"https://coffee.example.com/mcp\"],\n ])(\"rejects %s and names %s instead\", async (resource, published) => {\n const fetchSpy = mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n expect(() => authplaneProvider({ issuer: ISSUER, resource })).toThrow(\n `is advertised as ${JSON.stringify(published)}`,\n );\n expect(fetchSpy).not.toHaveBeenCalled();\n });\n\n it(\"lets an explicit audience override the default\", async () => {\n mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n const config = await authplaneProvider({\n issuer: ISSUER,\n resource: \"https://coffee.example.com/mcp\",\n audience: \"urn:acme:coffee\",\n });\n\n expect(config.verify.audience).toBe(\"urn:acme:coffee\");\n });\n\n it.each([\n [\"coffee.example.com/mcp\", /must be an absolute URL/],\n [\"ftp://coffee.example.com/mcp\", /must use the http or https scheme/],\n [\"https://coffee.example.com/mcp#frag\", /must not include a fragment/],\n ])(\"rejects the malformed resource %s\", async (resource, message) => {\n const fetchSpy = mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n expect(() => authplaneProvider({ issuer: ISSUER, resource })).toThrow(\n message,\n );\n expect(fetchSpy).not.toHaveBeenCalled();\n });\n\n it(\"rejects a malformed issuer before any discovery\", async () => {\n const fetchSpy = mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n expect(() =>\n authplaneProvider({\n issuer: \"auth.acme.com\",\n resource: \"https://coffee.example.com/mcp\",\n }),\n ).toThrow(/`issuer` must be an absolute URL/);\n expect(fetchSpy).not.toHaveBeenCalled();\n });\n\n it(\"accepts an issuer carrying a trailing slash\", async () => {\n // Discovery resolves the issuer itself; the provider passes it through\n // rather than rewriting the operator's value.\n mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n const config = await authplaneProvider({\n issuer: `${ISSUER}/`,\n resource: \"https://coffee.example.com/mcp\",\n });\n\n expect(config.verify.issuer).toBe(ISSUER);\n });\n\n it(\"accepts a local http issuer for development\", async () => {\n const local = \"http://localhost:9000\";\n mockDiscovery({ [local]: [\"checkout\"] });\n\n const config = await authplaneProvider({\n issuer: local,\n resource: \"http://localhost:3000/mcp\",\n });\n\n expect(config.verify.issuer).toBe(local);\n expect(config.verify.audience).toBe(\"http://localhost:3000/mcp\");\n });\n\n it(\"forwards scopes and the required-scope floor\", async () => {\n mockDiscovery({ [ISSUER]: [\"checkout\", \"profile\"] });\n\n const config = await authplaneProvider({\n issuer: ISSUER,\n resource: \"https://coffee.example.com/mcp\",\n scopes: [\"checkout\"],\n requiredScopes: [\"checkout\"],\n });\n\n expect(config.scopesSupported).toEqual([\"checkout\"]);\n expect(config.requiredScopes).toEqual([\"checkout\"]);\n });\n\n it(\"advertises the registration endpoint for dynamic client registration\", async () => {\n mockDiscovery({ [ISSUER]: [\"checkout\"] });\n\n const config = await authplaneProvider({\n issuer: ISSUER,\n resource: \"https://coffee.example.com/mcp\",\n });\n\n expect(config.oauthMetadata.issuer).toBe(ISSUER);\n expect(config.oauthMetadata.registration_endpoint).toBe(\n `${ISSUER}/register`,\n );\n });\n});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.
|
|
5
|
+
* `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic
|
|
6
|
+
* Client Registration enabled on the instance, and the OAuth application set to
|
|
7
|
+
* issue JWT access tokens (opaque tokens can't be JWKS-verified).
|
|
8
|
+
*
|
|
9
|
+
* Clerk access tokens carry no `aud` claim, so there is no `audience` option —
|
|
10
|
+
* verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function clerkProvider(opts: {
|
|
13
|
+
domain: string;
|
|
14
|
+
} & Omit<CustomProviderOptions, "issuer" | "audience">): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
import { toIssuerUrl } from "./shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.
|
|
5
|
+
* `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic
|
|
6
|
+
* Client Registration enabled on the instance, and the OAuth application set to
|
|
7
|
+
* issue JWT access tokens (opaque tokens can't be JWKS-verified).
|
|
8
|
+
*
|
|
9
|
+
* Clerk access tokens carry no `aud` claim, so there is no `audience` option —
|
|
10
|
+
* verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).
|
|
11
|
+
*/
|
|
12
|
+
export function clerkProvider(opts) {
|
|
13
|
+
const { domain, ...rest } = opts;
|
|
14
|
+
return customProvider({ issuer: toIssuerUrl(domain), ...rest });
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=clerk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/clerk.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,IAA6E;IAE7E,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACjC,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\nimport { toIssuerUrl } from \"./shared.js\";\n\n/**\n * OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.\n * `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic\n * Client Registration enabled on the instance, and the OAuth application set to\n * issue JWT access tokens (opaque tokens can't be JWKS-verified).\n *\n * Clerk access tokens carry no `aud` claim, so there is no `audience` option —\n * verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).\n */\nexport function clerkProvider(\n opts: { domain: string } & Omit<CustomProviderOptions, \"issuer\" | \"audience\">,\n): Promise<OAuthConfig> {\n const { domain, ...rest } = opts;\n return customProvider({ issuer: toIssuerUrl(domain), ...rest });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { clerkProvider } from "./clerk.js";
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
function doc(issuer) {
|
|
6
|
+
return {
|
|
7
|
+
issuer,
|
|
8
|
+
authorization_endpoint: `${issuer}/oauth/authorize`,
|
|
9
|
+
token_endpoint: `${issuer}/oauth/token`,
|
|
10
|
+
registration_endpoint: `${issuer}/oauth/register`,
|
|
11
|
+
response_types_supported: ["code"],
|
|
12
|
+
scopes_supported: ["profile", "email"],
|
|
13
|
+
jwks_uri: `${issuer}/.well-known/jwks.json`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe("clerkProvider", () => {
|
|
17
|
+
it("verifies issuer + JWKS with no audience (Clerk tokens carry no aud)", async () => {
|
|
18
|
+
const issuer = "https://acme.clerk.accounts.dev";
|
|
19
|
+
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(doc(issuer)), {
|
|
20
|
+
headers: { "content-type": "application/json" },
|
|
21
|
+
}));
|
|
22
|
+
const config = await clerkProvider({ domain: "acme.clerk.accounts.dev" });
|
|
23
|
+
expect(fetchSpy).toHaveBeenCalledWith(`${issuer}/.well-known/openid-configuration`, expect.anything());
|
|
24
|
+
expect(config.verify.issuer).toBe(issuer);
|
|
25
|
+
expect(config.verify.audience).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=clerk.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/clerk.test.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;AAEtC,SAAS,GAAG,CAAC,MAAc;IACzB,OAAO;QACL,MAAM;QACN,sBAAsB,EAAE,GAAG,MAAM,kBAAkB;QACnD,cAAc,EAAE,GAAG,MAAM,cAAc;QACvC,qBAAqB,EAAE,GAAG,MAAM,iBAAiB;QACjD,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,gBAAgB,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;QACtC,QAAQ,EAAE,GAAG,MAAM,wBAAwB;KAC5C,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAG,iCAAiC,CAAC;QACjD,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,iBAAiB,CAC9D,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE;YACxC,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC,CAAC;QAE1E,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CACnC,GAAG,MAAM,mCAAmC,EAC5C,MAAM,CAAC,QAAQ,EAAE,CAClB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// @vitest-environment node\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { clerkProvider } from \"./clerk.js\";\n\nafterEach(() => vi.restoreAllMocks());\n\nfunction doc(issuer: string) {\n return {\n issuer,\n authorization_endpoint: `${issuer}/oauth/authorize`,\n token_endpoint: `${issuer}/oauth/token`,\n registration_endpoint: `${issuer}/oauth/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"profile\", \"email\"],\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n };\n}\n\ndescribe(\"clerkProvider\", () => {\n it(\"verifies issuer + JWKS with no audience (Clerk tokens carry no aud)\", async () => {\n const issuer = \"https://acme.clerk.accounts.dev\";\n const fetchSpy = vi.spyOn(globalThis, \"fetch\").mockResolvedValue(\n new Response(JSON.stringify(doc(issuer)), {\n headers: { \"content-type\": \"application/json\" },\n }),\n );\n\n const config = await clerkProvider({ domain: \"acme.clerk.accounts.dev\" });\n\n expect(fetchSpy).toHaveBeenCalledWith(\n `${issuer}/.well-known/openid-configuration`,\n expect.anything(),\n );\n expect(config.verify.issuer).toBe(issuer);\n expect(config.verify.audience).toBeUndefined();\n });\n});\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
import type { OAuthConfig } from "../index.js";
|
|
3
|
+
/** Options accepted by {@link customProvider} and the branded providers. */
|
|
4
|
+
export type CustomProviderOptions = {
|
|
5
|
+
issuer: string;
|
|
6
|
+
/** Expected token `aud`. Omit to skip audience verification — only for IdPs
|
|
7
|
+
* that don't bind an audience (e.g. Clerk). Branded providers whose IdP does
|
|
8
|
+
* bind an audience re-require it in their own options. */
|
|
9
|
+
audience?: string;
|
|
10
|
+
/** Omit to let the server infer the resource origin from request headers. */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Advertise THIS server as the authorization server (skybridge-as-AS): the
|
|
13
|
+
* served AS metadata `issuer` and the PRM `authorization_servers` use this URL
|
|
14
|
+
* instead of the IdP's. Needed when skybridge must sit in the auth path — Auth0
|
|
15
|
+
* (the `audience` must be baked into `/authorize`) or the Alpic DCR proxy (Alpic
|
|
16
|
+
* injects the registration endpoint). Use the static public URL; `verify.issuer`
|
|
17
|
+
* stays the IdP (the token's real `iss`). */
|
|
18
|
+
serverUrl?: string;
|
|
19
|
+
/** Advertise this URL as the authorization server (served AS metadata `issuer`
|
|
20
|
+
* and PRM `authorization_servers`). Its own discovery document supplies the
|
|
21
|
+
* advertised endpoints, DCR registration, scopes and the `iss` tokens are
|
|
22
|
+
* verified against; only `jwks_uri` still comes from `issuer`'s document. Use
|
|
23
|
+
* when DCR/authorize/token live at a different URL than the discovery issuer
|
|
24
|
+
* (e.g. Descope's agentic URL vs. its base-project URL). `serverUrl` wins if
|
|
25
|
+
* both are set. */
|
|
26
|
+
authorizationServer?: string;
|
|
27
|
+
scopes?: string[];
|
|
28
|
+
requiredScopes?: string[];
|
|
29
|
+
metadataOverrides?: Omit<Partial<OAuthMetadata>, "issuer">;
|
|
30
|
+
};
|
|
31
|
+
/** Builds a complete {@link OAuthConfig} from an IdP's OAuth discovery document. */
|
|
32
|
+
export declare function customProvider(opts: CustomProviderOptions): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { discoverAuthorizationServer, } from "../discovery.js";
|
|
2
|
+
/** Builds a complete {@link OAuthConfig} from an IdP's OAuth discovery document. */
|
|
3
|
+
export async function customProvider(opts) {
|
|
4
|
+
const discovered = await discoverAuthorizationServer(opts.issuer);
|
|
5
|
+
// JWKS verification needs a signing-key URL; without it the server can't verify
|
|
6
|
+
// tokens. (`registration_endpoint` is optional per RFC 8414 — a no-DCR IdP simply
|
|
7
|
+
// omits it; a proxy like Alpic, or pre-registered clients, supply registration.)
|
|
8
|
+
if (!discovered.jwks_uri) {
|
|
9
|
+
throw new Error(`${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`);
|
|
10
|
+
}
|
|
11
|
+
// Overrides adjust only advertised metadata; the trust anchor (issuer, jwks_uri)
|
|
12
|
+
// always comes from validated discovery.
|
|
13
|
+
const { issuer: _issuer, jwks_uri: _jwks, ...overrides } = opts.metadataOverrides ?? {};
|
|
14
|
+
let advertised = discovered;
|
|
15
|
+
if (opts.authorizationServer) {
|
|
16
|
+
try {
|
|
17
|
+
advertised = await discoverAuthorizationServer(opts.authorizationServer);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
console.warn(`OAuth discovery failed for authorizationServer ${opts.authorizationServer}; advertising ${opts.issuer} metadata instead: ${err instanceof Error ? err.message : String(err)}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const base = { ...advertised, ...overrides };
|
|
24
|
+
const scopesSupported = opts.scopes ?? base.scopes_supported;
|
|
25
|
+
// serverUrl (skybridge-as-AS) overrides the advertised issuer, keeping the
|
|
26
|
+
// IdP as the token issuer. authorizationServer re-asserts its own URL so a
|
|
27
|
+
// failed AS discovery still points clients at the AS. serverUrl wins.
|
|
28
|
+
const advertisedIssuer = opts.serverUrl?.replace(/\/$/, "") ??
|
|
29
|
+
opts.authorizationServer?.replace(/\/$/, "");
|
|
30
|
+
const oauthMetadata = advertisedIssuer
|
|
31
|
+
? {
|
|
32
|
+
...base,
|
|
33
|
+
issuer: advertisedIssuer,
|
|
34
|
+
scopes_supported: scopesSupported,
|
|
35
|
+
}
|
|
36
|
+
: base;
|
|
37
|
+
return {
|
|
38
|
+
baseUrl: opts.baseUrl,
|
|
39
|
+
oauthMetadata,
|
|
40
|
+
verify: {
|
|
41
|
+
issuer: advertised.issuer,
|
|
42
|
+
audience: opts.audience,
|
|
43
|
+
jwksUri: advertised.jwks_uri ?? discovered.jwks_uri,
|
|
44
|
+
},
|
|
45
|
+
scopesSupported,
|
|
46
|
+
requiredScopes: opts.requiredScopes,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAgCzB,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAA2B;IAE3B,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAElE,gFAAgF;IAChF,kFAAkF;IAClF,iFAAiF;IACjF,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,4DAA4D,CAC3E,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,yCAAyC;IACzC,MAAM,EACJ,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK,EACf,GAAG,SAAS,EACb,GAAgC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC;IAC9D,IAAI,UAAU,GAAG,UAAU,CAAC;IAC5B,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CACV,kDAAkD,IAAI,CAAC,mBAAmB,iBAAiB,IAAI,CAAC,MAAM,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC/K,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAuB,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,CAAC;IACjE,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC;IAE7D,2EAA2E;IAC3E,2EAA2E;IAC3E,sEAAsE;IACtE,MAAM,gBAAgB,GACpB,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAuB,gBAAgB;QACxD,CAAC,CAAC;YACE,GAAG,IAAI;YACP,MAAM,EAAE,gBAAgB;YACxB,gBAAgB,EAAE,eAAe;SAClC;QACH,CAAC,CAAC,IAAI,CAAC;IAET,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,aAAa;QACb,MAAM,EAAE;YACN,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ;SACpD;QACD,eAAe;QACf,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC;AACJ,CAAC","sourcesContent":["import type { OAuthMetadata } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport {\n type DiscoveredMetadata,\n discoverAuthorizationServer,\n} from \"../discovery.js\";\nimport type { OAuthConfig } from \"../index.js\";\n\n/** Options accepted by {@link customProvider} and the branded providers. */\nexport type CustomProviderOptions = {\n issuer: string;\n /** Expected token `aud`. Omit to skip audience verification — only for IdPs\n * that don't bind an audience (e.g. Clerk). Branded providers whose IdP does\n * bind an audience re-require it in their own options. */\n audience?: string;\n /** Omit to let the server infer the resource origin from request headers. */\n baseUrl?: string;\n /** Advertise THIS server as the authorization server (skybridge-as-AS): the\n * served AS metadata `issuer` and the PRM `authorization_servers` use this URL\n * instead of the IdP's. Needed when skybridge must sit in the auth path — Auth0\n * (the `audience` must be baked into `/authorize`) or the Alpic DCR proxy (Alpic\n * injects the registration endpoint). Use the static public URL; `verify.issuer`\n * stays the IdP (the token's real `iss`). */\n serverUrl?: string;\n /** Advertise this URL as the authorization server (served AS metadata `issuer`\n * and PRM `authorization_servers`). Its own discovery document supplies the\n * advertised endpoints, DCR registration, scopes and the `iss` tokens are\n * verified against; only `jwks_uri` still comes from `issuer`'s document. Use\n * when DCR/authorize/token live at a different URL than the discovery issuer\n * (e.g. Descope's agentic URL vs. its base-project URL). `serverUrl` wins if\n * both are set. */\n authorizationServer?: string;\n scopes?: string[];\n requiredScopes?: string[];\n metadataOverrides?: Omit<Partial<OAuthMetadata>, \"issuer\">;\n};\n\n/** Builds a complete {@link OAuthConfig} from an IdP's OAuth discovery document. */\nexport async function customProvider(\n opts: CustomProviderOptions,\n): Promise<OAuthConfig> {\n const discovered = await discoverAuthorizationServer(opts.issuer);\n\n // JWKS verification needs a signing-key URL; without it the server can't verify\n // tokens. (`registration_endpoint` is optional per RFC 8414 — a no-DCR IdP simply\n // omits it; a proxy like Alpic, or pre-registered clients, supply registration.)\n if (!discovered.jwks_uri) {\n throw new Error(\n `${opts.issuer} discovery has no jwks_uri; JWKS verification requires it.`,\n );\n }\n\n // Overrides adjust only advertised metadata; the trust anchor (issuer, jwks_uri)\n // always comes from validated discovery.\n const {\n issuer: _issuer,\n jwks_uri: _jwks,\n ...overrides\n }: Partial<DiscoveredMetadata> = opts.metadataOverrides ?? {};\n let advertised = discovered;\n if (opts.authorizationServer) {\n try {\n advertised = await discoverAuthorizationServer(opts.authorizationServer);\n } catch (err) {\n console.warn(\n `OAuth discovery failed for authorizationServer ${opts.authorizationServer}; advertising ${opts.issuer} metadata instead: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n const base: DiscoveredMetadata = { ...advertised, ...overrides };\n const scopesSupported = opts.scopes ?? base.scopes_supported;\n\n // serverUrl (skybridge-as-AS) overrides the advertised issuer, keeping the\n // IdP as the token issuer. authorizationServer re-asserts its own URL so a\n // failed AS discovery still points clients at the AS. serverUrl wins.\n const advertisedIssuer =\n opts.serverUrl?.replace(/\\/$/, \"\") ??\n opts.authorizationServer?.replace(/\\/$/, \"\");\n const oauthMetadata: DiscoveredMetadata = advertisedIssuer\n ? {\n ...base,\n issuer: advertisedIssuer,\n scopes_supported: scopesSupported,\n }\n : base;\n\n return {\n baseUrl: opts.baseUrl,\n oauthMetadata,\n verify: {\n issuer: advertised.issuer,\n audience: opts.audience,\n jwksUri: advertised.jwks_uri ?? discovered.jwks_uri,\n },\n scopesSupported,\n requiredScopes: opts.requiredScopes,\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { customProvider } from "./custom.js";
|
|
5
|
+
const servers = [];
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
for (const srv of servers.splice(0)) {
|
|
8
|
+
srv.close();
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
function doc(origin, extra = {}) {
|
|
12
|
+
return {
|
|
13
|
+
issuer: origin,
|
|
14
|
+
authorization_endpoint: `${origin}/authorize`,
|
|
15
|
+
token_endpoint: `${origin}/token`,
|
|
16
|
+
registration_endpoint: `${origin}/register`,
|
|
17
|
+
response_types_supported: ["code"],
|
|
18
|
+
scopes_supported: ["openid", "email", "profile"],
|
|
19
|
+
jwks_uri: `${origin}/jwks`,
|
|
20
|
+
...extra,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Serves the discovery doc (built from the live origin) at the OIDC well-known path.
|
|
24
|
+
async function serveDiscovery(extra = {}) {
|
|
25
|
+
let origin = "";
|
|
26
|
+
const srv = http.createServer((req, res) => {
|
|
27
|
+
if (req.url !== "/.well-known/openid-configuration") {
|
|
28
|
+
res.writeHead(404).end();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
res.setHeader("content-type", "application/json");
|
|
32
|
+
res.end(JSON.stringify(doc(origin, extra)));
|
|
33
|
+
});
|
|
34
|
+
await new Promise((resolve) => srv.listen(0, resolve));
|
|
35
|
+
servers.push(srv);
|
|
36
|
+
origin = `http://localhost:${srv.address().port}`;
|
|
37
|
+
return origin;
|
|
38
|
+
}
|
|
39
|
+
describe("customProvider", () => {
|
|
40
|
+
it("builds an OAuthConfig from discovery", async () => {
|
|
41
|
+
const base = await serveDiscovery();
|
|
42
|
+
const config = await customProvider({
|
|
43
|
+
issuer: base,
|
|
44
|
+
audience: "my-api",
|
|
45
|
+
baseUrl: "https://app.example.test",
|
|
46
|
+
scopes: ["openid"],
|
|
47
|
+
});
|
|
48
|
+
expect(config.baseUrl).toBe("https://app.example.test");
|
|
49
|
+
expect(config.verify).toEqual({
|
|
50
|
+
issuer: base,
|
|
51
|
+
audience: "my-api",
|
|
52
|
+
jwksUri: `${base}/jwks`,
|
|
53
|
+
});
|
|
54
|
+
expect(config.scopesSupported).toEqual(["openid"]);
|
|
55
|
+
expect(config.oauthMetadata.registration_endpoint).toBe(`${base}/register`);
|
|
56
|
+
});
|
|
57
|
+
it("ignores a runtime issuer override (keeps the discovered trust anchor)", async () => {
|
|
58
|
+
const base = await serveDiscovery();
|
|
59
|
+
const config = await customProvider({
|
|
60
|
+
issuer: base,
|
|
61
|
+
audience: "a",
|
|
62
|
+
metadataOverrides: { issuer: "https://evil.test" },
|
|
63
|
+
});
|
|
64
|
+
expect(config.verify.issuer).toBe(base);
|
|
65
|
+
expect(config.oauthMetadata.issuer).toBe(base);
|
|
66
|
+
});
|
|
67
|
+
it("ignores a runtime jwks_uri override (keeps the discovered signing keys)", async () => {
|
|
68
|
+
const base = await serveDiscovery();
|
|
69
|
+
const config = await customProvider({
|
|
70
|
+
issuer: base,
|
|
71
|
+
audience: "a",
|
|
72
|
+
metadataOverrides: { jwks_uri: "https://evil.test/keys" },
|
|
73
|
+
});
|
|
74
|
+
expect(config.verify.jwksUri).toBe(`${base}/jwks`);
|
|
75
|
+
});
|
|
76
|
+
it("serves metadata without a registration_endpoint when the IdP has no DCR", async () => {
|
|
77
|
+
const base = await serveDiscovery({ registration_endpoint: undefined });
|
|
78
|
+
const config = await customProvider({ issuer: base, audience: "a" });
|
|
79
|
+
expect(config.oauthMetadata.registration_endpoint).toBeUndefined();
|
|
80
|
+
expect(config.verify.issuer).toBe(base);
|
|
81
|
+
});
|
|
82
|
+
it("throws when discovery has no jwks_uri (token verification needs it)", async () => {
|
|
83
|
+
const base = await serveDiscovery({ jwks_uri: undefined });
|
|
84
|
+
await expect(customProvider({ issuer: base, audience: "a" })).rejects.toThrow(/jwks_uri/);
|
|
85
|
+
});
|
|
86
|
+
it("serverUrl advertises this server as the AS, keeping the IdP as the token issuer", async () => {
|
|
87
|
+
const base = await serveDiscovery();
|
|
88
|
+
const config = await customProvider({
|
|
89
|
+
issuer: base,
|
|
90
|
+
audience: "a",
|
|
91
|
+
serverUrl: "https://app.example.test/",
|
|
92
|
+
scopes: ["openid"],
|
|
93
|
+
});
|
|
94
|
+
// AS metadata + advertised scopes reflect this server.
|
|
95
|
+
expect(config.oauthMetadata.issuer).toBe("https://app.example.test");
|
|
96
|
+
expect(config.oauthMetadata.scopes_supported).toEqual(["openid"]);
|
|
97
|
+
// but the token's issuer is still verified against the IdP.
|
|
98
|
+
expect(config.verify.issuer).toBe(base);
|
|
99
|
+
});
|
|
100
|
+
it("authorizationServer supplies the advertised metadata and the verified issuer", async () => {
|
|
101
|
+
const base = await serveDiscovery();
|
|
102
|
+
const as = await serveDiscovery({ scopes_supported: ["checkout"] });
|
|
103
|
+
const config = await customProvider({
|
|
104
|
+
issuer: base,
|
|
105
|
+
audience: "a",
|
|
106
|
+
authorizationServer: `${as}/`,
|
|
107
|
+
});
|
|
108
|
+
expect(config.oauthMetadata.issuer).toBe(as);
|
|
109
|
+
expect(config.oauthMetadata.authorization_endpoint).toBe(`${as}/authorize`);
|
|
110
|
+
expect(config.oauthMetadata.registration_endpoint).toBe(`${as}/register`);
|
|
111
|
+
expect(config.scopesSupported).toEqual(["checkout"]);
|
|
112
|
+
expect(config.verify).toEqual({
|
|
113
|
+
issuer: as,
|
|
114
|
+
audience: "a",
|
|
115
|
+
jwksUri: `${as}/jwks`,
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
it("falls back to the issuer's metadata when the authorizationServer is undiscoverable", async () => {
|
|
119
|
+
const base = await serveDiscovery();
|
|
120
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
121
|
+
const config = await customProvider({
|
|
122
|
+
issuer: base,
|
|
123
|
+
audience: "a",
|
|
124
|
+
authorizationServer: `${base}/agentic/P1/MS1`,
|
|
125
|
+
});
|
|
126
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
127
|
+
expect(config.oauthMetadata.issuer).toBe(`${base}/agentic/P1/MS1`);
|
|
128
|
+
expect(config.oauthMetadata.authorization_endpoint).toBe(`${base}/authorize`);
|
|
129
|
+
expect(config.verify.issuer).toBe(base);
|
|
130
|
+
warn.mockRestore();
|
|
131
|
+
});
|
|
132
|
+
it("serverUrl takes precedence over authorizationServer", async () => {
|
|
133
|
+
const base = await serveDiscovery();
|
|
134
|
+
const as = await serveDiscovery();
|
|
135
|
+
const config = await customProvider({
|
|
136
|
+
issuer: base,
|
|
137
|
+
audience: "a",
|
|
138
|
+
serverUrl: "https://app.example.test/",
|
|
139
|
+
authorizationServer: as,
|
|
140
|
+
});
|
|
141
|
+
expect(config.oauthMetadata.issuer).toBe("https://app.example.test");
|
|
142
|
+
expect(config.oauthMetadata.token_endpoint).toBe(`${as}/token`);
|
|
143
|
+
expect(config.verify.issuer).toBe(as);
|
|
144
|
+
});
|
|
145
|
+
it("applies metadataOverrides over discovered values", async () => {
|
|
146
|
+
const base = await serveDiscovery();
|
|
147
|
+
const config = await customProvider({
|
|
148
|
+
issuer: base,
|
|
149
|
+
audience: "a",
|
|
150
|
+
baseUrl: "https://app.example.test",
|
|
151
|
+
metadataOverrides: { token_endpoint: "https://override/token" },
|
|
152
|
+
});
|
|
153
|
+
expect(config.oauthMetadata.token_endpoint).toBe("https://override/token");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
//# sourceMappingURL=custom.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/custom.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { customProvider } from \"./custom.js\";\n\nconst servers: http.Server[] = [];\nafterEach(() => {\n for (const srv of servers.splice(0)) {\n srv.close();\n }\n});\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\", \"profile\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves the discovery doc (built from the live origin) at the OIDC well-known path.\nasync function serveDiscovery(extra: Record<string, unknown> = {}) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n if (req.url !== \"/.well-known/openid-configuration\") {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(doc(origin, extra)));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n servers.push(srv);\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"customProvider\", () => {\n it(\"builds an OAuthConfig from discovery\", async () => {\n const base = await serveDiscovery();\n\n const config = await customProvider({\n issuer: base,\n audience: \"my-api\",\n baseUrl: \"https://app.example.test\",\n scopes: [\"openid\"],\n });\n\n expect(config.baseUrl).toBe(\"https://app.example.test\");\n expect(config.verify).toEqual({\n issuer: base,\n audience: \"my-api\",\n jwksUri: `${base}/jwks`,\n });\n expect(config.scopesSupported).toEqual([\"openid\"]);\n expect(config.oauthMetadata.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"ignores a runtime issuer override (keeps the discovered trust anchor)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { issuer: \"https://evil.test\" } as never,\n });\n expect(config.verify.issuer).toBe(base);\n expect(config.oauthMetadata.issuer).toBe(base);\n });\n\n it(\"ignores a runtime jwks_uri override (keeps the discovered signing keys)\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n metadataOverrides: { jwks_uri: \"https://evil.test/keys\" } as never,\n });\n expect(config.verify.jwksUri).toBe(`${base}/jwks`);\n });\n\n it(\"serves metadata without a registration_endpoint when the IdP has no DCR\", async () => {\n const base = await serveDiscovery({ registration_endpoint: undefined });\n const config = await customProvider({ issuer: base, audience: \"a\" });\n expect(config.oauthMetadata.registration_endpoint).toBeUndefined();\n expect(config.verify.issuer).toBe(base);\n });\n\n it(\"throws when discovery has no jwks_uri (token verification needs it)\", async () => {\n const base = await serveDiscovery({ jwks_uri: undefined });\n await expect(\n customProvider({ issuer: base, audience: \"a\" }),\n ).rejects.toThrow(/jwks_uri/);\n });\n\n it(\"serverUrl advertises this server as the AS, keeping the IdP as the token issuer\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n serverUrl: \"https://app.example.test/\",\n scopes: [\"openid\"],\n });\n // AS metadata + advertised scopes reflect this server.\n expect(config.oauthMetadata.issuer).toBe(\"https://app.example.test\");\n expect(config.oauthMetadata.scopes_supported).toEqual([\"openid\"]);\n // but the token's issuer is still verified against the IdP.\n expect(config.verify.issuer).toBe(base);\n });\n\n it(\"authorizationServer supplies the advertised metadata and the verified issuer\", async () => {\n const base = await serveDiscovery();\n const as = await serveDiscovery({ scopes_supported: [\"checkout\"] });\n\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n authorizationServer: `${as}/`,\n });\n\n expect(config.oauthMetadata.issuer).toBe(as);\n expect(config.oauthMetadata.authorization_endpoint).toBe(`${as}/authorize`);\n expect(config.oauthMetadata.registration_endpoint).toBe(`${as}/register`);\n expect(config.scopesSupported).toEqual([\"checkout\"]);\n expect(config.verify).toEqual({\n issuer: as,\n audience: \"a\",\n jwksUri: `${as}/jwks`,\n });\n });\n\n it(\"falls back to the issuer's metadata when the authorizationServer is undiscoverable\", async () => {\n const base = await serveDiscovery();\n const warn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n authorizationServer: `${base}/agentic/P1/MS1`,\n });\n\n expect(warn).toHaveBeenCalledOnce();\n expect(config.oauthMetadata.issuer).toBe(`${base}/agentic/P1/MS1`);\n expect(config.oauthMetadata.authorization_endpoint).toBe(\n `${base}/authorize`,\n );\n expect(config.verify.issuer).toBe(base);\n warn.mockRestore();\n });\n\n it(\"serverUrl takes precedence over authorizationServer\", async () => {\n const base = await serveDiscovery();\n const as = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n serverUrl: \"https://app.example.test/\",\n authorizationServer: as,\n });\n expect(config.oauthMetadata.issuer).toBe(\"https://app.example.test\");\n expect(config.oauthMetadata.token_endpoint).toBe(`${as}/token`);\n expect(config.verify.issuer).toBe(as);\n });\n\n it(\"applies metadataOverrides over discovered values\", async () => {\n const base = await serveDiscovery();\n const config = await customProvider({\n issuer: base,\n audience: \"a\",\n baseUrl: \"https://app.example.test\",\n metadataOverrides: { token_endpoint: \"https://override/token\" },\n });\n expect(config.oauthMetadata.token_endpoint).toBe(\"https://override/token\");\n });\n});\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for a Descope MCP Server. `url` is the MCP Server's **Discovery
|
|
5
|
+
* URL** (a.k.a. Issuer) from the console's Connection Information, e.g.
|
|
6
|
+
* `https://api.descope.com/v1/apps/agentic/<projectId>/<mcpServerId>` (or your
|
|
7
|
+
* custom domain). Requires DCR enabled on the MCP Server. For Descope with DCR
|
|
8
|
+
* disabled and Alpic's DCR proxy, use {@link customProvider} with `serverUrl`
|
|
9
|
+
* instead (see `examples/auth-descope-alpic`). The token `audience` defaults to
|
|
10
|
+
* the **Project ID** derived from the URL — Descope binds `aud` to [DCR client
|
|
11
|
+
* id, project id], not the server URL; pass `audience` to override.
|
|
12
|
+
*/
|
|
13
|
+
export declare function descopeProvider(opts: {
|
|
14
|
+
url: string;
|
|
15
|
+
} & Omit<CustomProviderOptions, "issuer">): Promise<OAuthConfig>;
|