@adcp/sdk 12.0.0 → 12.0.1
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/lib/adapters/content-standards-adapter.mjs +0 -6
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/derived-account-store.mjs +0 -6
- package/dist/lib/adapters/derived-account-store.mjs.map +1 -1
- package/dist/lib/adapters/governance-adapter.mjs +0 -6
- package/dist/lib/adapters/governance-adapter.mjs.map +1 -1
- package/dist/lib/adapters/implicit-account-store.mjs +0 -6
- package/dist/lib/adapters/implicit-account-store.mjs.map +1 -1
- package/dist/lib/adapters/index.mjs +0 -6
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/index.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/types.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs +0 -6
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs.map +1 -1
- package/dist/lib/adapters/property-list-adapter.mjs +0 -6
- package/dist/lib/adapters/property-list-adapter.mjs.map +1 -1
- package/dist/lib/adapters/roster-account-store.mjs +0 -6
- package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
- package/dist/lib/adapters/si-session-manager.mjs +0 -6
- package/dist/lib/adapters/si-session-manager.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/brand-fields.mjs +0 -6
- package/dist/lib/adapters/version/3.0/brand-fields.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/get-products.mjs +0 -6
- package/dist/lib/adapters/version/3.0/get-products.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/index.mjs +0 -6
- package/dist/lib/adapters/version/3.0/index.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs +0 -6
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs.map +1 -1
- package/dist/lib/adapters/version/index.mjs +0 -6
- package/dist/lib/adapters/version/index.mjs.map +1 -1
- package/dist/lib/adapters/version/types.mjs +0 -6
- package/dist/lib/advanced.mjs +0 -6
- package/dist/lib/advanced.mjs.map +1 -1
- package/dist/lib/agents/index.generated.mjs +0 -6
- package/dist/lib/agents/index.generated.mjs.map +1 -1
- package/dist/lib/auth/index.mjs +0 -6
- package/dist/lib/auth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +0 -6
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs.map +1 -1
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +0 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs.map +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +0 -6
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnose.mjs +0 -6
- package/dist/lib/auth/oauth/diagnose.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnostics.mjs +0 -6
- package/dist/lib/auth/oauth/diagnostics.mjs.map +1 -1
- package/dist/lib/auth/oauth/discovery.mjs +0 -6
- package/dist/lib/auth/oauth/discovery.mjs.map +1 -1
- package/dist/lib/auth/oauth/file-storage.mjs +0 -6
- package/dist/lib/auth/oauth/file-storage.mjs.map +1 -1
- package/dist/lib/auth/oauth/index.mjs +0 -6
- package/dist/lib/auth/oauth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/secret-resolver.mjs +0 -6
- package/dist/lib/auth/oauth/secret-resolver.mjs.map +1 -1
- package/dist/lib/auth/oauth/storage-registry.mjs +0 -6
- package/dist/lib/auth/oauth/storage-registry.mjs.map +1 -1
- package/dist/lib/auth/oauth/types.mjs +0 -6
- package/dist/lib/auth/oauth/types.mjs.map +1 -1
- package/dist/lib/auth/oauth/web-flow.mjs +0 -6
- package/dist/lib/auth/oauth/web-flow.mjs.map +1 -1
- package/dist/lib/brand/index.mjs +0 -6
- package/dist/lib/brand/index.mjs.map +1 -1
- package/dist/lib/canonical-references/index.mjs +6 -7
- package/dist/lib/canonical-references/index.mjs.map +1 -1
- package/dist/lib/client/index.mjs +0 -6
- package/dist/lib/client/index.mjs.map +1 -1
- package/dist/lib/compliance/index.mjs +0 -6
- package/dist/lib/compliance/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/index.mjs +0 -6
- package/dist/lib/compliance-fixtures/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs +0 -6
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs.map +1 -1
- package/dist/lib/conformance/index.mjs +0 -6
- package/dist/lib/conformance/index.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs.map +1 -1
- package/dist/lib/conformance/oracle.mjs +0 -6
- package/dist/lib/conformance/oracle.mjs.map +1 -1
- package/dist/lib/conformance/runConformance.mjs +0 -6
- package/dist/lib/conformance/runConformance.mjs.map +1 -1
- package/dist/lib/conformance/runners.mjs +0 -6
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/schemaArbitrary.mjs +0 -6
- package/dist/lib/conformance/schemaArbitrary.mjs.map +1 -1
- package/dist/lib/conformance/schemaLoader.mjs +5 -6
- package/dist/lib/conformance/schemaLoader.mjs.map +1 -1
- package/dist/lib/conformance/seeder.mjs +0 -6
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/conformance/types.mjs +0 -6
- package/dist/lib/conformance/types.mjs.map +1 -1
- package/dist/lib/conformance/webhook.mjs +0 -6
- package/dist/lib/conformance/webhook.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +0 -6
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.mjs +0 -6
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +0 -6
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConfigurationManager.mjs +0 -6
- package/dist/lib/core/ConfigurationManager.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.mjs +0 -6
- package/dist/lib/core/GovernanceMiddleware.mjs.map +1 -1
- package/dist/lib/core/GovernanceTypes.mjs +0 -6
- package/dist/lib/core/GovernanceTypes.mjs.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.mjs +0 -6
- package/dist/lib/core/ProtocolResponseParser.mjs.map +1 -1
- package/dist/lib/core/ResponseValidator.mjs +0 -6
- package/dist/lib/core/ResponseValidator.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +0 -6
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskEventTypes.mjs +0 -6
- package/dist/lib/core/TaskEventTypes.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +0 -6
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/core/match.mjs +0 -6
- package/dist/lib/core/match.mjs.map +1 -1
- package/dist/lib/core/task-status.mjs +0 -6
- package/dist/lib/core/task-status.mjs.map +1 -1
- package/dist/lib/core/webhook-url.mjs +0 -6
- package/dist/lib/core/webhook-url.mjs.map +1 -1
- package/dist/lib/discovery/adagents-redirects.mjs +0 -6
- package/dist/lib/discovery/adagents-redirects.mjs.map +1 -1
- package/dist/lib/discovery/agent-directory.mjs +0 -6
- package/dist/lib/discovery/agent-directory.mjs.map +1 -1
- package/dist/lib/discovery/inline-publisher-properties.mjs +0 -6
- package/dist/lib/discovery/inline-publisher-properties.mjs.map +1 -1
- package/dist/lib/discovery/network-consistency-checker.mjs +0 -6
- package/dist/lib/discovery/network-consistency-checker.mjs.map +1 -1
- package/dist/lib/discovery/property-crawler.mjs +0 -6
- package/dist/lib/discovery/property-crawler.mjs.map +1 -1
- package/dist/lib/discovery/property-index.mjs +0 -6
- package/dist/lib/discovery/property-index.mjs.map +1 -1
- package/dist/lib/discovery/publisher-property-selector.mjs +0 -6
- package/dist/lib/discovery/publisher-property-selector.mjs.map +1 -1
- package/dist/lib/discovery/resolve-agent-properties.mjs +0 -6
- package/dist/lib/discovery/resolve-agent-properties.mjs.map +1 -1
- package/dist/lib/discovery/types.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs.map +1 -1
- package/dist/lib/enums.mjs +0 -6
- package/dist/lib/enums.mjs.map +1 -1
- package/dist/lib/errors/index.mjs +0 -6
- package/dist/lib/errors/index.mjs.map +1 -1
- package/dist/lib/express-mcp/index.mjs +0 -6
- package/dist/lib/express-mcp/index.mjs.map +1 -1
- package/dist/lib/governance/index.mjs +0 -6
- package/dist/lib/governance/index.mjs.map +1 -1
- package/dist/lib/handlers/types.mjs +0 -6
- package/dist/lib/handlers/types.mjs.map +1 -1
- package/dist/lib/index.mjs +0 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/media-buy/available-actions.mjs +0 -6
- package/dist/lib/media-buy/available-actions.mjs.map +1 -1
- package/dist/lib/media-buy/index.mjs +0 -6
- package/dist/lib/media-buy/index.mjs.map +1 -1
- package/dist/lib/media-buy/preflight.mjs +0 -6
- package/dist/lib/media-buy/preflight.mjs.map +1 -1
- package/dist/lib/media-buy/property-policy.mjs +0 -6
- package/dist/lib/media-buy/property-policy.mjs.map +1 -1
- package/dist/lib/media-buy/types.mjs +0 -6
- package/dist/lib/media-buy/types.mjs.map +1 -1
- package/dist/lib/media-buy/update-fields.generated.mjs +0 -6
- package/dist/lib/media-buy/update-fields.generated.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/server.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-template/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/server.mjs +0 -6
- package/dist/lib/mock-server/creative-template/server.mjs.map +1 -1
- package/dist/lib/mock-server/index.mjs +0 -6
- package/dist/lib/mock-server/index.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-social/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/server.mjs +0 -6
- package/dist/lib/mock-server/sales-social/server.mjs.map +1 -1
- package/dist/lib/mock-server/scenario.mjs +0 -6
- package/dist/lib/mock-server/scenario.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/server.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/server.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs.map +1 -1
- package/dist/lib/net/address-guards.mjs +0 -6
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/net/index.mjs +0 -6
- package/dist/lib/net/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +0 -6
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/observability/index.mjs +0 -6
- package/dist/lib/observability/index.mjs.map +1 -1
- package/dist/lib/observability/tracing.mjs +6 -7
- package/dist/lib/observability/tracing.mjs.map +1 -1
- package/dist/lib/protocols/a2a.mjs +0 -6
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/abort.mjs +0 -6
- package/dist/lib/protocols/abort.mjs.map +1 -1
- package/dist/lib/protocols/index.mjs +0 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +0 -6
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +0 -6
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.mjs +0 -6
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +0 -6
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/protocols/responseSizeLimit.mjs +0 -6
- package/dist/lib/protocols/responseSizeLimit.mjs.map +1 -1
- package/dist/lib/protocols/transportDiagnostics.mjs +0 -6
- package/dist/lib/protocols/transportDiagnostics.mjs.map +1 -1
- package/dist/lib/registry/cursor-store.mjs +0 -6
- package/dist/lib/registry/cursor-store.mjs.map +1 -1
- package/dist/lib/registry/feed-stream.mjs +0 -6
- package/dist/lib/registry/feed-stream.mjs.map +1 -1
- package/dist/lib/registry/index.mjs +0 -6
- package/dist/lib/registry/index.mjs.map +1 -1
- package/dist/lib/registry/property-registry.mjs +0 -6
- package/dist/lib/registry/property-registry.mjs.map +1 -1
- package/dist/lib/registry/sync.mjs +0 -6
- package/dist/lib/registry/sync.mjs.map +1 -1
- package/dist/lib/registry/types.generated.mjs +0 -6
- package/dist/lib/registry/types.mjs +0 -6
- package/dist/lib/schemas/index.mjs +0 -6
- package/dist/lib/schemas/index.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.mjs +0 -6
- package/dist/lib/server/a2a-adapter.mjs.map +1 -1
- package/dist/lib/server/account-mode.mjs +0 -6
- package/dist/lib/server/account-mode.mjs.map +1 -1
- package/dist/lib/server/adcp-server.mjs +0 -6
- package/dist/lib/server/adcp-server.mjs.map +1 -1
- package/dist/lib/server/auth-introspection.mjs +0 -6
- package/dist/lib/server/auth-introspection.mjs.map +1 -1
- package/dist/lib/server/auth-signature.mjs +0 -6
- package/dist/lib/server/auth-signature.mjs.map +1 -1
- package/dist/lib/server/auth.mjs +0 -6
- package/dist/lib/server/auth.mjs.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +0 -6
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/credential-policy.mjs +0 -6
- package/dist/lib/server/credential-policy.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/memory.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/pg.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/pg.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/redis.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/redis.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/index.mjs +0 -6
- package/dist/lib/server/ctx-metadata/index.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/store.mjs +0 -6
- package/dist/lib/server/ctx-metadata/store.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/wire-shape.mjs +0 -6
- package/dist/lib/server/ctx-metadata/wire-shape.mjs.map +1 -1
- package/dist/lib/server/decisioning/account.mjs +0 -6
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/admin-router.mjs +0 -6
- package/dist/lib/server/decisioning/admin-router.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/async-outcome.mjs +0 -6
- package/dist/lib/server/decisioning/async-outcome.mjs.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.mjs +0 -6
- package/dist/lib/server/decisioning/buyer-agent.mjs.map +1 -1
- package/dist/lib/server/decisioning/capabilities.mjs +0 -6
- package/dist/lib/server/decisioning/capabilities.mjs.map +1 -1
- package/dist/lib/server/decisioning/compose.mjs +0 -6
- package/dist/lib/server/decisioning/compose.mjs.map +1 -1
- package/dist/lib/server/decisioning/context.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs.map +1 -1
- package/dist/lib/server/decisioning/helpers.mjs +0 -6
- package/dist/lib/server/decisioning/helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +0 -6
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/pagination.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/platform.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/store.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/store.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/resolve-presets.mjs +0 -6
- package/dist/lib/server/decisioning/resolve-presets.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/to-context.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/to-context.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/audiences.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/brand-rights.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/campaign-governance.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/content-standards.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/lists.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sales.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/signals.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-changes.mjs +0 -6
- package/dist/lib/server/decisioning/status-changes.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-mappers.mjs +0 -6
- package/dist/lib/server/decisioning/status-mappers.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-store.mjs.map +1 -1
- package/dist/lib/server/decisioning/validate-specialisms.mjs +0 -6
- package/dist/lib/server/decisioning/validate-specialisms.mjs.map +1 -1
- package/dist/lib/server/dynamic-registry.mjs +0 -6
- package/dist/lib/server/dynamic-registry.mjs.map +1 -1
- package/dist/lib/server/envelope-allowlist.mjs +0 -6
- package/dist/lib/server/envelope-allowlist.mjs.map +1 -1
- package/dist/lib/server/error-arm-tools.mjs +5 -6
- package/dist/lib/server/error-arm-tools.mjs.map +1 -1
- package/dist/lib/server/errors.mjs +0 -6
- package/dist/lib/server/errors.mjs.map +1 -1
- package/dist/lib/server/example-tld-guard.mjs +0 -6
- package/dist/lib/server/example-tld-guard.mjs.map +1 -1
- package/dist/lib/server/express-adapter.mjs +0 -6
- package/dist/lib/server/express-adapter.mjs.map +1 -1
- package/dist/lib/server/governance.mjs +0 -6
- package/dist/lib/server/governance.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/lazy.mjs +0 -6
- package/dist/lib/server/idempotency/backends/lazy.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/memory.mjs +0 -6
- package/dist/lib/server/idempotency/backends/memory.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/pg.mjs +0 -6
- package/dist/lib/server/idempotency/backends/pg.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/redis.mjs +0 -6
- package/dist/lib/server/idempotency/backends/redis.mjs.map +1 -1
- package/dist/lib/server/idempotency/index.mjs +0 -6
- package/dist/lib/server/idempotency/index.mjs.map +1 -1
- package/dist/lib/server/idempotency/store.mjs +0 -6
- package/dist/lib/server/idempotency/store.mjs.map +1 -1
- package/dist/lib/server/index.mjs +0 -6
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +0 -6
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/mcp-modern-server.mjs +0 -6
- package/dist/lib/server/mcp-modern-server.mjs.map +1 -1
- package/dist/lib/server/media-buy-actions.mjs +0 -6
- package/dist/lib/server/media-buy-actions.mjs.map +1 -1
- package/dist/lib/server/media-buy-helpers.mjs +0 -6
- package/dist/lib/server/media-buy-helpers.mjs.map +1 -1
- package/dist/lib/server/media-buy-store.mjs +0 -6
- package/dist/lib/server/media-buy-store.mjs.map +1 -1
- package/dist/lib/server/normalize-errors.mjs +0 -6
- package/dist/lib/server/normalize-errors.mjs.map +1 -1
- package/dist/lib/server/operational-platform.mjs +0 -6
- package/dist/lib/server/operational-platform.mjs.map +1 -1
- package/dist/lib/server/pick-safe-details.mjs +0 -6
- package/dist/lib/server/pick-safe-details.mjs.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.mjs +0 -6
- package/dist/lib/server/pin-and-bind-fetch.mjs.map +1 -1
- package/dist/lib/server/postgres-state-store.mjs +0 -6
- package/dist/lib/server/postgres-state-store.mjs.map +1 -1
- package/dist/lib/server/postgres-task-store.mjs +0 -6
- package/dist/lib/server/postgres-task-store.mjs.map +1 -1
- package/dist/lib/server/product-defaults.mjs +0 -6
- package/dist/lib/server/product-defaults.mjs.map +1 -1
- package/dist/lib/server/redact.mjs +0 -6
- package/dist/lib/server/redact.mjs.map +1 -1
- package/dist/lib/server/responses.mjs +0 -6
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/server/serve.mjs +0 -6
- package/dist/lib/server/serve.mjs.map +1 -1
- package/dist/lib/server/socket-mode/conformance-client.mjs +0 -6
- package/dist/lib/server/socket-mode/conformance-client.mjs.map +1 -1
- package/dist/lib/server/socket-mode/index.mjs +0 -6
- package/dist/lib/server/socket-mode/index.mjs.map +1 -1
- package/dist/lib/server/socket-mode/ws-transport.mjs +0 -6
- package/dist/lib/server/socket-mode/ws-transport.mjs.map +1 -1
- package/dist/lib/server/state-machine.mjs +0 -6
- package/dist/lib/server/state-machine.mjs.map +1 -1
- package/dist/lib/server/state-store.mjs +0 -6
- package/dist/lib/server/state-store.mjs.map +1 -1
- package/dist/lib/server/structured-serialize.mjs +0 -6
- package/dist/lib/server/structured-serialize.mjs.map +1 -1
- package/dist/lib/server/targeting-helpers.mjs +0 -6
- package/dist/lib/server/targeting-helpers.mjs.map +1 -1
- package/dist/lib/server/tasks.mjs +0 -6
- package/dist/lib/server/tasks.mjs.map +1 -1
- package/dist/lib/server/test-controller-bridge.mjs +0 -6
- package/dist/lib/server/test-controller-bridge.mjs.map +1 -1
- package/dist/lib/server/test-controller.mjs +0 -6
- package/dist/lib/server/test-controller.mjs.map +1 -1
- package/dist/lib/server/upstream-helpers.mjs +0 -6
- package/dist/lib/server/upstream-helpers.mjs.map +1 -1
- package/dist/lib/server/webhook-emitter.mjs +0 -6
- package/dist/lib/server/webhook-emitter.mjs.map +1 -1
- package/dist/lib/server/wire-safe.mjs +0 -6
- package/dist/lib/server/wire-safe.mjs.map +1 -1
- package/dist/lib/server/wire-spec-fields.generated.mjs +0 -6
- package/dist/lib/server/wire-spec-fields.generated.mjs.map +1 -1
- package/dist/lib/server/wrap-envelope.mjs +0 -6
- package/dist/lib/server/wrap-envelope.mjs.map +1 -1
- package/dist/lib/signing/agent-context.mjs +0 -6
- package/dist/lib/signing/agent-context.mjs.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +0 -6
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/canonicalize.mjs +0 -6
- package/dist/lib/signing/agent-resolver/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs +0 -6
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -6
- package/dist/lib/signing/agent-resolver/consistency.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/errors.mjs +0 -6
- package/dist/lib/signing/agent-resolver/errors.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/etld.mjs +0 -6
- package/dist/lib/signing/agent-resolver/etld.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +0 -6
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/index.mjs +0 -6
- package/dist/lib/signing/agent-resolver/index.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +0 -6
- package/dist/lib/signing/agent-resolver/jwks-set.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/select-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/select-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/strict-json.mjs +0 -6
- package/dist/lib/signing/agent-resolver/strict-json.mjs.map +1 -1
- package/dist/lib/signing/brand-jwks.mjs +0 -6
- package/dist/lib/signing/brand-jwks.mjs.map +1 -1
- package/dist/lib/signing/canonicalize.mjs +0 -6
- package/dist/lib/signing/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/capability-cache.mjs +0 -6
- package/dist/lib/signing/capability-cache.mjs.map +1 -1
- package/dist/lib/signing/capability-priming.mjs +0 -6
- package/dist/lib/signing/capability-priming.mjs.map +1 -1
- package/dist/lib/signing/client.mjs +0 -6
- package/dist/lib/signing/client.mjs.map +1 -1
- package/dist/lib/signing/content-digest.mjs +0 -6
- package/dist/lib/signing/content-digest.mjs.map +1 -1
- package/dist/lib/signing/crypto.mjs +0 -6
- package/dist/lib/signing/crypto.mjs.map +1 -1
- package/dist/lib/signing/ecdsa-encoding.mjs +0 -6
- package/dist/lib/signing/ecdsa-encoding.mjs.map +1 -1
- package/dist/lib/signing/errors.mjs +0 -6
- package/dist/lib/signing/errors.mjs.map +1 -1
- package/dist/lib/signing/fetch-async.mjs +0 -6
- package/dist/lib/signing/fetch-async.mjs.map +1 -1
- package/dist/lib/signing/fetch.mjs +0 -6
- package/dist/lib/signing/fetch.mjs.map +1 -1
- package/dist/lib/signing/index.mjs +0 -6
- package/dist/lib/signing/index.mjs.map +1 -1
- package/dist/lib/signing/jwks-helpers.mjs +0 -6
- package/dist/lib/signing/jwks-helpers.mjs.map +1 -1
- package/dist/lib/signing/jwks-https.mjs +0 -6
- package/dist/lib/signing/jwks-https.mjs.map +1 -1
- package/dist/lib/signing/jwks.mjs +0 -6
- package/dist/lib/signing/jwks.mjs.map +1 -1
- package/dist/lib/signing/middleware.mjs +0 -6
- package/dist/lib/signing/middleware.mjs.map +1 -1
- package/dist/lib/signing/parser.mjs +0 -6
- package/dist/lib/signing/parser.mjs.map +1 -1
- package/dist/lib/signing/postgres-replay-store.mjs +0 -6
- package/dist/lib/signing/postgres-replay-store.mjs.map +1 -1
- package/dist/lib/signing/protocol-response.mjs +0 -6
- package/dist/lib/signing/protocol-response.mjs.map +1 -1
- package/dist/lib/signing/provider.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs.map +1 -1
- package/dist/lib/signing/replay.mjs +0 -6
- package/dist/lib/signing/replay.mjs.map +1 -1
- package/dist/lib/signing/request-context.mjs +0 -6
- package/dist/lib/signing/request-context.mjs.map +1 -1
- package/dist/lib/signing/revocation-https.mjs +0 -6
- package/dist/lib/signing/revocation-https.mjs.map +1 -1
- package/dist/lib/signing/revocation.mjs +0 -6
- package/dist/lib/signing/revocation.mjs.map +1 -1
- package/dist/lib/signing/server.mjs +0 -6
- package/dist/lib/signing/server.mjs.map +1 -1
- package/dist/lib/signing/signer-async.mjs +0 -6
- package/dist/lib/signing/signer-async.mjs.map +1 -1
- package/dist/lib/signing/signer.mjs +0 -6
- package/dist/lib/signing/signer.mjs.map +1 -1
- package/dist/lib/signing/testing.mjs +0 -6
- package/dist/lib/signing/testing.mjs.map +1 -1
- package/dist/lib/signing/types.mjs +0 -6
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.mjs +0 -6
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/signing/webhook-auth-detection.mjs +0 -6
- package/dist/lib/signing/webhook-auth-detection.mjs.map +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +0 -6
- package/dist/lib/signing/webhook-verifier.mjs.map +1 -1
- package/dist/lib/storage/MemoryStorage.mjs +0 -6
- package/dist/lib/storage/MemoryStorage.mjs.map +1 -1
- package/dist/lib/storage/interfaces.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs.map +1 -1
- package/dist/lib/substitution/encoder/index.mjs +0 -6
- package/dist/lib/substitution/encoder/index.mjs.map +1 -1
- package/dist/lib/substitution/index.mjs +0 -6
- package/dist/lib/substitution/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs +0 -6
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs.map +1 -1
- package/dist/lib/substitution/observer/alignment.mjs +0 -6
- package/dist/lib/substitution/observer/alignment.mjs.map +1 -1
- package/dist/lib/substitution/observer/assertions.mjs +0 -6
- package/dist/lib/substitution/observer/assertions.mjs.map +1 -1
- package/dist/lib/substitution/observer/html-parser.mjs +0 -6
- package/dist/lib/substitution/observer/html-parser.mjs.map +1 -1
- package/dist/lib/substitution/observer/index.mjs +0 -6
- package/dist/lib/substitution/observer/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/ssrf.mjs +0 -6
- package/dist/lib/substitution/observer/ssrf.mjs.map +1 -1
- package/dist/lib/substitution/rfc3986.mjs +0 -6
- package/dist/lib/substitution/rfc3986.mjs.map +1 -1
- package/dist/lib/substitution/translate.mjs +0 -6
- package/dist/lib/substitution/translate.mjs.map +1 -1
- package/dist/lib/substitution/types.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs.map +1 -1
- package/dist/lib/testing/agent-tester.mjs +0 -6
- package/dist/lib/testing/agent-tester.mjs.map +1 -1
- package/dist/lib/testing/client.mjs +0 -6
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/briefs.mjs +0 -6
- package/dist/lib/testing/compliance/briefs.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +0 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/index.mjs +0 -6
- package/dist/lib/testing/compliance/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +0 -6
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +0 -6
- package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +0 -6
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs.map +1 -1
- package/dist/lib/testing/controller-assertions.mjs +0 -6
- package/dist/lib/testing/controller-assertions.mjs.map +1 -1
- package/dist/lib/testing/formatter.mjs +0 -6
- package/dist/lib/testing/formatter.mjs.map +1 -1
- package/dist/lib/testing/index.mjs +0 -6
- package/dist/lib/testing/index.mjs.map +1 -1
- package/dist/lib/testing/local-agent-runner.mjs +0 -6
- package/dist/lib/testing/local-agent-runner.mjs.map +1 -1
- package/dist/lib/testing/orchestrator.mjs +0 -6
- package/dist/lib/testing/orchestrator.mjs.map +1 -1
- package/dist/lib/testing/personas/index.mjs +0 -6
- package/dist/lib/testing/personas/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +0 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +0 -6
- package/dist/lib/testing/scenarios/capabilities.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +0 -6
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.mjs +0 -6
- package/dist/lib/testing/scenarios/deterministic.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +0 -6
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +0 -6
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/error-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +0 -6
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/health.mjs +0 -6
- package/dist/lib/testing/scenarios/health.mjs.map +1 -1
- package/dist/lib/testing/scenarios/index.mjs +0 -6
- package/dist/lib/testing/scenarios/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +0 -6
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/signals.mjs +0 -6
- package/dist/lib/testing/scenarios/signals.mjs.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs +0 -6
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +0 -6
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/seed-merge.mjs +0 -6
- package/dist/lib/testing/seed-merge.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +0 -6
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +0 -6
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +5 -6
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/context.mjs +0 -6
- package/dist/lib/testing/storyboard/context.mjs.map +1 -1
- package/dist/lib/testing/storyboard/default-invariants.mjs +0 -6
- package/dist/lib/testing/storyboard/default-invariants.mjs.map +1 -1
- package/dist/lib/testing/storyboard/index.mjs +0 -6
- package/dist/lib/testing/storyboard/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +0 -6
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +0 -6
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/path.mjs +0 -6
- package/dist/lib/testing/storyboard/path.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +0 -6
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs +0 -6
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rejection-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/rejection-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +0 -6
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/sandbox-entities.mjs +0 -6
- package/dist/lib/testing/storyboard/sandbox-entities.mjs.map +1 -1
- package/dist/lib/testing/storyboard/seeding.mjs +0 -6
- package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/index.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/types.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +0 -6
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/storyboard/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +0 -6
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +0 -6
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-receiver.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-receiver.mjs.map +1 -1
- package/dist/lib/testing/stubs/governance-agent-stub.mjs +0 -6
- package/dist/lib/testing/stubs/governance-agent-stub.mjs.map +1 -1
- package/dist/lib/testing/stubs/index.mjs +0 -6
- package/dist/lib/testing/stubs/index.mjs.map +1 -1
- package/dist/lib/testing/test-controller.mjs +0 -6
- package/dist/lib/testing/test-controller.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.mjs +0 -6
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.mjs +0 -6
- package/dist/lib/types/adcp.mjs +0 -6
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/asset-instances.mjs +0 -6
- package/dist/lib/types/compat.mjs +0 -6
- package/dist/lib/types/compat.mjs.map +1 -1
- package/dist/lib/types/core.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs.map +1 -1
- package/dist/lib/types/error-codes.mjs +0 -6
- package/dist/lib/types/error-codes.mjs.map +1 -1
- package/dist/lib/types/error-details.aliases.mjs +0 -6
- package/dist/lib/types/error-details.aliases.mjs.map +1 -1
- package/dist/lib/types/format-asset-slots.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs.map +1 -1
- package/dist/lib/types/index.mjs +0 -6
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/types/inline-enums.aliases.mjs +0 -6
- package/dist/lib/types/inline-enums.aliases.mjs.map +1 -1
- package/dist/lib/types/inline-enums.generated.mjs +0 -6
- package/dist/lib/types/inline-enums.generated.mjs.map +1 -1
- package/dist/lib/types/manifest.generated.mjs +0 -6
- package/dist/lib/types/manifest.generated.mjs.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +0 -6
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/server-payload-aliases.mjs +0 -6
- package/dist/lib/types/server-payload.mjs +0 -6
- package/dist/lib/types/sync-rows.mjs +0 -6
- package/dist/lib/types/tools.generated.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs.map +1 -1
- package/dist/lib/types/v2-5/tools.generated.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs.map +1 -1
- package/dist/lib/types/v3-1-beta/tools.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs.map +1 -1
- package/dist/lib/upstream-recorder/constants.mjs +0 -6
- package/dist/lib/upstream-recorder/constants.mjs.map +1 -1
- package/dist/lib/upstream-recorder/index.mjs +0 -6
- package/dist/lib/upstream-recorder/index.mjs.map +1 -1
- package/dist/lib/upstream-recorder/recorder.mjs +0 -6
- package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
- package/dist/lib/upstream-recorder/types.mjs +0 -6
- package/dist/lib/upstream-recorder/types.mjs.map +1 -1
- package/dist/lib/utils/a2a-artifacts.mjs +0 -6
- package/dist/lib/utils/a2a-artifacts.mjs.map +1 -1
- package/dist/lib/utils/a2a-discovery.mjs +0 -6
- package/dist/lib/utils/a2a-discovery.mjs.map +1 -1
- package/dist/lib/utils/activation-key-builders.mjs +0 -6
- package/dist/lib/utils/activation-key-builders.mjs.map +1 -1
- package/dist/lib/utils/adcp-version-config.mjs +0 -6
- package/dist/lib/utils/adcp-version-config.mjs.map +1 -1
- package/dist/lib/utils/asset-builders.mjs +0 -6
- package/dist/lib/utils/asset-builders.mjs.map +1 -1
- package/dist/lib/utils/build-creative-return-builders.mjs +0 -6
- package/dist/lib/utils/build-creative-return-builders.mjs.map +1 -1
- package/dist/lib/utils/buyer-retry-policy.mjs +0 -6
- package/dist/lib/utils/buyer-retry-policy.mjs.map +1 -1
- package/dist/lib/utils/capabilities.mjs +0 -6
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/capability-rollups.mjs +0 -6
- package/dist/lib/utils/capability-rollups.mjs.map +1 -1
- package/dist/lib/utils/creative-adapter.mjs +0 -6
- package/dist/lib/utils/creative-adapter.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +0 -6
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/deprecation.mjs +0 -6
- package/dist/lib/utils/deprecation.mjs.map +1 -1
- package/dist/lib/utils/envelope-status-compat.mjs +0 -6
- package/dist/lib/utils/envelope-status-compat.mjs.map +1 -1
- package/dist/lib/utils/error-extraction.mjs +0 -6
- package/dist/lib/utils/error-extraction.mjs.map +1 -1
- package/dist/lib/utils/extract-result.mjs +0 -6
- package/dist/lib/utils/extract-result.mjs.map +1 -1
- package/dist/lib/utils/format-asset-slot-builders.mjs +0 -6
- package/dist/lib/utils/format-asset-slot-builders.mjs.map +1 -1
- package/dist/lib/utils/format-assets.mjs +0 -6
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/format-render-builders.mjs +0 -6
- package/dist/lib/utils/format-render-builders.mjs.map +1 -1
- package/dist/lib/utils/format-renders.mjs +0 -6
- package/dist/lib/utils/format-renders.mjs.map +1 -1
- package/dist/lib/utils/get-products-cache-scope.mjs +0 -6
- package/dist/lib/utils/get-products-cache-scope.mjs.map +1 -1
- package/dist/lib/utils/glob.mjs +0 -6
- package/dist/lib/utils/glob.mjs.map +1 -1
- package/dist/lib/utils/global-async-local-storage.mjs +0 -6
- package/dist/lib/utils/global-async-local-storage.mjs.map +1 -1
- package/dist/lib/utils/idempotency.mjs +0 -6
- package/dist/lib/utils/idempotency.mjs.map +1 -1
- package/dist/lib/utils/index.mjs +0 -6
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/jcs.mjs +0 -6
- package/dist/lib/utils/jcs.mjs.map +1 -1
- package/dist/lib/utils/json-depth.mjs +0 -6
- package/dist/lib/utils/json-depth.mjs.map +1 -1
- package/dist/lib/utils/logger.mjs +0 -6
- package/dist/lib/utils/logger.mjs.map +1 -1
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs +0 -6
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs.map +1 -1
- package/dist/lib/utils/media-buy-status.mjs +0 -6
- package/dist/lib/utils/media-buy-status.mjs.map +1 -1
- package/dist/lib/utils/pagination.mjs +0 -6
- package/dist/lib/utils/pagination.mjs.map +1 -1
- package/dist/lib/utils/preview-creative-builders.mjs +0 -6
- package/dist/lib/utils/preview-creative-builders.mjs.map +1 -1
- package/dist/lib/utils/preview-normalizer.mjs +0 -6
- package/dist/lib/utils/preview-normalizer.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +0 -6
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/pricing-adapter.mjs +0 -6
- package/dist/lib/utils/pricing-adapter.mjs.map +1 -1
- package/dist/lib/utils/probe-policy.mjs +0 -6
- package/dist/lib/utils/probe-policy.mjs.map +1 -1
- package/dist/lib/utils/protocol-detection.mjs +0 -6
- package/dist/lib/utils/protocol-detection.mjs.map +1 -1
- package/dist/lib/utils/redact-secrets.mjs +0 -6
- package/dist/lib/utils/redact-secrets.mjs.map +1 -1
- package/dist/lib/utils/redis-default-prefix-warn.mjs +0 -6
- package/dist/lib/utils/redis-default-prefix-warn.mjs.map +1 -1
- package/dist/lib/utils/render-builders.mjs +0 -6
- package/dist/lib/utils/render-builders.mjs.map +1 -1
- package/dist/lib/utils/request-normalizer.mjs +0 -6
- package/dist/lib/utils/request-normalizer.mjs.map +1 -1
- package/dist/lib/utils/response-schemas.mjs +0 -6
- package/dist/lib/utils/response-schemas.mjs.map +1 -1
- package/dist/lib/utils/response-unwrapper.mjs +0 -6
- package/dist/lib/utils/response-unwrapper.mjs.map +1 -1
- package/dist/lib/utils/retry.mjs +0 -6
- package/dist/lib/utils/retry.mjs.map +1 -1
- package/dist/lib/utils/signal-discovery-helpers.mjs +0 -6
- package/dist/lib/utils/signal-discovery-helpers.mjs.map +1 -1
- package/dist/lib/utils/signal-id-builders.mjs +0 -6
- package/dist/lib/utils/signal-id-builders.mjs.map +1 -1
- package/dist/lib/utils/sync-creatives-adapter.mjs +0 -6
- package/dist/lib/utils/sync-creatives-adapter.mjs.map +1 -1
- package/dist/lib/utils/task-state.mjs +0 -6
- package/dist/lib/utils/task-state.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.mjs +0 -6
- package/dist/lib/utils/tool-request-schemas.mjs.map +1 -1
- package/dist/lib/utils/typeGuards.mjs +0 -6
- package/dist/lib/utils/typeGuards.mjs.map +1 -1
- package/dist/lib/utils/union-errors.mjs +0 -6
- package/dist/lib/utils/union-errors.mjs.map +1 -1
- package/dist/lib/utils/validate-user-agent.mjs +0 -6
- package/dist/lib/utils/validate-user-agent.mjs.map +1 -1
- package/dist/lib/v2/format-schema/fetch.mjs +0 -6
- package/dist/lib/v2/format-schema/fetch.mjs.map +1 -1
- package/dist/lib/v2/format-schema/index.mjs +0 -6
- package/dist/lib/v2/format-schema/index.mjs.map +1 -1
- package/dist/lib/v2/format-schema/regex-safety.mjs +0 -6
- package/dist/lib/v2/format-schema/regex-safety.mjs.map +1 -1
- package/dist/lib/v2/format-schema/resolver.mjs +7 -8
- package/dist/lib/v2/format-schema/resolver.mjs.map +1 -1
- package/dist/lib/v2/format-schema/sandbox-refs.mjs +0 -6
- package/dist/lib/v2/format-schema/sandbox-refs.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +0 -6
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +0 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/cache-versions.mjs +0 -6
- package/dist/lib/v2/projection/cache-versions.mjs.map +1 -1
- package/dist/lib/v2/projection/canonical-properties.mjs +5 -6
- package/dist/lib/v2/projection/canonical-properties.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +5 -6
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/constants.mjs +0 -6
- package/dist/lib/v2/projection/constants.mjs.map +1 -1
- package/dist/lib/v2/projection/index.mjs +0 -6
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/registry.mjs +5 -6
- package/dist/lib/v2/projection/registry.mjs.map +1 -1
- package/dist/lib/v2/projection/types.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +0 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +0 -6
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/formats.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/formats.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/index.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/index.mjs.map +1 -1
- package/dist/lib/validation/client-hooks.mjs +0 -6
- package/dist/lib/validation/client-hooks.mjs.map +1 -1
- package/dist/lib/validation/hints.mjs +0 -6
- package/dist/lib/validation/hints.mjs.map +1 -1
- package/dist/lib/validation/index.mjs +0 -6
- package/dist/lib/validation/index.mjs.map +1 -1
- package/dist/lib/validation/schema-errors.mjs +0 -6
- package/dist/lib/validation/schema-errors.mjs.map +1 -1
- package/dist/lib/validation/schema-loader.mjs +5 -6
- package/dist/lib/validation/schema-loader.mjs.map +1 -1
- package/dist/lib/validation/schema-validator.mjs +0 -6
- package/dist/lib/validation/schema-validator.mjs.map +1 -1
- package/dist/lib/validation/sync-creatives.mjs +0 -6
- package/dist/lib/validation/sync-creatives.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -9
- package/dist/lib/version.mjs.map +1 -1
- package/dist/lib/webhooks/index.mjs +0 -6
- package/dist/lib/webhooks/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/index.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/sync.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/sync.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/types.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/auth/oauth/authorization-required.ts"],"sourcesContent":["/**\n * Zero-config OAuth discovery + actionable authorization errors.\n *\n * When a library consumer calls an OAuth-gated MCP agent without saved\n * credentials, they get a `NeedsAuthorizationError` containing everything\n * needed to either run an interactive flow or present a clear prompt — no\n * re-probing required.\n *\n * Design note: this module only *discovers* — it never opens a browser or\n * writes state. Interactive flow handling stays in the caller (CLI or a\n * bespoke `OAuthFlowHandler`) so library behavior is predictable.\n */\nimport { ssrfSafeFetch, decodeBodyAsJsonOrText } from '../../net';\nimport { AuthenticationRequiredError, type OAuthMetadataInfo } from '../../errors';\nimport { parseWWWAuthenticate, type WWWAuthenticateChallenge } from './diagnostics';\n\n/**\n * Maximum length of an individual server-supplied string we copy into the\n * `AuthorizationRequirements` payload. Anything longer is truncated with a\n * `…` suffix so hostile agents can't smuggle megabyte error-descriptions\n * into operator-facing prompts and logs.\n */\nconst MAX_DISPLAY_STRING = 256;\n\n/**\n * Maximum number of scope strings we copy into `scopesSupported`. RFC 8414\n * allows an unbounded array; hostile servers have sent thousands.\n */\nconst MAX_SCOPES = 64;\n\n/**\n * Maximum number of auth-params we preserve under `challenge.params`. Bounds\n * memory pressure from hostile servers that pack the `WWW-Authenticate`\n * header with unknown auth-params.\n */\nconst MAX_CHALLENGE_PARAMS = 32;\n\n/**\n * Strip ASCII control characters (excluding TAB) from a server-supplied\n * string that may be shown to a human. Defeats terminal-hijack tricks like\n * `\\x1b]0;pwned\\x07` (rewrites terminal title), `\\r` (overwrites prompt),\n * and `\\x1b[2J` (clears screen). Also truncates over-long values.\n */\nfunction sanitizeDisplay(value: string): string {\n const stripped = value.replace(/[\\x00-\\x08\\x0b-\\x1f\\x7f]/g, '');\n if (stripped.length <= MAX_DISPLAY_STRING) return stripped;\n return `${stripped.slice(0, MAX_DISPLAY_STRING - 1)}…`;\n}\n\n/**\n * Sanitize the parsed `WWW-Authenticate` challenge before embedding it in a\n * `NeedsAuthorizationError`. Consumers that render `requirements.challenge`\n * to a terminal should never see control characters. Caps `params` count to\n * bound hostile servers.\n *\n * The `scheme` is already lowercased + token-constrained by the parser.\n * Param keys are also token-constrained so they're safe; only values need\n * sanitizing.\n */\nfunction sanitizeChallenge(c: WWWAuthenticateChallenge): WWWAuthenticateChallenge {\n const params: Record<string, string> = {};\n for (const [k, v] of Object.entries(c.params)) {\n if (Object.keys(params).length >= MAX_CHALLENGE_PARAMS) break;\n params[k] = sanitizeDisplay(v);\n }\n return {\n scheme: c.scheme,\n ...(c.realm !== undefined ? { realm: sanitizeDisplay(c.realm) } : {}),\n ...(c.error !== undefined ? { error: sanitizeDisplay(c.error) } : {}),\n ...(c.error_description !== undefined ? { error_description: sanitizeDisplay(c.error_description) } : {}),\n ...(c.scope !== undefined ? { scope: sanitizeDisplay(c.scope) } : {}),\n ...(c.resource_metadata !== undefined ? { resource_metadata: sanitizeDisplay(c.resource_metadata) } : {}),\n params,\n };\n}\n\n/**\n * Structured description of what an agent's authorization server requires\n * before it will accept a `tools/call`. Produced by\n * {@link discoverAuthorizationRequirements}.\n */\nexport interface AuthorizationRequirements {\n /** The agent URL we probed. */\n agentUrl: string;\n /** Resource URL the agent advertises in its `WWW-Authenticate` / PRM (RFC 9728). */\n resource?: string;\n /** URL of the protected-resource-metadata document (from `WWW-Authenticate: resource_metadata=…`). */\n resourceMetadataUrl?: string;\n /** First `authorization_servers[0]` from the protected-resource metadata. */\n authorizationServer?: string;\n /**\n * All `authorization_servers` advertised by the PRM, in declaration order.\n * The walker only probes `[0]` (PRM preference order — see RFC 9728 §3.3);\n * the full list is exposed so callers can surface multi-issuer deployments\n * in diagnostics. A length > 1 is uncommon and worth flagging to the\n * operator — typically a federation partner or a staged migration.\n */\n authorizationServers?: string[];\n /** `authorization_endpoint` from the authorization-server metadata (RFC 8414). */\n authorizationEndpoint?: string;\n /** `token_endpoint` from the authorization-server metadata. */\n tokenEndpoint?: string;\n /** `registration_endpoint` if the AS supports RFC 7591 dynamic client registration. */\n registrationEndpoint?: string;\n /** Scopes advertised by the AS (RFC 8414 `scopes_supported`). */\n scopesSupported?: string[];\n /**\n * `grant_types_supported` advertised by the AS (RFC 8414). `undefined` when\n * the AS didn't publish the field — per RFC 8414 that defaults to\n * `[\"authorization_code\", \"implicit\"]`. Clients should treat absence as\n * \"unknown, try your grant and see,\" not as proof the grant is unsupported.\n */\n grantTypesSupported?: string[];\n /** True when the AS metadata came from the OIDC fallback URL rather than RFC 8414. */\n metadataSource?: 'rfc-8414' | 'openid-configuration';\n /** Scope hinted in the `WWW-Authenticate` challenge's `scope` auth-param. */\n challengeScope?: string;\n /** Raw parsed challenge the agent returned on the 401. */\n challenge: WWWAuthenticateChallenge;\n}\n\n/**\n * Error raised when an agent returns a 401 and we do not have credentials\n * to satisfy it. Carries everything needed to run an interactive OAuth flow\n * or show the operator an actionable prompt.\n *\n * Extends {@link AuthenticationRequiredError} so existing callers that catch\n * `AuthenticationRequiredError` automatically receive the richer variant —\n * they can downcast with `instanceof NeedsAuthorizationError` when they\n * want walked discovery metadata.\n *\n * Note on `code` vs `subCode`:\n * The parent class declares `readonly code = 'AUTHENTICATION_REQUIRED'` as\n * a class field that re-initializes on every construction, so a child-side\n * override attempt is silently overwritten under `useDefineForClassFields`.\n * The narrow discriminator is exposed as `subCode` — prefer\n * `instanceof NeedsAuthorizationError` for type-narrowing and use `subCode`\n * only for structured-log keying.\n *\n * Note on `hasOAuth`:\n * The inherited getter returns `true` only when both `authorization_endpoint`\n * and `token_endpoint` were walked successfully. A partially-walked\n * requirements record (PRM reachable but AS metadata missing) still yields\n * `hasOAuth === false` even though `requirements.authorizationServer` is set.\n * Treat it as \"we have enough to start a flow,\" not \"server wants OAuth.\"\n */\nexport class NeedsAuthorizationError extends AuthenticationRequiredError {\n /** Narrow discriminator for consumers that already know about this class. */\n readonly subCode = 'needs_authorization' as const;\n readonly requirements: AuthorizationRequirements;\n\n constructor(requirements: AuthorizationRequirements, message?: string) {\n super(requirements.agentUrl, synthesizeOAuthMetadata(requirements), message ?? defaultMessage(requirements));\n this.name = 'NeedsAuthorizationError';\n this.requirements = requirements;\n // Extend the parent's `details` payload so structured-logging consumers\n // that read `err.details` (via `extractErrorInfo` or equivalent) see the\n // full walked chain, not just the synthesized one-hop oauthMetadata.\n this.details = { ...(this.details as object | undefined), requirements };\n }\n}\n\n/**\n * Build an {@link OAuthMetadataInfo} for the base-class constructor so that\n * legacy callers still see a non-empty `oauthMetadata` + working\n * `hasOAuth`/`authorizationUrl` getters on the error. When discovery didn't\n * yield enough info, return undefined and let the base class degrade\n * gracefully.\n */\nfunction synthesizeOAuthMetadata(req: AuthorizationRequirements): OAuthMetadataInfo | undefined {\n if (!req.authorizationEndpoint || !req.tokenEndpoint) return undefined;\n return {\n authorization_endpoint: req.authorizationEndpoint,\n token_endpoint: req.tokenEndpoint,\n ...(req.registrationEndpoint ? { registration_endpoint: req.registrationEndpoint } : {}),\n ...(req.authorizationServer ? { issuer: req.authorizationServer } : {}),\n };\n}\n\nfunction defaultMessage(req: AuthorizationRequirements): string {\n const parts = [`Agent ${sanitizeDisplay(req.agentUrl)} requires OAuth authorization.`];\n if (req.authorizationServer) {\n parts.push(`Authorization server: ${req.authorizationServer}.`);\n }\n if (req.challenge.error_description) {\n parts.push(`Server said: ${sanitizeDisplay(req.challenge.error_description)}.`);\n }\n parts.push('Provide an OAuthFlowHandler or run an interactive flow to complete authorization.');\n return parts.join(' ');\n}\n\n/**\n * Options for {@link discoverAuthorizationRequirements}.\n */\nexport interface DiscoverAuthorizationOptions {\n /** Allow http:// and private-IP probe targets. Default false. */\n allowPrivateIp?: boolean;\n /** Override per-probe HTTP timeout (ms). Default inherits from ssrfSafeFetch (10s). */\n timeoutMs?: number;\n /**\n * If provided, use this `WWW-Authenticate` header verbatim instead of\n * re-probing the agent. Use this when you already have a 401 response in\n * hand and want to avoid a second round-trip.\n */\n wwwAuthenticate?: string;\n}\n\n/**\n * Walk the OAuth discovery chain starting from an agent URL. Returns\n * `AuthorizationRequirements` when the agent demands OAuth, or `null` when a\n * `tools/list` call succeeds without credentials.\n *\n * Strategy:\n * 1. POST `tools/list` to the agent with no `Authorization` header.\n * 2. If 401 + `WWW-Authenticate: Bearer`: parse the challenge.\n * 3. If the challenge carries `resource_metadata=…`: GET it and read\n * `resource` + `authorization_servers`.\n * 4. For the first `authorization_servers[0]`: GET `/.well-known/oauth-authorization-server`\n * and read `authorization_endpoint`, `token_endpoint`, `registration_endpoint`,\n * `scopes_supported`.\n * 5. Return a structured record.\n *\n * Anything missing from the chain surfaces as `undefined` on the result —\n * callers can still present a partial picture without re-probing.\n */\nexport async function discoverAuthorizationRequirements(\n agentUrl: string,\n options: DiscoverAuthorizationOptions = {}\n): Promise<AuthorizationRequirements | null> {\n // `allowPrivateIp` authorizes the agent probe. Chain hops (PRM, AS\n // metadata) inherit that trust ONLY when they share the agent's origin —\n // a compromised agent that advertises a private-IP authorization server\n // on a different origin would otherwise pivot a discovery walk into the\n // host's internal network.\n const allowPrivateIpOnAgent = options.allowPrivateIp ?? false;\n const agentOrigin = originOf(agentUrl);\n const allowPrivateIpForHop = (url: string): boolean =>\n allowPrivateIpOnAgent && agentOrigin !== undefined && originOf(url) === agentOrigin;\n\n let challengeHeader = options.wwwAuthenticate;\n if (!challengeHeader) {\n const probe = await probeAgent401(agentUrl, allowPrivateIpOnAgent, options.timeoutMs);\n if (probe.status !== 401) {\n // 200, 403, 5xx, or network error — not an OAuth 401 we can act on.\n return null;\n }\n challengeHeader = probe.wwwAuthenticate;\n }\n\n const challenge = parseWWWAuthenticate(challengeHeader ?? null);\n if (!challenge || challenge.scheme !== 'bearer') {\n // A 401 without a Bearer challenge isn't our kind of problem — let it propagate.\n return null;\n }\n\n const requirements: AuthorizationRequirements = {\n agentUrl,\n resourceMetadataUrl: challenge.resource_metadata ? sanitizeDisplay(challenge.resource_metadata) : undefined,\n challengeScope: challenge.scope ? sanitizeDisplay(challenge.scope) : undefined,\n challenge: sanitizeChallenge(challenge),\n };\n\n // Walk protected-resource metadata (RFC 9728 §3). Private-IP targets are\n // only allowed when the hop's origin matches the agent's.\n if (challenge.resource_metadata && isSafeHttpUrl(challenge.resource_metadata)) {\n const prm = await fetchJson(\n challenge.resource_metadata,\n allowPrivateIpForHop(challenge.resource_metadata),\n options.timeoutMs\n );\n if (prm && typeof prm === 'object') {\n const resource = (prm as { resource?: unknown }).resource;\n const servers = (prm as { authorization_servers?: unknown }).authorization_servers;\n if (typeof resource === 'string') requirements.resource = sanitizeDisplay(resource);\n if (Array.isArray(servers)) {\n const validServers = servers\n .filter((s: unknown): s is string => typeof s === 'string' && isSafeHttpUrl(s))\n .map(sanitizeDisplay);\n if (validServers.length > 0) {\n requirements.authorizationServers = validServers;\n requirements.authorizationServer = validServers[0];\n }\n }\n }\n }\n\n // Walk authorization-server metadata using the first issuer. RFC 8414 §3\n // first, then OIDC Discovery 1.0 fallback for AS deployments (Keycloak in\n // OIDC mode, Ping, ADFS) that only publish at\n // `{issuer}/.well-known/openid-configuration`.\n if (requirements.authorizationServer) {\n const asUrl = buildAuthorizationServerMetadataUrl(requirements.authorizationServer);\n let md: Record<string, unknown> | undefined;\n let source: 'rfc-8414' | 'openid-configuration' | undefined;\n if (asUrl) {\n const res = await fetchJson(asUrl, allowPrivateIpForHop(asUrl), options.timeoutMs);\n if (res && typeof res === 'object') {\n md = res as Record<string, unknown>;\n source = 'rfc-8414';\n }\n }\n if (!md) {\n const oidcUrl = buildOidcDiscoveryUrl(requirements.authorizationServer);\n if (oidcUrl) {\n const res = await fetchJson(oidcUrl, allowPrivateIpForHop(oidcUrl), options.timeoutMs);\n if (res && typeof res === 'object') {\n md = res as Record<string, unknown>;\n source = 'openid-configuration';\n }\n }\n }\n if (md) {\n requirements.metadataSource = source;\n if (typeof md.authorization_endpoint === 'string') {\n requirements.authorizationEndpoint = sanitizeDisplay(md.authorization_endpoint);\n }\n if (typeof md.token_endpoint === 'string') {\n requirements.tokenEndpoint = sanitizeDisplay(md.token_endpoint);\n }\n if (typeof md.registration_endpoint === 'string') {\n requirements.registrationEndpoint = sanitizeDisplay(md.registration_endpoint);\n }\n if (Array.isArray(md.scopes_supported)) {\n const scopes = md.scopes_supported\n .filter((s: unknown): s is string => typeof s === 'string')\n .slice(0, MAX_SCOPES)\n .map(sanitizeDisplay);\n if (scopes.length > 0) requirements.scopesSupported = scopes;\n }\n if (Array.isArray(md.grant_types_supported)) {\n const grants = md.grant_types_supported\n .filter((g: unknown): g is string => typeof g === 'string')\n .slice(0, 32)\n .map(sanitizeDisplay);\n if (grants.length > 0) requirements.grantTypesSupported = grants;\n }\n }\n }\n\n return requirements;\n}\n\n/**\n * Probe the agent for the parsed `WWW-Authenticate` challenge on a 401.\n *\n * Where `discoverAuthorizationRequirements` walks the full RFC 9728 → 8414\n * chain for Bearer/OAuth challenges, this helper is the catch-all: it works\n * for ANY scheme (Basic, Digest, Bearer-without-PRM, …) and returns the\n * structured challenge so callers can surface scheme-specific remediation\n * (e.g. \"use `--auth-scheme basic`\" when the gateway speaks RFC 7617).\n *\n * Returns `null` when the agent isn't responding with a 401, or when the\n * response carries no parseable `WWW-Authenticate`. Reuses `probeAgent401`\n * so the SSRF gate and timeout policy stay consistent with the OAuth path.\n */\nexport async function probeAuthChallenge(\n agentUrl: string,\n options: { allowPrivateIp?: boolean; timeoutMs?: number } = {}\n): Promise<WWWAuthenticateChallenge | null> {\n const probe = await probeAgent401(agentUrl, options.allowPrivateIp ?? false, options.timeoutMs);\n if (probe.status !== 401) return null;\n return parseWWWAuthenticate(probe.wwwAuthenticate ?? null);\n}\n\n/**\n * Fire a single unauthenticated `tools/list` at the agent and return the\n * status + `WWW-Authenticate` header. Used when the caller hasn't provided\n * a cached 401 response.\n */\nasync function probeAgent401(\n agentUrl: string,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<{ status: number; wwwAuthenticate?: string }> {\n const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' });\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n body,\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return { status: res.status, wwwAuthenticate: res.headers['www-authenticate'] };\n } catch {\n return { status: 0 };\n }\n}\n\n/**\n * Check a URL is an absolute `http:`/`https:` URL before we hand it to\n * `ssrfSafeFetch`. Defeats `javascript:`, `file:`, `data:`, etc. the agent\n * might echo back in PRM or AS metadata. `ssrfSafeFetch` refuses non-HTTP\n * schemes too, but failing fast here gives a cleaner discovery flow and\n * avoids surfacing scheme errors as mysterious discovery misses.\n */\nfunction originOf(value: string): string | undefined {\n try {\n return new URL(value).origin;\n } catch {\n return undefined;\n }\n}\n\nfunction isSafeHttpUrl(value: string): boolean {\n try {\n const u = new URL(value);\n return u.protocol === 'https:' || u.protocol === 'http:';\n } catch {\n return false;\n }\n}\n\n/**\n * Build the authorization-server metadata URL for an issuer per RFC 8414 §3.\n *\n * For issuer `https://as.example.com/tenant1`, metadata lives at\n * `https://as.example.com/.well-known/oauth-authorization-server/tenant1`\n * (well-known prefix inserted BEFORE the issuer path, not after). This\n * matches how the MCP SDK and most authorization servers advertise metadata.\n */\nfunction buildAuthorizationServerMetadataUrl(issuer: string): string | undefined {\n if (!isSafeHttpUrl(issuer)) return undefined;\n const u = new URL(issuer);\n const pathname = u.pathname.endsWith('/') ? u.pathname.slice(0, -1) : u.pathname;\n return `${u.origin}/.well-known/oauth-authorization-server${pathname}`;\n}\n\n/**\n * OpenID Connect Discovery 1.0 fallback URL. Some authorization servers\n * (Keycloak in OIDC mode, Ping, ADFS) only publish metadata at\n * `{issuer}/.well-known/openid-configuration` — path *suffixed*, not\n * prefixed. The OIDC discovery doc carries `token_endpoint`,\n * `authorization_endpoint`, and `grant_types_supported` with the same\n * semantics as RFC 8414, so it's a safe read-through when the RFC 8414\n * probe misses.\n */\nfunction buildOidcDiscoveryUrl(issuer: string): string | undefined {\n if (!isSafeHttpUrl(issuer)) return undefined;\n const u = new URL(issuer);\n const base = u.pathname.endsWith('/') ? `${u.origin}${u.pathname.slice(0, -1)}` : `${u.origin}${u.pathname}`;\n return `${base}/.well-known/openid-configuration`;\n}\n\nasync function fetchJson(url: string, allowPrivateIp: boolean, timeoutMs?: number): Promise<unknown> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n if (res.status !== 200) return undefined;\n return decodeBodyAsJsonOrText(res.body, res.headers['content-type']);\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/auth/oauth/authorization-required.ts"],"sourcesContent":["/**\n * Zero-config OAuth discovery + actionable authorization errors.\n *\n * When a library consumer calls an OAuth-gated MCP agent without saved\n * credentials, they get a `NeedsAuthorizationError` containing everything\n * needed to either run an interactive flow or present a clear prompt — no\n * re-probing required.\n *\n * Design note: this module only *discovers* — it never opens a browser or\n * writes state. Interactive flow handling stays in the caller (CLI or a\n * bespoke `OAuthFlowHandler`) so library behavior is predictable.\n */\nimport { ssrfSafeFetch, decodeBodyAsJsonOrText } from '../../net';\nimport { AuthenticationRequiredError, type OAuthMetadataInfo } from '../../errors';\nimport { parseWWWAuthenticate, type WWWAuthenticateChallenge } from './diagnostics';\n\n/**\n * Maximum length of an individual server-supplied string we copy into the\n * `AuthorizationRequirements` payload. Anything longer is truncated with a\n * `…` suffix so hostile agents can't smuggle megabyte error-descriptions\n * into operator-facing prompts and logs.\n */\nconst MAX_DISPLAY_STRING = 256;\n\n/**\n * Maximum number of scope strings we copy into `scopesSupported`. RFC 8414\n * allows an unbounded array; hostile servers have sent thousands.\n */\nconst MAX_SCOPES = 64;\n\n/**\n * Maximum number of auth-params we preserve under `challenge.params`. Bounds\n * memory pressure from hostile servers that pack the `WWW-Authenticate`\n * header with unknown auth-params.\n */\nconst MAX_CHALLENGE_PARAMS = 32;\n\n/**\n * Strip ASCII control characters (excluding TAB) from a server-supplied\n * string that may be shown to a human. Defeats terminal-hijack tricks like\n * `\\x1b]0;pwned\\x07` (rewrites terminal title), `\\r` (overwrites prompt),\n * and `\\x1b[2J` (clears screen). Also truncates over-long values.\n */\nfunction sanitizeDisplay(value: string): string {\n const stripped = value.replace(/[\\x00-\\x08\\x0b-\\x1f\\x7f]/g, '');\n if (stripped.length <= MAX_DISPLAY_STRING) return stripped;\n return `${stripped.slice(0, MAX_DISPLAY_STRING - 1)}…`;\n}\n\n/**\n * Sanitize the parsed `WWW-Authenticate` challenge before embedding it in a\n * `NeedsAuthorizationError`. Consumers that render `requirements.challenge`\n * to a terminal should never see control characters. Caps `params` count to\n * bound hostile servers.\n *\n * The `scheme` is already lowercased + token-constrained by the parser.\n * Param keys are also token-constrained so they're safe; only values need\n * sanitizing.\n */\nfunction sanitizeChallenge(c: WWWAuthenticateChallenge): WWWAuthenticateChallenge {\n const params: Record<string, string> = {};\n for (const [k, v] of Object.entries(c.params)) {\n if (Object.keys(params).length >= MAX_CHALLENGE_PARAMS) break;\n params[k] = sanitizeDisplay(v);\n }\n return {\n scheme: c.scheme,\n ...(c.realm !== undefined ? { realm: sanitizeDisplay(c.realm) } : {}),\n ...(c.error !== undefined ? { error: sanitizeDisplay(c.error) } : {}),\n ...(c.error_description !== undefined ? { error_description: sanitizeDisplay(c.error_description) } : {}),\n ...(c.scope !== undefined ? { scope: sanitizeDisplay(c.scope) } : {}),\n ...(c.resource_metadata !== undefined ? { resource_metadata: sanitizeDisplay(c.resource_metadata) } : {}),\n params,\n };\n}\n\n/**\n * Structured description of what an agent's authorization server requires\n * before it will accept a `tools/call`. Produced by\n * {@link discoverAuthorizationRequirements}.\n */\nexport interface AuthorizationRequirements {\n /** The agent URL we probed. */\n agentUrl: string;\n /** Resource URL the agent advertises in its `WWW-Authenticate` / PRM (RFC 9728). */\n resource?: string;\n /** URL of the protected-resource-metadata document (from `WWW-Authenticate: resource_metadata=…`). */\n resourceMetadataUrl?: string;\n /** First `authorization_servers[0]` from the protected-resource metadata. */\n authorizationServer?: string;\n /**\n * All `authorization_servers` advertised by the PRM, in declaration order.\n * The walker only probes `[0]` (PRM preference order — see RFC 9728 §3.3);\n * the full list is exposed so callers can surface multi-issuer deployments\n * in diagnostics. A length > 1 is uncommon and worth flagging to the\n * operator — typically a federation partner or a staged migration.\n */\n authorizationServers?: string[];\n /** `authorization_endpoint` from the authorization-server metadata (RFC 8414). */\n authorizationEndpoint?: string;\n /** `token_endpoint` from the authorization-server metadata. */\n tokenEndpoint?: string;\n /** `registration_endpoint` if the AS supports RFC 7591 dynamic client registration. */\n registrationEndpoint?: string;\n /** Scopes advertised by the AS (RFC 8414 `scopes_supported`). */\n scopesSupported?: string[];\n /**\n * `grant_types_supported` advertised by the AS (RFC 8414). `undefined` when\n * the AS didn't publish the field — per RFC 8414 that defaults to\n * `[\"authorization_code\", \"implicit\"]`. Clients should treat absence as\n * \"unknown, try your grant and see,\" not as proof the grant is unsupported.\n */\n grantTypesSupported?: string[];\n /** True when the AS metadata came from the OIDC fallback URL rather than RFC 8414. */\n metadataSource?: 'rfc-8414' | 'openid-configuration';\n /** Scope hinted in the `WWW-Authenticate` challenge's `scope` auth-param. */\n challengeScope?: string;\n /** Raw parsed challenge the agent returned on the 401. */\n challenge: WWWAuthenticateChallenge;\n}\n\n/**\n * Error raised when an agent returns a 401 and we do not have credentials\n * to satisfy it. Carries everything needed to run an interactive OAuth flow\n * or show the operator an actionable prompt.\n *\n * Extends {@link AuthenticationRequiredError} so existing callers that catch\n * `AuthenticationRequiredError` automatically receive the richer variant —\n * they can downcast with `instanceof NeedsAuthorizationError` when they\n * want walked discovery metadata.\n *\n * Note on `code` vs `subCode`:\n * The parent class declares `readonly code = 'AUTHENTICATION_REQUIRED'` as\n * a class field that re-initializes on every construction, so a child-side\n * override attempt is silently overwritten under `useDefineForClassFields`.\n * The narrow discriminator is exposed as `subCode` — prefer\n * `instanceof NeedsAuthorizationError` for type-narrowing and use `subCode`\n * only for structured-log keying.\n *\n * Note on `hasOAuth`:\n * The inherited getter returns `true` only when both `authorization_endpoint`\n * and `token_endpoint` were walked successfully. A partially-walked\n * requirements record (PRM reachable but AS metadata missing) still yields\n * `hasOAuth === false` even though `requirements.authorizationServer` is set.\n * Treat it as \"we have enough to start a flow,\" not \"server wants OAuth.\"\n */\nexport class NeedsAuthorizationError extends AuthenticationRequiredError {\n /** Narrow discriminator for consumers that already know about this class. */\n readonly subCode = 'needs_authorization' as const;\n readonly requirements: AuthorizationRequirements;\n\n constructor(requirements: AuthorizationRequirements, message?: string) {\n super(requirements.agentUrl, synthesizeOAuthMetadata(requirements), message ?? defaultMessage(requirements));\n this.name = 'NeedsAuthorizationError';\n this.requirements = requirements;\n // Extend the parent's `details` payload so structured-logging consumers\n // that read `err.details` (via `extractErrorInfo` or equivalent) see the\n // full walked chain, not just the synthesized one-hop oauthMetadata.\n this.details = { ...(this.details as object | undefined), requirements };\n }\n}\n\n/**\n * Build an {@link OAuthMetadataInfo} for the base-class constructor so that\n * legacy callers still see a non-empty `oauthMetadata` + working\n * `hasOAuth`/`authorizationUrl` getters on the error. When discovery didn't\n * yield enough info, return undefined and let the base class degrade\n * gracefully.\n */\nfunction synthesizeOAuthMetadata(req: AuthorizationRequirements): OAuthMetadataInfo | undefined {\n if (!req.authorizationEndpoint || !req.tokenEndpoint) return undefined;\n return {\n authorization_endpoint: req.authorizationEndpoint,\n token_endpoint: req.tokenEndpoint,\n ...(req.registrationEndpoint ? { registration_endpoint: req.registrationEndpoint } : {}),\n ...(req.authorizationServer ? { issuer: req.authorizationServer } : {}),\n };\n}\n\nfunction defaultMessage(req: AuthorizationRequirements): string {\n const parts = [`Agent ${sanitizeDisplay(req.agentUrl)} requires OAuth authorization.`];\n if (req.authorizationServer) {\n parts.push(`Authorization server: ${req.authorizationServer}.`);\n }\n if (req.challenge.error_description) {\n parts.push(`Server said: ${sanitizeDisplay(req.challenge.error_description)}.`);\n }\n parts.push('Provide an OAuthFlowHandler or run an interactive flow to complete authorization.');\n return parts.join(' ');\n}\n\n/**\n * Options for {@link discoverAuthorizationRequirements}.\n */\nexport interface DiscoverAuthorizationOptions {\n /** Allow http:// and private-IP probe targets. Default false. */\n allowPrivateIp?: boolean;\n /** Override per-probe HTTP timeout (ms). Default inherits from ssrfSafeFetch (10s). */\n timeoutMs?: number;\n /**\n * If provided, use this `WWW-Authenticate` header verbatim instead of\n * re-probing the agent. Use this when you already have a 401 response in\n * hand and want to avoid a second round-trip.\n */\n wwwAuthenticate?: string;\n}\n\n/**\n * Walk the OAuth discovery chain starting from an agent URL. Returns\n * `AuthorizationRequirements` when the agent demands OAuth, or `null` when a\n * `tools/list` call succeeds without credentials.\n *\n * Strategy:\n * 1. POST `tools/list` to the agent with no `Authorization` header.\n * 2. If 401 + `WWW-Authenticate: Bearer`: parse the challenge.\n * 3. If the challenge carries `resource_metadata=…`: GET it and read\n * `resource` + `authorization_servers`.\n * 4. For the first `authorization_servers[0]`: GET `/.well-known/oauth-authorization-server`\n * and read `authorization_endpoint`, `token_endpoint`, `registration_endpoint`,\n * `scopes_supported`.\n * 5. Return a structured record.\n *\n * Anything missing from the chain surfaces as `undefined` on the result —\n * callers can still present a partial picture without re-probing.\n */\nexport async function discoverAuthorizationRequirements(\n agentUrl: string,\n options: DiscoverAuthorizationOptions = {}\n): Promise<AuthorizationRequirements | null> {\n // `allowPrivateIp` authorizes the agent probe. Chain hops (PRM, AS\n // metadata) inherit that trust ONLY when they share the agent's origin —\n // a compromised agent that advertises a private-IP authorization server\n // on a different origin would otherwise pivot a discovery walk into the\n // host's internal network.\n const allowPrivateIpOnAgent = options.allowPrivateIp ?? false;\n const agentOrigin = originOf(agentUrl);\n const allowPrivateIpForHop = (url: string): boolean =>\n allowPrivateIpOnAgent && agentOrigin !== undefined && originOf(url) === agentOrigin;\n\n let challengeHeader = options.wwwAuthenticate;\n if (!challengeHeader) {\n const probe = await probeAgent401(agentUrl, allowPrivateIpOnAgent, options.timeoutMs);\n if (probe.status !== 401) {\n // 200, 403, 5xx, or network error — not an OAuth 401 we can act on.\n return null;\n }\n challengeHeader = probe.wwwAuthenticate;\n }\n\n const challenge = parseWWWAuthenticate(challengeHeader ?? null);\n if (!challenge || challenge.scheme !== 'bearer') {\n // A 401 without a Bearer challenge isn't our kind of problem — let it propagate.\n return null;\n }\n\n const requirements: AuthorizationRequirements = {\n agentUrl,\n resourceMetadataUrl: challenge.resource_metadata ? sanitizeDisplay(challenge.resource_metadata) : undefined,\n challengeScope: challenge.scope ? sanitizeDisplay(challenge.scope) : undefined,\n challenge: sanitizeChallenge(challenge),\n };\n\n // Walk protected-resource metadata (RFC 9728 §3). Private-IP targets are\n // only allowed when the hop's origin matches the agent's.\n if (challenge.resource_metadata && isSafeHttpUrl(challenge.resource_metadata)) {\n const prm = await fetchJson(\n challenge.resource_metadata,\n allowPrivateIpForHop(challenge.resource_metadata),\n options.timeoutMs\n );\n if (prm && typeof prm === 'object') {\n const resource = (prm as { resource?: unknown }).resource;\n const servers = (prm as { authorization_servers?: unknown }).authorization_servers;\n if (typeof resource === 'string') requirements.resource = sanitizeDisplay(resource);\n if (Array.isArray(servers)) {\n const validServers = servers\n .filter((s: unknown): s is string => typeof s === 'string' && isSafeHttpUrl(s))\n .map(sanitizeDisplay);\n if (validServers.length > 0) {\n requirements.authorizationServers = validServers;\n requirements.authorizationServer = validServers[0];\n }\n }\n }\n }\n\n // Walk authorization-server metadata using the first issuer. RFC 8414 §3\n // first, then OIDC Discovery 1.0 fallback for AS deployments (Keycloak in\n // OIDC mode, Ping, ADFS) that only publish at\n // `{issuer}/.well-known/openid-configuration`.\n if (requirements.authorizationServer) {\n const asUrl = buildAuthorizationServerMetadataUrl(requirements.authorizationServer);\n let md: Record<string, unknown> | undefined;\n let source: 'rfc-8414' | 'openid-configuration' | undefined;\n if (asUrl) {\n const res = await fetchJson(asUrl, allowPrivateIpForHop(asUrl), options.timeoutMs);\n if (res && typeof res === 'object') {\n md = res as Record<string, unknown>;\n source = 'rfc-8414';\n }\n }\n if (!md) {\n const oidcUrl = buildOidcDiscoveryUrl(requirements.authorizationServer);\n if (oidcUrl) {\n const res = await fetchJson(oidcUrl, allowPrivateIpForHop(oidcUrl), options.timeoutMs);\n if (res && typeof res === 'object') {\n md = res as Record<string, unknown>;\n source = 'openid-configuration';\n }\n }\n }\n if (md) {\n requirements.metadataSource = source;\n if (typeof md.authorization_endpoint === 'string') {\n requirements.authorizationEndpoint = sanitizeDisplay(md.authorization_endpoint);\n }\n if (typeof md.token_endpoint === 'string') {\n requirements.tokenEndpoint = sanitizeDisplay(md.token_endpoint);\n }\n if (typeof md.registration_endpoint === 'string') {\n requirements.registrationEndpoint = sanitizeDisplay(md.registration_endpoint);\n }\n if (Array.isArray(md.scopes_supported)) {\n const scopes = md.scopes_supported\n .filter((s: unknown): s is string => typeof s === 'string')\n .slice(0, MAX_SCOPES)\n .map(sanitizeDisplay);\n if (scopes.length > 0) requirements.scopesSupported = scopes;\n }\n if (Array.isArray(md.grant_types_supported)) {\n const grants = md.grant_types_supported\n .filter((g: unknown): g is string => typeof g === 'string')\n .slice(0, 32)\n .map(sanitizeDisplay);\n if (grants.length > 0) requirements.grantTypesSupported = grants;\n }\n }\n }\n\n return requirements;\n}\n\n/**\n * Probe the agent for the parsed `WWW-Authenticate` challenge on a 401.\n *\n * Where `discoverAuthorizationRequirements` walks the full RFC 9728 → 8414\n * chain for Bearer/OAuth challenges, this helper is the catch-all: it works\n * for ANY scheme (Basic, Digest, Bearer-without-PRM, …) and returns the\n * structured challenge so callers can surface scheme-specific remediation\n * (e.g. \"use `--auth-scheme basic`\" when the gateway speaks RFC 7617).\n *\n * Returns `null` when the agent isn't responding with a 401, or when the\n * response carries no parseable `WWW-Authenticate`. Reuses `probeAgent401`\n * so the SSRF gate and timeout policy stay consistent with the OAuth path.\n */\nexport async function probeAuthChallenge(\n agentUrl: string,\n options: { allowPrivateIp?: boolean; timeoutMs?: number } = {}\n): Promise<WWWAuthenticateChallenge | null> {\n const probe = await probeAgent401(agentUrl, options.allowPrivateIp ?? false, options.timeoutMs);\n if (probe.status !== 401) return null;\n return parseWWWAuthenticate(probe.wwwAuthenticate ?? null);\n}\n\n/**\n * Fire a single unauthenticated `tools/list` at the agent and return the\n * status + `WWW-Authenticate` header. Used when the caller hasn't provided\n * a cached 401 response.\n */\nasync function probeAgent401(\n agentUrl: string,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<{ status: number; wwwAuthenticate?: string }> {\n const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' });\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n body,\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return { status: res.status, wwwAuthenticate: res.headers['www-authenticate'] };\n } catch {\n return { status: 0 };\n }\n}\n\n/**\n * Check a URL is an absolute `http:`/`https:` URL before we hand it to\n * `ssrfSafeFetch`. Defeats `javascript:`, `file:`, `data:`, etc. the agent\n * might echo back in PRM or AS metadata. `ssrfSafeFetch` refuses non-HTTP\n * schemes too, but failing fast here gives a cleaner discovery flow and\n * avoids surfacing scheme errors as mysterious discovery misses.\n */\nfunction originOf(value: string): string | undefined {\n try {\n return new URL(value).origin;\n } catch {\n return undefined;\n }\n}\n\nfunction isSafeHttpUrl(value: string): boolean {\n try {\n const u = new URL(value);\n return u.protocol === 'https:' || u.protocol === 'http:';\n } catch {\n return false;\n }\n}\n\n/**\n * Build the authorization-server metadata URL for an issuer per RFC 8414 §3.\n *\n * For issuer `https://as.example.com/tenant1`, metadata lives at\n * `https://as.example.com/.well-known/oauth-authorization-server/tenant1`\n * (well-known prefix inserted BEFORE the issuer path, not after). This\n * matches how the MCP SDK and most authorization servers advertise metadata.\n */\nfunction buildAuthorizationServerMetadataUrl(issuer: string): string | undefined {\n if (!isSafeHttpUrl(issuer)) return undefined;\n const u = new URL(issuer);\n const pathname = u.pathname.endsWith('/') ? u.pathname.slice(0, -1) : u.pathname;\n return `${u.origin}/.well-known/oauth-authorization-server${pathname}`;\n}\n\n/**\n * OpenID Connect Discovery 1.0 fallback URL. Some authorization servers\n * (Keycloak in OIDC mode, Ping, ADFS) only publish metadata at\n * `{issuer}/.well-known/openid-configuration` — path *suffixed*, not\n * prefixed. The OIDC discovery doc carries `token_endpoint`,\n * `authorization_endpoint`, and `grant_types_supported` with the same\n * semantics as RFC 8414, so it's a safe read-through when the RFC 8414\n * probe misses.\n */\nfunction buildOidcDiscoveryUrl(issuer: string): string | undefined {\n if (!isSafeHttpUrl(issuer)) return undefined;\n const u = new URL(issuer);\n const base = u.pathname.endsWith('/') ? `${u.origin}${u.pathname.slice(0, -1)}` : `${u.origin}${u.pathname}`;\n return `${base}/.well-known/openid-configuration`;\n}\n\nasync function fetchJson(url: string, allowPrivateIp: boolean, timeoutMs?: number): Promise<unknown> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n if (res.status !== 200) return undefined;\n return decodeBodyAsJsonOrText(res.body, res.headers['content-type']);\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { ssrfSafeFetch, decodeBodyAsJsonOrText, SsrfRefusedError } from "../../net/index.mjs";
|
|
8
2
|
import { decodeAccessTokenClaims, parseWWWAuthenticate, validateTokenAudience } from "./diagnostics.mjs";
|
|
9
3
|
const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/auth/oauth/diagnose.ts"],"sourcesContent":["/**\n * `adcp diagnose-auth` — structured OAuth handshake diagnostics.\n *\n * Performs an end-to-end trace of the OAuth flow for a single MCP agent and\n * emits a structured {@link AuthDiagnosisReport} with wire-level HTTP captures,\n * decoded token claims, and ranked hypotheses about what might be wrong.\n *\n * Designed for incident diagnosis, not continuous monitoring — each run does\n * a full round-trip including (optionally) a token refresh and a `tools/call`\n * attempt, so don't call this in a tight loop.\n *\n * The runner is decoupled from the CLI so library consumers can invoke it\n * programmatically (e.g. from a monitoring agent or a test-controller fixture)\n * and render their own output.\n */\nimport { ssrfSafeFetch, decodeBodyAsJsonOrText, SsrfRefusedError } from '../../net';\nimport type { AgentConfig } from './types';\nimport { decodeAccessTokenClaims, parseWWWAuthenticate, validateTokenAudience } from './diagnostics';\nimport type { DecodedAccessToken } from './diagnostics';\n\n/**\n * Options for {@link runAuthDiagnosis}. Most callers pass an `AgentConfig` and\n * accept the defaults.\n */\nexport interface DiagnoseOptions {\n /** Allow http:// and private-IP probe targets. Default false. */\n allowPrivateIp?: boolean;\n /** Skip the `tools/call` probe (e.g. if the agent doesn't expose a no-op tool). */\n skipToolCall?: boolean;\n /**\n * Name of the tool to exercise in the `tools/call` probe. Default: `get_products`.\n * This default is AdCP-specific; for non-AdCP MCP agents, pass a tool name that exists.\n */\n probeToolName?: string;\n /** Arguments to send with the probe tool call. Default: `{ brief: 'diagnose-auth probe' }`. */\n probeToolArgs?: Record<string, unknown>;\n /** Skip the token-refresh attempt even when a refresh_token is available. */\n skipRefresh?: boolean;\n /** Replace the default timeout for individual HTTP probes (ms). Default inherits from ssrfSafeFetch (10s). */\n timeoutMs?: number;\n /**\n * Include raw access_token / refresh_token / id_token values in the report.\n * Default false — tokens are replaced with `<redacted length=N>` markers so\n * that `--json` output is safe to paste into bug reports and log aggregators.\n */\n includeTokens?: boolean;\n}\n\n/** Wire-level capture of a single HTTP probe. */\nexport interface HttpCapture {\n url: string;\n method: string;\n /** 0 if the request never left the client (DNS failure, SSRF refusal, …). */\n status: number;\n /** Response headers with lowercased keys. */\n headers: Record<string, string>;\n /** Parsed JSON body if `content-type` is JSON, raw text otherwise, or `null` on error. */\n body: unknown;\n /** Client-side error message, if any. */\n error?: string;\n}\n\n/** One step of the diagnosis, in execution order. */\nexport interface DiagnosisStep {\n name:\n | 'probe_protected_resource_metadata'\n | 'probe_authorization_server_metadata'\n | 'decode_current_token'\n | 'token_refresh_attempt'\n | 'decode_refreshed_token'\n | 'list_tools_probe'\n | 'tool_call_probe';\n /** Present for HTTP steps. */\n http?: HttpCapture;\n /** Present for decode steps. Claims are unverified. */\n decodedToken?: DecodedAccessToken | null;\n /** Free-form notes attached by the step for hypothesis ranking. */\n notes?: string[];\n /** Step-level error (distinct from `http.error` — set when the step was skipped or could not run). */\n error?: string;\n}\n\n/** Rank verdict for a hypothesis. Ordered most-to-least actionable. */\nexport type HypothesisVerdict = 'likely' | 'possible' | 'ruled_out' | 'not_observed';\n\n/** One ranked hypothesis about what might be wrong. */\nexport interface Hypothesis {\n /**\n * Stable ID. Currently `H1`, `H2`, `H4`, `H5`, `H6` — `H3` (session ID handling)\n * is reserved for a future addition. Downstream consumers should treat this as\n * an opaque string rather than exhaustively switching on it.\n */\n id: string;\n /** Short human-readable title. */\n title: string;\n /** One-line summary, tailored with evidence from this run. */\n summary: string;\n verdict: HypothesisVerdict;\n /** Supporting evidence extracted from the steps above. */\n evidence: string[];\n}\n\n/**\n * Report schema version. Bumped when breaking changes to the shape of\n * {@link AuthDiagnosisReport} land. Dashboards and downstream consumers should\n * check this before relying on specific fields.\n */\nexport const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1 as const;\n\n/** Full diagnosis report returned by {@link runAuthDiagnosis}. */\nexport interface AuthDiagnosisReport {\n /** See {@link AUTH_DIAGNOSIS_SCHEMA_VERSION}. */\n schemaVersion: typeof AUTH_DIAGNOSIS_SCHEMA_VERSION;\n /** Agent URL under test. */\n agentUrl: string;\n /** Alias from saved config, if one was provided. */\n aliasId?: string;\n /** Per-step wire captures, in execution order. */\n steps: DiagnosisStep[];\n /** Ranked hypotheses — `likely` first, then `possible`, then `ruled_out`. */\n hypotheses: Hypothesis[];\n /** Report generation timestamp (ISO-8601, UTC). */\n generatedAt: string;\n}\n\n/**\n * Run the full diagnosis. Pass an agent config with (optionally) saved\n * `oauth_tokens` to exercise a realistic handshake. Returns a structured\n * report — rendering is the caller's job (CLI renders a text summary;\n * programmatic consumers typically serialize to JSON).\n */\nexport async function runAuthDiagnosis(\n agent: AgentConfig,\n options: DiagnoseOptions = {}\n): Promise<AuthDiagnosisReport> {\n const steps: DiagnosisStep[] = [];\n const agentUrl = agent.agent_uri;\n const allowPrivateIp = options.allowPrivateIp ?? false;\n const includeTokens = options.includeTokens ?? false;\n // Per-invocation RPC id counter so concurrent `runAuthDiagnosis` calls don't share state.\n let rpcId = 0;\n const nextRpcId = () => ++rpcId;\n\n // Step 1: protected-resource metadata (RFC 9728)\n const prmCapture = await probeProtectedResourceMetadata(agentUrl, allowPrivateIp, options.timeoutMs);\n steps.push({ name: 'probe_protected_resource_metadata', http: prmCapture });\n\n // Step 2: authorization-server metadata (RFC 8414) — derives issuer from PRM\n const asCapture = await probeAuthorizationServerMetadata(prmCapture, allowPrivateIp, options.timeoutMs);\n steps.push({ name: 'probe_authorization_server_metadata', http: asCapture });\n\n // Step 3: decode the current access token (if saved)\n const currentToken = agent.oauth_tokens?.access_token;\n const currentDecoded = currentToken ? decodeAccessTokenClaims(currentToken) : null;\n steps.push({\n name: 'decode_current_token',\n decodedToken: currentDecoded,\n notes: currentToken\n ? currentDecoded\n ? [`Decoded JWT with claims: ${Object.keys(currentDecoded.claims).join(', ')}`]\n : ['Saved access_token is opaque (not a JWT) — `aud` inspection is not possible']\n : ['No saved access_token'],\n });\n\n // Step 4: token refresh (only if we have a refresh_token AND the user didn't skip)\n let refreshedAccessToken: string | undefined;\n let refreshedDecoded: DecodedAccessToken | null = null;\n if (!options.skipRefresh && agent.oauth_tokens?.refresh_token) {\n const tokenEndpoint = extractTokenEndpoint(asCapture);\n const clientId = agent.oauth_client?.client_id;\n // Always request the agent URL as the resource indicator, even if PRM\n // advertises something different — a well-behaved client sends what it\n // actually wants to talk to, and using PRM.resource here would cause H2\n // to fire spuriously when the real problem is H1.\n const resource = agentUrl;\n if (!tokenEndpoint) {\n steps.push({\n name: 'token_refresh_attempt',\n error: 'No token_endpoint available from authorization-server metadata — cannot attempt refresh',\n });\n } else if (!clientId) {\n steps.push({\n name: 'token_refresh_attempt',\n error: 'No saved oauth_client.client_id — cannot attempt refresh without it',\n });\n } else {\n const refreshCapture = await attemptTokenRefresh({\n tokenEndpoint,\n clientId,\n clientSecret: agent.oauth_client?.client_secret,\n refreshToken: agent.oauth_tokens.refresh_token,\n resource,\n allowPrivateIp,\n timeoutMs: options.timeoutMs,\n });\n // Capture the raw access_token string BEFORE redaction so we can still\n // decode claims for H2 analysis. The report body itself uses the\n // redacted capture unless includeTokens is set.\n if (refreshCapture.status === 200 && refreshCapture.body && typeof refreshCapture.body === 'object') {\n const body = refreshCapture.body as Record<string, unknown>;\n if (typeof body.access_token === 'string') {\n refreshedAccessToken = body.access_token;\n refreshedDecoded = decodeAccessTokenClaims(refreshedAccessToken);\n }\n }\n steps.push({\n name: 'token_refresh_attempt',\n http: includeTokens ? refreshCapture : redactTokenMaterial(refreshCapture),\n });\n if (refreshedAccessToken) {\n steps.push({\n name: 'decode_refreshed_token',\n decodedToken: refreshedDecoded,\n notes: refreshedDecoded\n ? [`Decoded refreshed JWT with claims: ${Object.keys(refreshedDecoded.claims).join(', ')}`]\n : ['Refreshed access_token is opaque (not a JWT)'],\n });\n }\n }\n }\n\n // Step 5: unauthenticated list_tools to surface 401 + WWW-Authenticate\n const listToolsCapture = await probeListTools(agentUrl, undefined, allowPrivateIp, options.timeoutMs, nextRpcId);\n steps.push({ name: 'list_tools_probe', http: listToolsCapture });\n\n // Step 6: authenticated tool_call (skip if no token or if caller opts out)\n const tokenForCall = refreshedAccessToken ?? currentToken;\n if (!options.skipToolCall && tokenForCall) {\n const toolName = options.probeToolName ?? 'get_products';\n const args = options.probeToolArgs ?? { brief: 'diagnose-auth probe' };\n const toolCapture = await probeToolCall(\n agentUrl,\n tokenForCall,\n toolName,\n args,\n allowPrivateIp,\n options.timeoutMs,\n nextRpcId\n );\n steps.push({ name: 'tool_call_probe', http: toolCapture });\n } else if (!tokenForCall) {\n steps.push({\n name: 'tool_call_probe',\n error: 'No access_token available — skipping authenticated tool call probe',\n });\n }\n\n const hypotheses = rankHypotheses({\n agentUrl,\n steps,\n currentDecoded,\n refreshedDecoded,\n });\n\n return {\n schemaVersion: AUTH_DIAGNOSIS_SCHEMA_VERSION,\n agentUrl,\n aliasId: agent.id,\n steps,\n hypotheses,\n generatedAt: new Date().toISOString(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Probes\n// ---------------------------------------------------------------------------\n\nasync function probeProtectedResourceMetadata(\n agentUrl: string,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<HttpCapture> {\n const u = new URL(agentUrl);\n const url = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n return httpGet(url, allowPrivateIp, timeoutMs);\n}\n\nasync function probeAuthorizationServerMetadata(\n prm: HttpCapture,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<HttpCapture> {\n const issuer = extractIssuer(prm);\n if (!issuer) {\n return {\n url: '',\n method: 'GET',\n status: 0,\n headers: {},\n body: null,\n error: 'protected-resource metadata did not yield an authorization_servers[0] entry',\n };\n }\n const url = `${issuer.replace(/\\/$/, '')}/.well-known/oauth-authorization-server`;\n return httpGet(url, allowPrivateIp, timeoutMs);\n}\n\nasync function probeListTools(\n agentUrl: string,\n bearerToken: string | undefined,\n allowPrivateIp: boolean,\n timeoutMs: number | undefined,\n nextRpcId: () => number\n): Promise<HttpCapture> {\n return httpJsonRpc(agentUrl, { method: 'tools/list' }, bearerToken, allowPrivateIp, timeoutMs, nextRpcId);\n}\n\nasync function probeToolCall(\n agentUrl: string,\n bearerToken: string,\n toolName: string,\n args: Record<string, unknown>,\n allowPrivateIp: boolean,\n timeoutMs: number | undefined,\n nextRpcId: () => number\n): Promise<HttpCapture> {\n return httpJsonRpc(\n agentUrl,\n { method: 'tools/call', params: { name: toolName, arguments: args } },\n bearerToken,\n allowPrivateIp,\n timeoutMs,\n nextRpcId\n );\n}\n\nasync function attemptTokenRefresh(options: {\n tokenEndpoint: string;\n clientId: string;\n clientSecret?: string;\n refreshToken: string;\n resource: string;\n allowPrivateIp: boolean;\n timeoutMs?: number;\n}): Promise<HttpCapture> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: options.refreshToken,\n client_id: options.clientId,\n // RFC 8707: explicitly request a token for this resource. If the AS\n // ignores this (H2), the refreshed token's aud claim will still be wrong.\n resource: options.resource,\n });\n if (options.clientSecret) body.set('client_secret', options.clientSecret);\n\n return httpFormPost(options.tokenEndpoint, body, options.allowPrivateIp, options.timeoutMs);\n}\n\nasync function httpGet(url: string, allowPrivateIp: boolean, timeoutMs?: number): Promise<HttpCapture> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return {\n url,\n method: 'GET',\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return { url, method: 'GET', status: 0, headers: {}, body: null, error: formatError(err) };\n }\n}\n\nasync function httpJsonRpc(\n url: string,\n rpc: { method: string; params?: unknown },\n bearerToken: string | undefined,\n allowPrivateIp: boolean,\n timeoutMs: number | undefined,\n nextRpcId: () => number\n): Promise<HttpCapture> {\n const body = JSON.stringify({ jsonrpc: '2.0', id: nextRpcId(), ...rpc });\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/json',\n };\n if (bearerToken) headers.authorization = `Bearer ${bearerToken}`;\n\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'POST',\n headers,\n body,\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return {\n url,\n method: 'POST',\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return { url, method: 'POST', status: 0, headers: {}, body: null, error: formatError(err) };\n }\n}\n\nasync function httpFormPost(\n url: string,\n body: URLSearchParams,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<HttpCapture> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/x-www-form-urlencoded',\n accept: 'application/json',\n },\n body: body.toString(),\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return {\n url,\n method: 'POST',\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return { url, method: 'POST', status: 0, headers: {}, body: null, error: formatError(err) };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Hypothesis ranking\n// ---------------------------------------------------------------------------\n\ninterface RankInput {\n agentUrl: string;\n steps: DiagnosisStep[];\n currentDecoded: DecodedAccessToken | null;\n refreshedDecoded: DecodedAccessToken | null;\n}\n\nfunction rankHypotheses(input: RankInput): Hypothesis[] {\n const out: Hypothesis[] = [];\n const prmStep = input.steps.find(s => s.name === 'probe_protected_resource_metadata');\n const listStep = input.steps.find(s => s.name === 'list_tools_probe');\n const toolStep = input.steps.find(s => s.name === 'tool_call_probe');\n const refreshStep = input.steps.find(s => s.name === 'token_refresh_attempt');\n\n // H1: Resource URL mismatch between well-known and agent host\n out.push(rankH1(input.agentUrl, prmStep));\n\n // H2: Refresh grant missing `resource` parameter (RFC 8707)\n out.push(rankH2(input.agentUrl, refreshStep, input.refreshedDecoded));\n\n // H4: Agent endpoint returns 401 with no WWW-Authenticate (RFC 6750 violation)\n out.push(rankH4(listStep, toolStep));\n\n // H5: Token `aud` claim missing or doesn't match agent URL\n out.push(rankH5(input.agentUrl, input.currentDecoded, prmStep));\n\n // H6: Agent endpoint doesn't validate audience (accepts token but ignores it)\n out.push(rankH6(input.agentUrl, input.currentDecoded, toolStep));\n\n // H7: Authorization server supports Dynamic Client Registration (RFC 7591)\n // for client_credentials — informational, helps operators decide whether\n // a throwaway M2M client is viable for compliance runs.\n const asStep = input.steps.find(s => s.name === 'probe_authorization_server_metadata');\n out.push(rankH7(asStep));\n\n // Order: likely > possible > ruled_out > not_observed\n const order: Record<HypothesisVerdict, number> = {\n likely: 0,\n possible: 1,\n ruled_out: 2,\n not_observed: 3,\n };\n return out.sort((a, b) => order[a.verdict] - order[b.verdict]);\n}\n\nfunction rankH1(agentUrl: string, prmStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H1',\n title: 'Resource URL mismatch between well-known and agent host',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (\n !prmStep?.http ||\n prmStep.http.status !== 200 ||\n typeof prmStep.http.body !== 'object' ||\n prmStep.http.body === null\n ) {\n base.verdict = 'not_observed';\n base.summary = 'No protected-resource metadata was retrieved — cannot compare `resource` to agent URL';\n return base;\n }\n const advertised = (prmStep.http.body as { resource?: unknown }).resource;\n if (typeof advertised !== 'string') {\n base.verdict = 'not_observed';\n base.summary = 'protected-resource metadata has no `resource` field';\n return base;\n }\n\n const normAdvertised = normalizeForCompare(advertised);\n const normAgent = normalizeForCompare(agentUrl);\n if (normAdvertised === normAgent) {\n base.verdict = 'ruled_out';\n base.summary = `Advertised resource matches agent URL (${advertised})`;\n return base;\n }\n\n // RFC 9728 §2 + §3.2 — `resource` is the canonical resource identifier the\n // AS binds tokens to (the `aud` claim target per RFC 9068 §3), not the\n // request URI. RFC 8707 §2 requires only an absolute URI with optional path\n // component, so a parent-path resource ID covering the agent's sub-path is\n // spec-correct, not a mismatch.\n const advertisedOrigin = originForCompare(advertised);\n const agentOrigin = originForCompare(agentUrl);\n\n // Unparseable resource (e.g. AS returned an opaque URN like `urn:foo`). We\n // can't classify origin/path containment without parsing, and falling\n // through would misleadingly evaluate `isPathPrefix` against raw strings.\n // Surface as `possible` so the operator sees the actual advertisement\n // without us inventing a fix hint we can't justify.\n if (!advertisedOrigin) {\n base.verdict = 'possible';\n base.summary = `Advertised resource is not a parseable URL: \"${advertised}\"`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n 'RFC 8707 §2 requires `resource` to be an absolute URI. Check the AS configuration if this should be a URL identifier.',\n ];\n return base;\n }\n\n if (agentOrigin && advertisedOrigin !== agentOrigin) {\n base.verdict = 'likely';\n base.summary = `Advertised resource origin \"${advertisedOrigin}\" does not match agent origin \"${agentOrigin}\"`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n \"Fix: point the agent at the same origin (scheme+host+port) the PRM advertises, or fix the PRM `resource` to advertise the agent's own origin.\",\n ];\n return base;\n }\n\n if (isUrlPrefix(normAdvertised, normAgent)) {\n // Agent endpoint is a sub-path under the advertised resource identifier.\n // RFC 9728 §3.2 leaves this open; RFC 9068 §3 + RFC 8707 §2 together\n // make it the canonical pattern for a single resource server fronting\n // multiple endpoints under a shared `aud` identifier.\n base.verdict = 'ruled_out';\n base.summary = `Agent URL is a sub-path under advertised resource identifier (spec-correct per RFC 9728 §2/§3.2)`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n `The AS will issue tokens with \\`aud=${advertised}\\`; the agent at \\`${agentUrl}\\` validates that aud against its own resource identifier, not the request URI. This is the intended pattern for a single resource server fronting multiple endpoints.`,\n ];\n return base;\n }\n\n // Same origin, agent is not a sub-path of advertised resource. The AS may\n // legitimately host multiple resource identifiers on the same origin, each\n // covering a disjoint set of endpoints; without inspecting AS aud-binding\n // policy we can't tell whether this is intended or a misconfiguration.\n base.verdict = 'possible';\n base.summary = `Advertised resource \"${advertised}\" does not contain agent URL \"${agentUrl}\" as a sub-path (both on origin ${agentOrigin ?? '(unknown)'})`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n 'If the AS binds tokens with `aud=<advertised resource>`, this agent endpoint may be outside that audience — confirm the AS aud-binding policy or that the operator advertised the correct resource identifier for this endpoint.',\n ];\n return base;\n}\n\nfunction rankH2(\n agentUrl: string,\n refreshStep: DiagnosisStep | undefined,\n refreshedDecoded: DecodedAccessToken | null\n): Hypothesis {\n const base: Hypothesis = {\n id: 'H2',\n title: 'Refresh grant missing `resource` parameter (RFC 8707)',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!refreshStep) {\n base.summary = 'No token refresh was attempted (skipped, missing refresh_token, or missing client registration)';\n return base;\n }\n if (refreshStep.error) {\n base.summary = `Token refresh could not run: ${refreshStep.error}`;\n return base;\n }\n if (!refreshStep.http || refreshStep.http.status !== 200) {\n base.summary = `Token refresh returned HTTP ${refreshStep.http?.status ?? 0}; cannot evaluate aud handling`;\n base.evidence = refreshStep.http ? [`Body: ${safeStringify(refreshStep.http.body)}`] : [];\n return base;\n }\n if (!refreshedDecoded) {\n base.verdict = 'possible';\n base.summary = 'Refreshed token is opaque — audience cannot be inspected from the wire';\n base.evidence = [\n 'If this agent is a public resource server, an opaque token should still carry `aud` when inspected via introspection.',\n ];\n return base;\n }\n const result = validateTokenAudience(jwtFromDecoded(refreshedDecoded), agentUrl);\n if (result.ok) {\n base.verdict = 'ruled_out';\n base.summary = 'Refreshed token `aud` claim matches the expected resource — AS honors RFC 8707 `resource`';\n return base;\n }\n base.verdict = 'likely';\n base.summary = 'We asked the AS to refresh with `resource=<agent-url>` but the new token `aud` still does not match';\n base.evidence = [\n `Expected aud: ${agentUrl}`,\n `Actual aud: ${safeStringify(result.actualAudience)}`,\n 'Fix: update the AS to honor RFC 8707 `resource` and emit the corresponding `aud` claim on refresh_token grants.',\n ];\n return base;\n}\n\nfunction rankH4(listStep?: DiagnosisStep, toolStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H4',\n title: 'Agent endpoint returns 401 with no `WWW-Authenticate` (RFC 6750 violation)',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n const unauthed = listStep?.http;\n const authed = toolStep?.http;\n const rfc6750Offender = (cap: HttpCapture | undefined) =>\n cap && cap.status === 401 && !cap.headers['www-authenticate'];\n\n if (rfc6750Offender(unauthed) || rfc6750Offender(authed)) {\n base.verdict = 'likely';\n base.summary = 'Agent returned 401 without a `WWW-Authenticate` header — breaks RFC 6750 §3 and RFC 9728 discovery';\n base.evidence = [];\n if (rfc6750Offender(unauthed))\n base.evidence.push(`unauthenticated tools/list: ${unauthed!.status} (no WWW-Authenticate)`);\n if (rfc6750Offender(authed))\n base.evidence.push(`authenticated tools/call: ${authed!.status} (no WWW-Authenticate)`);\n base.evidence.push('Fix: emit `WWW-Authenticate: Bearer error=\"…\", resource_metadata=\"…\"` on 401 responses.');\n return base;\n }\n\n if (\n (unauthed?.status === 401 && unauthed.headers['www-authenticate']) ||\n (authed?.status === 401 && authed.headers['www-authenticate'])\n ) {\n base.verdict = 'ruled_out';\n base.summary = 'Agent correctly emits a WWW-Authenticate challenge on 401';\n const challenge = parseWWWAuthenticate(\n unauthed?.headers['www-authenticate'] ?? authed?.headers['www-authenticate']\n );\n if (challenge) {\n base.evidence = [\n `Scheme: ${challenge.scheme}`,\n ...(challenge.error ? [`error: ${challenge.error}`] : []),\n ...(challenge.resource_metadata ? [`resource_metadata: ${challenge.resource_metadata}`] : []),\n ];\n }\n return base;\n }\n\n base.summary = 'No 401 was observed on either probe — cannot evaluate challenge format';\n return base;\n}\n\nfunction rankH5(\n agentUrl: string,\n currentDecoded: DecodedAccessToken | null,\n prmStep: DiagnosisStep | undefined\n): Hypothesis {\n const base: Hypothesis = {\n id: 'H5',\n title: 'Token `aud` claim missing or does not match agent URL',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!currentDecoded) {\n base.summary = 'No saved access token was decodable — cannot inspect `aud`';\n return base;\n }\n // Prefer the advertised resource (PRM) when available, fall back to agent URL.\n const prmResource =\n prmStep?.http?.body && typeof (prmStep.http.body as { resource?: unknown }).resource === 'string'\n ? (prmStep.http.body as { resource: string }).resource\n : undefined;\n const expected = prmResource ?? agentUrl;\n const prmDiffersFromAgent =\n prmResource !== undefined && normalizeForCompare(prmResource) !== normalizeForCompare(agentUrl);\n const result = validateTokenAudience(jwtFromDecoded(currentDecoded), expected);\n if (result.ok) {\n base.verdict = 'ruled_out';\n base.summary = `Saved token \\`aud\\` matches expected resource (${expected})`;\n if (prmDiffersFromAgent) {\n base.evidence = [\n `Compared against advertised PRM resource; agent URL differs (see H1). Token is valid for the resource the AS thinks it's protecting, not for the agent URL you configured.`,\n ];\n }\n return base;\n }\n base.verdict = 'likely';\n base.summary = result.reason ?? 'Saved token audience does not match expected resource';\n base.evidence = [\n `Expected: ${expected}${prmDiffersFromAgent ? ' (from PRM; see H1 — agent URL differs)' : ''}`,\n `Actual aud: ${safeStringify(result.actualAudience ?? '(missing)')}`,\n 'Fix: ensure the AS sets `aud` to the resource URL on token issuance (RFC 9068 §2.2 / RFC 8707).',\n ];\n return base;\n}\n\nfunction rankH6(agentUrl: string, currentDecoded: DecodedAccessToken | null, toolStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H6',\n title: 'Agent accepts token but does not validate audience',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!toolStep?.http || !currentDecoded) {\n base.summary = 'Need both a decoded token and a tool_call probe to evaluate';\n return base;\n }\n if (toolStep.http.status !== 200) {\n base.verdict = 'ruled_out';\n base.summary = `tool_call returned HTTP ${toolStep.http.status} — agent did not accept the token blindly`;\n return base;\n }\n const audResult = validateTokenAudience(jwtFromDecoded(currentDecoded), agentUrl);\n if (audResult.ok) {\n base.verdict = 'ruled_out';\n base.summary = 'Token `aud` matches agent URL and tool_call succeeded — cannot distinguish from correct behavior';\n return base;\n }\n base.verdict = 'likely';\n base.summary =\n 'tool_call succeeded with a token whose `aud` does not match the agent URL — agent is not enforcing audience';\n base.evidence = [\n `tool_call status: ${toolStep.http.status}`,\n `Token aud: ${safeStringify(audResult.actualAudience ?? '(missing)')}`,\n `Agent URL: ${agentUrl}`,\n 'Fix: have the agent reject tokens whose `aud` does not include its own resource URL (RFC 9068).',\n ];\n return base;\n}\n\n/**\n * H7 — DCR readiness for client_credentials. Pure capability probe; no\n * attempt to actually register. Flagged `possible` when both a\n * `registration_endpoint` and `client_credentials` in `grant_types_supported`\n * are advertised, so an operator deciding whether to script a throwaway M2M\n * client has a signal. `ruled_out` when one or both are missing.\n */\nfunction rankH7(asStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H7',\n title: 'Authorization server supports Dynamic Client Registration for client_credentials',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!asStep?.http || asStep.http.status !== 200 || !asStep.http.body || typeof asStep.http.body !== 'object') {\n base.summary = 'Authorization server metadata not observed';\n return base;\n }\n const md = asStep.http.body as { registration_endpoint?: unknown; grant_types_supported?: unknown };\n const registrationEndpoint = typeof md.registration_endpoint === 'string' ? md.registration_endpoint : undefined;\n const grants = Array.isArray(md.grant_types_supported)\n ? md.grant_types_supported.filter((g: unknown): g is string => typeof g === 'string')\n : undefined;\n const supportsCc = grants ? grants.includes('client_credentials') : undefined;\n if (!registrationEndpoint) {\n base.verdict = 'ruled_out';\n base.summary = 'No registration_endpoint advertised — DCR not available';\n base.evidence = ['AS metadata has no registration_endpoint field'];\n return base;\n }\n if (supportsCc === false) {\n base.verdict = 'ruled_out';\n base.summary = 'registration_endpoint present but client_credentials not in grant_types_supported';\n base.evidence = [\n `registration_endpoint: ${registrationEndpoint}`,\n `grant_types_supported: [${(grants ?? []).join(', ')}]`,\n ];\n return base;\n }\n base.verdict = 'possible';\n base.summary =\n supportsCc === true\n ? 'AS advertises DCR and client_credentials — a throwaway M2M client may be creatable'\n : 'AS advertises DCR (grant_types_supported absent, RFC 8414 default may not include client_credentials)';\n base.evidence = [\n `registration_endpoint: ${registrationEndpoint}`,\n `grant_types_supported: [${(grants ?? []).join(', ') || '(absent)'}]`,\n 'Note: many production ASes gate DCR behind an initial-access token; success is not guaranteed.',\n ];\n return base;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractIssuer(prm: HttpCapture): string | undefined {\n if (prm.status !== 200 || !prm.body || typeof prm.body !== 'object') return undefined;\n const servers = (prm.body as { authorization_servers?: unknown }).authorization_servers;\n if (!Array.isArray(servers) || typeof servers[0] !== 'string') return undefined;\n return servers[0];\n}\n\nfunction extractTokenEndpoint(as: HttpCapture): string | undefined {\n if (as.status !== 200 || !as.body || typeof as.body !== 'object') return undefined;\n const endpoint = (as.body as { token_endpoint?: unknown }).token_endpoint;\n return typeof endpoint === 'string' ? endpoint : undefined;\n}\n\n/** Fields in an OAuth token-endpoint response that should never leak to disk/logs. */\nconst TOKEN_MATERIAL_FIELDS = new Set(['access_token', 'refresh_token', 'id_token']);\n\n/**\n * Redact token-bearing fields from a token-endpoint capture so that `--json`\n * output can safely be pasted into bug reports. The redacted form preserves\n * the original length (useful for \"wrong length\" diagnostics) without leaking\n * the secret. Set `includeTokens: true` on {@link DiagnoseOptions} to skip.\n */\nfunction redactTokenMaterial(capture: HttpCapture): HttpCapture {\n if (!capture.body || typeof capture.body !== 'object' || Array.isArray(capture.body)) return capture;\n const body = capture.body as Record<string, unknown>;\n const redacted: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(body)) {\n if (TOKEN_MATERIAL_FIELDS.has(k) && typeof v === 'string') {\n redacted[k] = `<redacted length=${v.length}>`;\n } else {\n redacted[k] = v;\n }\n }\n return { ...capture, body: redacted };\n}\n\nfunction normalizeForCompare(value: string): string {\n try {\n const u = new URL(value);\n const port = u.port && !isDefaultPort(u.protocol, u.port) ? `:${u.port}` : '';\n const path = u.pathname.length > 1 && u.pathname.endsWith('/') ? u.pathname.slice(0, -1) : u.pathname;\n return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}${path}`;\n } catch {\n return value;\n }\n}\n\n/**\n * Scheme + lowercased host + non-default port. Returns `null` when the input\n * has no host (e.g. `urn:foo`, `mailto:bar@x`) or fails to parse. RFC 8707 §2\n * allows any absolute URI as `resource`, but origin-based comparisons only\n * make sense for hierarchical http(s)-style URLs.\n */\nfunction originForCompare(value: string): string | null {\n try {\n const u = new URL(value);\n if (!u.hostname) return null;\n const port = u.port && !isDefaultPort(u.protocol, u.port) ? `:${u.port}` : '';\n return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Whether `candidate` is `prefix` itself or a strict URL descendant (same\n * scheme+host+port plus a path segment under `prefix`'s path). Inputs come\n * from {@link normalizeForCompare}; trailing slashes are stripped for paths\n * longer than `/`, but the root path `/` is preserved. We require a `/`\n * boundary after the prefix so `.../foo` does not match `.../foobar/...`.\n * Path comparison is case-sensitive per RFC 3986 §6.2.2.1; only scheme/host\n * are lowercased upstream in {@link normalizeForCompare}.\n */\nfunction isUrlPrefix(prefix: string, candidate: string): boolean {\n if (prefix === candidate) return true;\n const trimmed = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n return candidate.startsWith(trimmed + '/');\n}\n\nfunction isDefaultPort(scheme: string, port: string): boolean {\n if (scheme === 'https:' && port === '443') return true;\n if (scheme === 'http:' && port === '80') return true;\n return false;\n}\n\nfunction jwtFromDecoded(_decoded: DecodedAccessToken): string {\n // validateTokenAudience re-decodes from a JWT string, so we round-trip.\n // Since we already decoded this, we can't reconstruct the original signature,\n // but the validator only cares about the claims segment — so we synthesize a\n // three-segment token with the original claims and dummy header/signature.\n const h = base64url(JSON.stringify(_decoded.header));\n const c = base64url(JSON.stringify(_decoded.claims));\n return `${h}.${c}.${_decoded.signature || 'sig'}`;\n}\n\nfunction base64url(input: string): string {\n // `base64` output has at most 2 trailing `=` characters; the bounded form\n // `={0,2}$` avoids the polynomial-regex flag CodeQL raises on `=+$`.\n return Buffer.from(input)\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/={0,2}$/, '');\n}\n\nfunction formatError(err: unknown): string {\n if (err instanceof SsrfRefusedError) {\n return `SSRF guard refused: ${err.code} (${err.message})`;\n }\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/auth/oauth/diagnose.ts"],"sourcesContent":["/**\n * `adcp diagnose-auth` — structured OAuth handshake diagnostics.\n *\n * Performs an end-to-end trace of the OAuth flow for a single MCP agent and\n * emits a structured {@link AuthDiagnosisReport} with wire-level HTTP captures,\n * decoded token claims, and ranked hypotheses about what might be wrong.\n *\n * Designed for incident diagnosis, not continuous monitoring — each run does\n * a full round-trip including (optionally) a token refresh and a `tools/call`\n * attempt, so don't call this in a tight loop.\n *\n * The runner is decoupled from the CLI so library consumers can invoke it\n * programmatically (e.g. from a monitoring agent or a test-controller fixture)\n * and render their own output.\n */\nimport { ssrfSafeFetch, decodeBodyAsJsonOrText, SsrfRefusedError } from '../../net';\nimport type { AgentConfig } from './types';\nimport { decodeAccessTokenClaims, parseWWWAuthenticate, validateTokenAudience } from './diagnostics';\nimport type { DecodedAccessToken } from './diagnostics';\n\n/**\n * Options for {@link runAuthDiagnosis}. Most callers pass an `AgentConfig` and\n * accept the defaults.\n */\nexport interface DiagnoseOptions {\n /** Allow http:// and private-IP probe targets. Default false. */\n allowPrivateIp?: boolean;\n /** Skip the `tools/call` probe (e.g. if the agent doesn't expose a no-op tool). */\n skipToolCall?: boolean;\n /**\n * Name of the tool to exercise in the `tools/call` probe. Default: `get_products`.\n * This default is AdCP-specific; for non-AdCP MCP agents, pass a tool name that exists.\n */\n probeToolName?: string;\n /** Arguments to send with the probe tool call. Default: `{ brief: 'diagnose-auth probe' }`. */\n probeToolArgs?: Record<string, unknown>;\n /** Skip the token-refresh attempt even when a refresh_token is available. */\n skipRefresh?: boolean;\n /** Replace the default timeout for individual HTTP probes (ms). Default inherits from ssrfSafeFetch (10s). */\n timeoutMs?: number;\n /**\n * Include raw access_token / refresh_token / id_token values in the report.\n * Default false — tokens are replaced with `<redacted length=N>` markers so\n * that `--json` output is safe to paste into bug reports and log aggregators.\n */\n includeTokens?: boolean;\n}\n\n/** Wire-level capture of a single HTTP probe. */\nexport interface HttpCapture {\n url: string;\n method: string;\n /** 0 if the request never left the client (DNS failure, SSRF refusal, …). */\n status: number;\n /** Response headers with lowercased keys. */\n headers: Record<string, string>;\n /** Parsed JSON body if `content-type` is JSON, raw text otherwise, or `null` on error. */\n body: unknown;\n /** Client-side error message, if any. */\n error?: string;\n}\n\n/** One step of the diagnosis, in execution order. */\nexport interface DiagnosisStep {\n name:\n | 'probe_protected_resource_metadata'\n | 'probe_authorization_server_metadata'\n | 'decode_current_token'\n | 'token_refresh_attempt'\n | 'decode_refreshed_token'\n | 'list_tools_probe'\n | 'tool_call_probe';\n /** Present for HTTP steps. */\n http?: HttpCapture;\n /** Present for decode steps. Claims are unverified. */\n decodedToken?: DecodedAccessToken | null;\n /** Free-form notes attached by the step for hypothesis ranking. */\n notes?: string[];\n /** Step-level error (distinct from `http.error` — set when the step was skipped or could not run). */\n error?: string;\n}\n\n/** Rank verdict for a hypothesis. Ordered most-to-least actionable. */\nexport type HypothesisVerdict = 'likely' | 'possible' | 'ruled_out' | 'not_observed';\n\n/** One ranked hypothesis about what might be wrong. */\nexport interface Hypothesis {\n /**\n * Stable ID. Currently `H1`, `H2`, `H4`, `H5`, `H6` — `H3` (session ID handling)\n * is reserved for a future addition. Downstream consumers should treat this as\n * an opaque string rather than exhaustively switching on it.\n */\n id: string;\n /** Short human-readable title. */\n title: string;\n /** One-line summary, tailored with evidence from this run. */\n summary: string;\n verdict: HypothesisVerdict;\n /** Supporting evidence extracted from the steps above. */\n evidence: string[];\n}\n\n/**\n * Report schema version. Bumped when breaking changes to the shape of\n * {@link AuthDiagnosisReport} land. Dashboards and downstream consumers should\n * check this before relying on specific fields.\n */\nexport const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1 as const;\n\n/** Full diagnosis report returned by {@link runAuthDiagnosis}. */\nexport interface AuthDiagnosisReport {\n /** See {@link AUTH_DIAGNOSIS_SCHEMA_VERSION}. */\n schemaVersion: typeof AUTH_DIAGNOSIS_SCHEMA_VERSION;\n /** Agent URL under test. */\n agentUrl: string;\n /** Alias from saved config, if one was provided. */\n aliasId?: string;\n /** Per-step wire captures, in execution order. */\n steps: DiagnosisStep[];\n /** Ranked hypotheses — `likely` first, then `possible`, then `ruled_out`. */\n hypotheses: Hypothesis[];\n /** Report generation timestamp (ISO-8601, UTC). */\n generatedAt: string;\n}\n\n/**\n * Run the full diagnosis. Pass an agent config with (optionally) saved\n * `oauth_tokens` to exercise a realistic handshake. Returns a structured\n * report — rendering is the caller's job (CLI renders a text summary;\n * programmatic consumers typically serialize to JSON).\n */\nexport async function runAuthDiagnosis(\n agent: AgentConfig,\n options: DiagnoseOptions = {}\n): Promise<AuthDiagnosisReport> {\n const steps: DiagnosisStep[] = [];\n const agentUrl = agent.agent_uri;\n const allowPrivateIp = options.allowPrivateIp ?? false;\n const includeTokens = options.includeTokens ?? false;\n // Per-invocation RPC id counter so concurrent `runAuthDiagnosis` calls don't share state.\n let rpcId = 0;\n const nextRpcId = () => ++rpcId;\n\n // Step 1: protected-resource metadata (RFC 9728)\n const prmCapture = await probeProtectedResourceMetadata(agentUrl, allowPrivateIp, options.timeoutMs);\n steps.push({ name: 'probe_protected_resource_metadata', http: prmCapture });\n\n // Step 2: authorization-server metadata (RFC 8414) — derives issuer from PRM\n const asCapture = await probeAuthorizationServerMetadata(prmCapture, allowPrivateIp, options.timeoutMs);\n steps.push({ name: 'probe_authorization_server_metadata', http: asCapture });\n\n // Step 3: decode the current access token (if saved)\n const currentToken = agent.oauth_tokens?.access_token;\n const currentDecoded = currentToken ? decodeAccessTokenClaims(currentToken) : null;\n steps.push({\n name: 'decode_current_token',\n decodedToken: currentDecoded,\n notes: currentToken\n ? currentDecoded\n ? [`Decoded JWT with claims: ${Object.keys(currentDecoded.claims).join(', ')}`]\n : ['Saved access_token is opaque (not a JWT) — `aud` inspection is not possible']\n : ['No saved access_token'],\n });\n\n // Step 4: token refresh (only if we have a refresh_token AND the user didn't skip)\n let refreshedAccessToken: string | undefined;\n let refreshedDecoded: DecodedAccessToken | null = null;\n if (!options.skipRefresh && agent.oauth_tokens?.refresh_token) {\n const tokenEndpoint = extractTokenEndpoint(asCapture);\n const clientId = agent.oauth_client?.client_id;\n // Always request the agent URL as the resource indicator, even if PRM\n // advertises something different — a well-behaved client sends what it\n // actually wants to talk to, and using PRM.resource here would cause H2\n // to fire spuriously when the real problem is H1.\n const resource = agentUrl;\n if (!tokenEndpoint) {\n steps.push({\n name: 'token_refresh_attempt',\n error: 'No token_endpoint available from authorization-server metadata — cannot attempt refresh',\n });\n } else if (!clientId) {\n steps.push({\n name: 'token_refresh_attempt',\n error: 'No saved oauth_client.client_id — cannot attempt refresh without it',\n });\n } else {\n const refreshCapture = await attemptTokenRefresh({\n tokenEndpoint,\n clientId,\n clientSecret: agent.oauth_client?.client_secret,\n refreshToken: agent.oauth_tokens.refresh_token,\n resource,\n allowPrivateIp,\n timeoutMs: options.timeoutMs,\n });\n // Capture the raw access_token string BEFORE redaction so we can still\n // decode claims for H2 analysis. The report body itself uses the\n // redacted capture unless includeTokens is set.\n if (refreshCapture.status === 200 && refreshCapture.body && typeof refreshCapture.body === 'object') {\n const body = refreshCapture.body as Record<string, unknown>;\n if (typeof body.access_token === 'string') {\n refreshedAccessToken = body.access_token;\n refreshedDecoded = decodeAccessTokenClaims(refreshedAccessToken);\n }\n }\n steps.push({\n name: 'token_refresh_attempt',\n http: includeTokens ? refreshCapture : redactTokenMaterial(refreshCapture),\n });\n if (refreshedAccessToken) {\n steps.push({\n name: 'decode_refreshed_token',\n decodedToken: refreshedDecoded,\n notes: refreshedDecoded\n ? [`Decoded refreshed JWT with claims: ${Object.keys(refreshedDecoded.claims).join(', ')}`]\n : ['Refreshed access_token is opaque (not a JWT)'],\n });\n }\n }\n }\n\n // Step 5: unauthenticated list_tools to surface 401 + WWW-Authenticate\n const listToolsCapture = await probeListTools(agentUrl, undefined, allowPrivateIp, options.timeoutMs, nextRpcId);\n steps.push({ name: 'list_tools_probe', http: listToolsCapture });\n\n // Step 6: authenticated tool_call (skip if no token or if caller opts out)\n const tokenForCall = refreshedAccessToken ?? currentToken;\n if (!options.skipToolCall && tokenForCall) {\n const toolName = options.probeToolName ?? 'get_products';\n const args = options.probeToolArgs ?? { brief: 'diagnose-auth probe' };\n const toolCapture = await probeToolCall(\n agentUrl,\n tokenForCall,\n toolName,\n args,\n allowPrivateIp,\n options.timeoutMs,\n nextRpcId\n );\n steps.push({ name: 'tool_call_probe', http: toolCapture });\n } else if (!tokenForCall) {\n steps.push({\n name: 'tool_call_probe',\n error: 'No access_token available — skipping authenticated tool call probe',\n });\n }\n\n const hypotheses = rankHypotheses({\n agentUrl,\n steps,\n currentDecoded,\n refreshedDecoded,\n });\n\n return {\n schemaVersion: AUTH_DIAGNOSIS_SCHEMA_VERSION,\n agentUrl,\n aliasId: agent.id,\n steps,\n hypotheses,\n generatedAt: new Date().toISOString(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Probes\n// ---------------------------------------------------------------------------\n\nasync function probeProtectedResourceMetadata(\n agentUrl: string,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<HttpCapture> {\n const u = new URL(agentUrl);\n const url = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n return httpGet(url, allowPrivateIp, timeoutMs);\n}\n\nasync function probeAuthorizationServerMetadata(\n prm: HttpCapture,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<HttpCapture> {\n const issuer = extractIssuer(prm);\n if (!issuer) {\n return {\n url: '',\n method: 'GET',\n status: 0,\n headers: {},\n body: null,\n error: 'protected-resource metadata did not yield an authorization_servers[0] entry',\n };\n }\n const url = `${issuer.replace(/\\/$/, '')}/.well-known/oauth-authorization-server`;\n return httpGet(url, allowPrivateIp, timeoutMs);\n}\n\nasync function probeListTools(\n agentUrl: string,\n bearerToken: string | undefined,\n allowPrivateIp: boolean,\n timeoutMs: number | undefined,\n nextRpcId: () => number\n): Promise<HttpCapture> {\n return httpJsonRpc(agentUrl, { method: 'tools/list' }, bearerToken, allowPrivateIp, timeoutMs, nextRpcId);\n}\n\nasync function probeToolCall(\n agentUrl: string,\n bearerToken: string,\n toolName: string,\n args: Record<string, unknown>,\n allowPrivateIp: boolean,\n timeoutMs: number | undefined,\n nextRpcId: () => number\n): Promise<HttpCapture> {\n return httpJsonRpc(\n agentUrl,\n { method: 'tools/call', params: { name: toolName, arguments: args } },\n bearerToken,\n allowPrivateIp,\n timeoutMs,\n nextRpcId\n );\n}\n\nasync function attemptTokenRefresh(options: {\n tokenEndpoint: string;\n clientId: string;\n clientSecret?: string;\n refreshToken: string;\n resource: string;\n allowPrivateIp: boolean;\n timeoutMs?: number;\n}): Promise<HttpCapture> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: options.refreshToken,\n client_id: options.clientId,\n // RFC 8707: explicitly request a token for this resource. If the AS\n // ignores this (H2), the refreshed token's aud claim will still be wrong.\n resource: options.resource,\n });\n if (options.clientSecret) body.set('client_secret', options.clientSecret);\n\n return httpFormPost(options.tokenEndpoint, body, options.allowPrivateIp, options.timeoutMs);\n}\n\nasync function httpGet(url: string, allowPrivateIp: boolean, timeoutMs?: number): Promise<HttpCapture> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return {\n url,\n method: 'GET',\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return { url, method: 'GET', status: 0, headers: {}, body: null, error: formatError(err) };\n }\n}\n\nasync function httpJsonRpc(\n url: string,\n rpc: { method: string; params?: unknown },\n bearerToken: string | undefined,\n allowPrivateIp: boolean,\n timeoutMs: number | undefined,\n nextRpcId: () => number\n): Promise<HttpCapture> {\n const body = JSON.stringify({ jsonrpc: '2.0', id: nextRpcId(), ...rpc });\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/json',\n };\n if (bearerToken) headers.authorization = `Bearer ${bearerToken}`;\n\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'POST',\n headers,\n body,\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return {\n url,\n method: 'POST',\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return { url, method: 'POST', status: 0, headers: {}, body: null, error: formatError(err) };\n }\n}\n\nasync function httpFormPost(\n url: string,\n body: URLSearchParams,\n allowPrivateIp: boolean,\n timeoutMs?: number\n): Promise<HttpCapture> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/x-www-form-urlencoded',\n accept: 'application/json',\n },\n body: body.toString(),\n allowPrivateIp,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n return {\n url,\n method: 'POST',\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return { url, method: 'POST', status: 0, headers: {}, body: null, error: formatError(err) };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Hypothesis ranking\n// ---------------------------------------------------------------------------\n\ninterface RankInput {\n agentUrl: string;\n steps: DiagnosisStep[];\n currentDecoded: DecodedAccessToken | null;\n refreshedDecoded: DecodedAccessToken | null;\n}\n\nfunction rankHypotheses(input: RankInput): Hypothesis[] {\n const out: Hypothesis[] = [];\n const prmStep = input.steps.find(s => s.name === 'probe_protected_resource_metadata');\n const listStep = input.steps.find(s => s.name === 'list_tools_probe');\n const toolStep = input.steps.find(s => s.name === 'tool_call_probe');\n const refreshStep = input.steps.find(s => s.name === 'token_refresh_attempt');\n\n // H1: Resource URL mismatch between well-known and agent host\n out.push(rankH1(input.agentUrl, prmStep));\n\n // H2: Refresh grant missing `resource` parameter (RFC 8707)\n out.push(rankH2(input.agentUrl, refreshStep, input.refreshedDecoded));\n\n // H4: Agent endpoint returns 401 with no WWW-Authenticate (RFC 6750 violation)\n out.push(rankH4(listStep, toolStep));\n\n // H5: Token `aud` claim missing or doesn't match agent URL\n out.push(rankH5(input.agentUrl, input.currentDecoded, prmStep));\n\n // H6: Agent endpoint doesn't validate audience (accepts token but ignores it)\n out.push(rankH6(input.agentUrl, input.currentDecoded, toolStep));\n\n // H7: Authorization server supports Dynamic Client Registration (RFC 7591)\n // for client_credentials — informational, helps operators decide whether\n // a throwaway M2M client is viable for compliance runs.\n const asStep = input.steps.find(s => s.name === 'probe_authorization_server_metadata');\n out.push(rankH7(asStep));\n\n // Order: likely > possible > ruled_out > not_observed\n const order: Record<HypothesisVerdict, number> = {\n likely: 0,\n possible: 1,\n ruled_out: 2,\n not_observed: 3,\n };\n return out.sort((a, b) => order[a.verdict] - order[b.verdict]);\n}\n\nfunction rankH1(agentUrl: string, prmStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H1',\n title: 'Resource URL mismatch between well-known and agent host',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (\n !prmStep?.http ||\n prmStep.http.status !== 200 ||\n typeof prmStep.http.body !== 'object' ||\n prmStep.http.body === null\n ) {\n base.verdict = 'not_observed';\n base.summary = 'No protected-resource metadata was retrieved — cannot compare `resource` to agent URL';\n return base;\n }\n const advertised = (prmStep.http.body as { resource?: unknown }).resource;\n if (typeof advertised !== 'string') {\n base.verdict = 'not_observed';\n base.summary = 'protected-resource metadata has no `resource` field';\n return base;\n }\n\n const normAdvertised = normalizeForCompare(advertised);\n const normAgent = normalizeForCompare(agentUrl);\n if (normAdvertised === normAgent) {\n base.verdict = 'ruled_out';\n base.summary = `Advertised resource matches agent URL (${advertised})`;\n return base;\n }\n\n // RFC 9728 §2 + §3.2 — `resource` is the canonical resource identifier the\n // AS binds tokens to (the `aud` claim target per RFC 9068 §3), not the\n // request URI. RFC 8707 §2 requires only an absolute URI with optional path\n // component, so a parent-path resource ID covering the agent's sub-path is\n // spec-correct, not a mismatch.\n const advertisedOrigin = originForCompare(advertised);\n const agentOrigin = originForCompare(agentUrl);\n\n // Unparseable resource (e.g. AS returned an opaque URN like `urn:foo`). We\n // can't classify origin/path containment without parsing, and falling\n // through would misleadingly evaluate `isPathPrefix` against raw strings.\n // Surface as `possible` so the operator sees the actual advertisement\n // without us inventing a fix hint we can't justify.\n if (!advertisedOrigin) {\n base.verdict = 'possible';\n base.summary = `Advertised resource is not a parseable URL: \"${advertised}\"`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n 'RFC 8707 §2 requires `resource` to be an absolute URI. Check the AS configuration if this should be a URL identifier.',\n ];\n return base;\n }\n\n if (agentOrigin && advertisedOrigin !== agentOrigin) {\n base.verdict = 'likely';\n base.summary = `Advertised resource origin \"${advertisedOrigin}\" does not match agent origin \"${agentOrigin}\"`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n \"Fix: point the agent at the same origin (scheme+host+port) the PRM advertises, or fix the PRM `resource` to advertise the agent's own origin.\",\n ];\n return base;\n }\n\n if (isUrlPrefix(normAdvertised, normAgent)) {\n // Agent endpoint is a sub-path under the advertised resource identifier.\n // RFC 9728 §3.2 leaves this open; RFC 9068 §3 + RFC 8707 §2 together\n // make it the canonical pattern for a single resource server fronting\n // multiple endpoints under a shared `aud` identifier.\n base.verdict = 'ruled_out';\n base.summary = `Agent URL is a sub-path under advertised resource identifier (spec-correct per RFC 9728 §2/§3.2)`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n `The AS will issue tokens with \\`aud=${advertised}\\`; the agent at \\`${agentUrl}\\` validates that aud against its own resource identifier, not the request URI. This is the intended pattern for a single resource server fronting multiple endpoints.`,\n ];\n return base;\n }\n\n // Same origin, agent is not a sub-path of advertised resource. The AS may\n // legitimately host multiple resource identifiers on the same origin, each\n // covering a disjoint set of endpoints; without inspecting AS aud-binding\n // policy we can't tell whether this is intended or a misconfiguration.\n base.verdict = 'possible';\n base.summary = `Advertised resource \"${advertised}\" does not contain agent URL \"${agentUrl}\" as a sub-path (both on origin ${agentOrigin ?? '(unknown)'})`;\n base.evidence = [\n `PRM resource: ${advertised}`,\n `Agent URL: ${agentUrl}`,\n 'If the AS binds tokens with `aud=<advertised resource>`, this agent endpoint may be outside that audience — confirm the AS aud-binding policy or that the operator advertised the correct resource identifier for this endpoint.',\n ];\n return base;\n}\n\nfunction rankH2(\n agentUrl: string,\n refreshStep: DiagnosisStep | undefined,\n refreshedDecoded: DecodedAccessToken | null\n): Hypothesis {\n const base: Hypothesis = {\n id: 'H2',\n title: 'Refresh grant missing `resource` parameter (RFC 8707)',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!refreshStep) {\n base.summary = 'No token refresh was attempted (skipped, missing refresh_token, or missing client registration)';\n return base;\n }\n if (refreshStep.error) {\n base.summary = `Token refresh could not run: ${refreshStep.error}`;\n return base;\n }\n if (!refreshStep.http || refreshStep.http.status !== 200) {\n base.summary = `Token refresh returned HTTP ${refreshStep.http?.status ?? 0}; cannot evaluate aud handling`;\n base.evidence = refreshStep.http ? [`Body: ${safeStringify(refreshStep.http.body)}`] : [];\n return base;\n }\n if (!refreshedDecoded) {\n base.verdict = 'possible';\n base.summary = 'Refreshed token is opaque — audience cannot be inspected from the wire';\n base.evidence = [\n 'If this agent is a public resource server, an opaque token should still carry `aud` when inspected via introspection.',\n ];\n return base;\n }\n const result = validateTokenAudience(jwtFromDecoded(refreshedDecoded), agentUrl);\n if (result.ok) {\n base.verdict = 'ruled_out';\n base.summary = 'Refreshed token `aud` claim matches the expected resource — AS honors RFC 8707 `resource`';\n return base;\n }\n base.verdict = 'likely';\n base.summary = 'We asked the AS to refresh with `resource=<agent-url>` but the new token `aud` still does not match';\n base.evidence = [\n `Expected aud: ${agentUrl}`,\n `Actual aud: ${safeStringify(result.actualAudience)}`,\n 'Fix: update the AS to honor RFC 8707 `resource` and emit the corresponding `aud` claim on refresh_token grants.',\n ];\n return base;\n}\n\nfunction rankH4(listStep?: DiagnosisStep, toolStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H4',\n title: 'Agent endpoint returns 401 with no `WWW-Authenticate` (RFC 6750 violation)',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n const unauthed = listStep?.http;\n const authed = toolStep?.http;\n const rfc6750Offender = (cap: HttpCapture | undefined) =>\n cap && cap.status === 401 && !cap.headers['www-authenticate'];\n\n if (rfc6750Offender(unauthed) || rfc6750Offender(authed)) {\n base.verdict = 'likely';\n base.summary = 'Agent returned 401 without a `WWW-Authenticate` header — breaks RFC 6750 §3 and RFC 9728 discovery';\n base.evidence = [];\n if (rfc6750Offender(unauthed))\n base.evidence.push(`unauthenticated tools/list: ${unauthed!.status} (no WWW-Authenticate)`);\n if (rfc6750Offender(authed))\n base.evidence.push(`authenticated tools/call: ${authed!.status} (no WWW-Authenticate)`);\n base.evidence.push('Fix: emit `WWW-Authenticate: Bearer error=\"…\", resource_metadata=\"…\"` on 401 responses.');\n return base;\n }\n\n if (\n (unauthed?.status === 401 && unauthed.headers['www-authenticate']) ||\n (authed?.status === 401 && authed.headers['www-authenticate'])\n ) {\n base.verdict = 'ruled_out';\n base.summary = 'Agent correctly emits a WWW-Authenticate challenge on 401';\n const challenge = parseWWWAuthenticate(\n unauthed?.headers['www-authenticate'] ?? authed?.headers['www-authenticate']\n );\n if (challenge) {\n base.evidence = [\n `Scheme: ${challenge.scheme}`,\n ...(challenge.error ? [`error: ${challenge.error}`] : []),\n ...(challenge.resource_metadata ? [`resource_metadata: ${challenge.resource_metadata}`] : []),\n ];\n }\n return base;\n }\n\n base.summary = 'No 401 was observed on either probe — cannot evaluate challenge format';\n return base;\n}\n\nfunction rankH5(\n agentUrl: string,\n currentDecoded: DecodedAccessToken | null,\n prmStep: DiagnosisStep | undefined\n): Hypothesis {\n const base: Hypothesis = {\n id: 'H5',\n title: 'Token `aud` claim missing or does not match agent URL',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!currentDecoded) {\n base.summary = 'No saved access token was decodable — cannot inspect `aud`';\n return base;\n }\n // Prefer the advertised resource (PRM) when available, fall back to agent URL.\n const prmResource =\n prmStep?.http?.body && typeof (prmStep.http.body as { resource?: unknown }).resource === 'string'\n ? (prmStep.http.body as { resource: string }).resource\n : undefined;\n const expected = prmResource ?? agentUrl;\n const prmDiffersFromAgent =\n prmResource !== undefined && normalizeForCompare(prmResource) !== normalizeForCompare(agentUrl);\n const result = validateTokenAudience(jwtFromDecoded(currentDecoded), expected);\n if (result.ok) {\n base.verdict = 'ruled_out';\n base.summary = `Saved token \\`aud\\` matches expected resource (${expected})`;\n if (prmDiffersFromAgent) {\n base.evidence = [\n `Compared against advertised PRM resource; agent URL differs (see H1). Token is valid for the resource the AS thinks it's protecting, not for the agent URL you configured.`,\n ];\n }\n return base;\n }\n base.verdict = 'likely';\n base.summary = result.reason ?? 'Saved token audience does not match expected resource';\n base.evidence = [\n `Expected: ${expected}${prmDiffersFromAgent ? ' (from PRM; see H1 — agent URL differs)' : ''}`,\n `Actual aud: ${safeStringify(result.actualAudience ?? '(missing)')}`,\n 'Fix: ensure the AS sets `aud` to the resource URL on token issuance (RFC 9068 §2.2 / RFC 8707).',\n ];\n return base;\n}\n\nfunction rankH6(agentUrl: string, currentDecoded: DecodedAccessToken | null, toolStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H6',\n title: 'Agent accepts token but does not validate audience',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!toolStep?.http || !currentDecoded) {\n base.summary = 'Need both a decoded token and a tool_call probe to evaluate';\n return base;\n }\n if (toolStep.http.status !== 200) {\n base.verdict = 'ruled_out';\n base.summary = `tool_call returned HTTP ${toolStep.http.status} — agent did not accept the token blindly`;\n return base;\n }\n const audResult = validateTokenAudience(jwtFromDecoded(currentDecoded), agentUrl);\n if (audResult.ok) {\n base.verdict = 'ruled_out';\n base.summary = 'Token `aud` matches agent URL and tool_call succeeded — cannot distinguish from correct behavior';\n return base;\n }\n base.verdict = 'likely';\n base.summary =\n 'tool_call succeeded with a token whose `aud` does not match the agent URL — agent is not enforcing audience';\n base.evidence = [\n `tool_call status: ${toolStep.http.status}`,\n `Token aud: ${safeStringify(audResult.actualAudience ?? '(missing)')}`,\n `Agent URL: ${agentUrl}`,\n 'Fix: have the agent reject tokens whose `aud` does not include its own resource URL (RFC 9068).',\n ];\n return base;\n}\n\n/**\n * H7 — DCR readiness for client_credentials. Pure capability probe; no\n * attempt to actually register. Flagged `possible` when both a\n * `registration_endpoint` and `client_credentials` in `grant_types_supported`\n * are advertised, so an operator deciding whether to script a throwaway M2M\n * client has a signal. `ruled_out` when one or both are missing.\n */\nfunction rankH7(asStep?: DiagnosisStep): Hypothesis {\n const base: Hypothesis = {\n id: 'H7',\n title: 'Authorization server supports Dynamic Client Registration for client_credentials',\n summary: '',\n verdict: 'not_observed',\n evidence: [],\n };\n if (!asStep?.http || asStep.http.status !== 200 || !asStep.http.body || typeof asStep.http.body !== 'object') {\n base.summary = 'Authorization server metadata not observed';\n return base;\n }\n const md = asStep.http.body as { registration_endpoint?: unknown; grant_types_supported?: unknown };\n const registrationEndpoint = typeof md.registration_endpoint === 'string' ? md.registration_endpoint : undefined;\n const grants = Array.isArray(md.grant_types_supported)\n ? md.grant_types_supported.filter((g: unknown): g is string => typeof g === 'string')\n : undefined;\n const supportsCc = grants ? grants.includes('client_credentials') : undefined;\n if (!registrationEndpoint) {\n base.verdict = 'ruled_out';\n base.summary = 'No registration_endpoint advertised — DCR not available';\n base.evidence = ['AS metadata has no registration_endpoint field'];\n return base;\n }\n if (supportsCc === false) {\n base.verdict = 'ruled_out';\n base.summary = 'registration_endpoint present but client_credentials not in grant_types_supported';\n base.evidence = [\n `registration_endpoint: ${registrationEndpoint}`,\n `grant_types_supported: [${(grants ?? []).join(', ')}]`,\n ];\n return base;\n }\n base.verdict = 'possible';\n base.summary =\n supportsCc === true\n ? 'AS advertises DCR and client_credentials — a throwaway M2M client may be creatable'\n : 'AS advertises DCR (grant_types_supported absent, RFC 8414 default may not include client_credentials)';\n base.evidence = [\n `registration_endpoint: ${registrationEndpoint}`,\n `grant_types_supported: [${(grants ?? []).join(', ') || '(absent)'}]`,\n 'Note: many production ASes gate DCR behind an initial-access token; success is not guaranteed.',\n ];\n return base;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractIssuer(prm: HttpCapture): string | undefined {\n if (prm.status !== 200 || !prm.body || typeof prm.body !== 'object') return undefined;\n const servers = (prm.body as { authorization_servers?: unknown }).authorization_servers;\n if (!Array.isArray(servers) || typeof servers[0] !== 'string') return undefined;\n return servers[0];\n}\n\nfunction extractTokenEndpoint(as: HttpCapture): string | undefined {\n if (as.status !== 200 || !as.body || typeof as.body !== 'object') return undefined;\n const endpoint = (as.body as { token_endpoint?: unknown }).token_endpoint;\n return typeof endpoint === 'string' ? endpoint : undefined;\n}\n\n/** Fields in an OAuth token-endpoint response that should never leak to disk/logs. */\nconst TOKEN_MATERIAL_FIELDS = new Set(['access_token', 'refresh_token', 'id_token']);\n\n/**\n * Redact token-bearing fields from a token-endpoint capture so that `--json`\n * output can safely be pasted into bug reports. The redacted form preserves\n * the original length (useful for \"wrong length\" diagnostics) without leaking\n * the secret. Set `includeTokens: true` on {@link DiagnoseOptions} to skip.\n */\nfunction redactTokenMaterial(capture: HttpCapture): HttpCapture {\n if (!capture.body || typeof capture.body !== 'object' || Array.isArray(capture.body)) return capture;\n const body = capture.body as Record<string, unknown>;\n const redacted: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(body)) {\n if (TOKEN_MATERIAL_FIELDS.has(k) && typeof v === 'string') {\n redacted[k] = `<redacted length=${v.length}>`;\n } else {\n redacted[k] = v;\n }\n }\n return { ...capture, body: redacted };\n}\n\nfunction normalizeForCompare(value: string): string {\n try {\n const u = new URL(value);\n const port = u.port && !isDefaultPort(u.protocol, u.port) ? `:${u.port}` : '';\n const path = u.pathname.length > 1 && u.pathname.endsWith('/') ? u.pathname.slice(0, -1) : u.pathname;\n return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}${path}`;\n } catch {\n return value;\n }\n}\n\n/**\n * Scheme + lowercased host + non-default port. Returns `null` when the input\n * has no host (e.g. `urn:foo`, `mailto:bar@x`) or fails to parse. RFC 8707 §2\n * allows any absolute URI as `resource`, but origin-based comparisons only\n * make sense for hierarchical http(s)-style URLs.\n */\nfunction originForCompare(value: string): string | null {\n try {\n const u = new URL(value);\n if (!u.hostname) return null;\n const port = u.port && !isDefaultPort(u.protocol, u.port) ? `:${u.port}` : '';\n return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Whether `candidate` is `prefix` itself or a strict URL descendant (same\n * scheme+host+port plus a path segment under `prefix`'s path). Inputs come\n * from {@link normalizeForCompare}; trailing slashes are stripped for paths\n * longer than `/`, but the root path `/` is preserved. We require a `/`\n * boundary after the prefix so `.../foo` does not match `.../foobar/...`.\n * Path comparison is case-sensitive per RFC 3986 §6.2.2.1; only scheme/host\n * are lowercased upstream in {@link normalizeForCompare}.\n */\nfunction isUrlPrefix(prefix: string, candidate: string): boolean {\n if (prefix === candidate) return true;\n const trimmed = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n return candidate.startsWith(trimmed + '/');\n}\n\nfunction isDefaultPort(scheme: string, port: string): boolean {\n if (scheme === 'https:' && port === '443') return true;\n if (scheme === 'http:' && port === '80') return true;\n return false;\n}\n\nfunction jwtFromDecoded(_decoded: DecodedAccessToken): string {\n // validateTokenAudience re-decodes from a JWT string, so we round-trip.\n // Since we already decoded this, we can't reconstruct the original signature,\n // but the validator only cares about the claims segment — so we synthesize a\n // three-segment token with the original claims and dummy header/signature.\n const h = base64url(JSON.stringify(_decoded.header));\n const c = base64url(JSON.stringify(_decoded.claims));\n return `${h}.${c}.${_decoded.signature || 'sig'}`;\n}\n\nfunction base64url(input: string): string {\n // `base64` output has at most 2 trailing `=` characters; the bounded form\n // `={0,2}$` avoids the polynomial-regex flag CodeQL raises on `=+$`.\n return Buffer.from(input)\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/={0,2}$/, '');\n}\n\nfunction formatError(err: unknown): string {\n if (err instanceof SsrfRefusedError) {\n return `SSRF guard refused: ${err.code} (${err.message})`;\n }\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
function parseWWWAuthenticate(header) {
|
|
8
2
|
if (!header) return null;
|
|
9
3
|
const trimmed = header.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/auth/oauth/diagnostics.ts"],"sourcesContent":["/**\n * OAuth diagnostics utilities.\n *\n * Small, pure helpers used by `adcp diagnose-auth` and by consumers who want\n * to introspect OAuth wire-level state without rolling their own parsers.\n *\n * None of these helpers perform cryptographic validation. `decodeAccessTokenClaims`\n * reads a JWT without checking its signature, and `validateTokenAudience` only\n * compares the `aud` claim — it does not verify the token is authentic.\n */\n\n/**\n * Parsed WWW-Authenticate challenge (RFC 9110 §11.6.1, RFC 6750, RFC 9728).\n *\n * Only the fields most relevant to OAuth diagnostics are surfaced; unknown\n * auth-params are preserved under `params` so callers can inspect them.\n */\nexport interface WWWAuthenticateChallenge {\n /** Auth-scheme token, e.g. \"Bearer\" or \"DPoP\". Always lowercased. */\n scheme: string;\n /** `realm` auth-param, if present. */\n realm?: string;\n /** `error` auth-param (RFC 6750 §3), e.g. \"invalid_token\". */\n error?: string;\n /** `error_description` auth-param (RFC 6750 §3). */\n error_description?: string;\n /** `scope` auth-param (RFC 6750 §3). */\n scope?: string;\n /** `resource_metadata` auth-param (RFC 9728 §5.3) — URL of the protected-resource metadata document. */\n resource_metadata?: string;\n /** All auth-params (lowercased keys), including unknown ones. */\n params: Record<string, string>;\n}\n\n/**\n * Parse a `WWW-Authenticate` header into its auth-scheme and parameters.\n *\n * Handles the single-challenge case used by MCP servers in practice: one scheme\n * (typically `Bearer`) followed by comma-separated `key=value` or `key=\"value\"`\n * auth-params. Returns `null` for an empty or malformed header.\n *\n * Quoted-string values may contain escaped quotes (`\\\"`) and backslashes (`\\\\`),\n * per RFC 9110 §5.6.4; these are unescaped on the way out.\n *\n * @example\n * ```ts\n * const c = parseWWWAuthenticate(\n * 'Bearer realm=\"api\", error=\"invalid_token\", resource_metadata=\"https://api.example.com/.well-known/oauth-protected-resource\"'\n * );\n * // c.scheme === 'bearer'\n * // c.error === 'invalid_token'\n * // c.resource_metadata === 'https://api.example.com/.well-known/oauth-protected-resource'\n * ```\n */\nexport function parseWWWAuthenticate(header: string | null | undefined): WWWAuthenticateChallenge | null {\n if (!header) return null;\n const trimmed = header.trim();\n if (!trimmed) return null;\n\n // Scheme is the leading token, up to the first whitespace (or end of string for bare schemes).\n const schemeMatch = /^([!#$%&'*+\\-.^_`|~0-9A-Za-z]+)(?:\\s+([\\s\\S]*))?$/.exec(trimmed);\n if (!schemeMatch) return null;\n\n const scheme = schemeMatch[1]!.toLowerCase();\n const rest = schemeMatch[2] ?? '';\n const params: Record<string, string> = {};\n\n // Walk the param list, handling quoted values with backslash escapes.\n let i = 0;\n while (i < rest.length) {\n // skip leading whitespace and commas\n while (i < rest.length && (rest[i] === ' ' || rest[i] === '\\t' || rest[i] === ',')) i++;\n if (i >= rest.length) break;\n\n // read key\n const keyStart = i;\n while (i < rest.length && /[!#$%&'*+\\-.^_`|~0-9A-Za-z]/.test(rest[i]!)) i++;\n const key = rest.slice(keyStart, i).toLowerCase();\n if (!key) break;\n\n // expect '='\n while (i < rest.length && (rest[i] === ' ' || rest[i] === '\\t')) i++;\n if (rest[i] !== '=') {\n // Not a key=value pair — could be a token68. Ignore and move on.\n while (i < rest.length && rest[i] !== ',') i++;\n continue;\n }\n i++; // skip '='\n while (i < rest.length && (rest[i] === ' ' || rest[i] === '\\t')) i++;\n\n // read value: either quoted-string or token\n let value = '';\n if (rest[i] === '\"') {\n i++;\n while (i < rest.length && rest[i] !== '\"') {\n if (rest[i] === '\\\\' && i + 1 < rest.length) {\n value += rest[i + 1];\n i += 2;\n } else {\n value += rest[i];\n i++;\n }\n }\n if (rest[i] === '\"') i++;\n } else {\n const valStart = i;\n while (i < rest.length && rest[i] !== ',' && rest[i] !== ' ' && rest[i] !== '\\t') i++;\n value = rest.slice(valStart, i);\n }\n\n params[key] = value;\n }\n\n return {\n scheme,\n realm: params.realm,\n error: params.error,\n error_description: params.error_description,\n scope: params.scope,\n resource_metadata: params.resource_metadata,\n params,\n };\n}\n\n/**\n * JWT header section (first segment), decoded but unverified.\n */\nexport interface DecodedJWTHeader {\n alg?: string;\n typ?: string;\n kid?: string;\n [key: string]: unknown;\n}\n\n/**\n * JWT claims section (second segment), decoded but unverified.\n *\n * Standard registered claims (RFC 7519 §4.1) are typed; everything else\n * flows through as `unknown`.\n */\nexport interface DecodedJWTClaims {\n iss?: string;\n sub?: string;\n /** Audience — string or array of strings per RFC 7519. */\n aud?: string | string[];\n exp?: number;\n nbf?: number;\n iat?: number;\n jti?: string;\n scope?: string;\n [key: string]: unknown;\n}\n\n/**\n * Result of decoding an access token.\n */\nexport interface DecodedAccessToken {\n header: DecodedJWTHeader;\n claims: DecodedJWTClaims;\n /** Raw signature segment (base64url, unverified). */\n signature: string;\n}\n\n/**\n * Decode a JWT access token without verifying its signature.\n *\n * For diagnostics only — the returned claims MUST NOT be trusted for\n * authorization decisions. Returns `null` if the token is not a well-formed\n * three-part JWT, or if the header/claims segments are not valid JSON.\n *\n * Opaque (non-JWT) tokens always return `null`, which is the expected outcome\n * for servers that issue reference tokens rather than JWTs.\n */\nexport function decodeAccessTokenClaims(token: string | null | undefined): DecodedAccessToken | null {\n if (!token || typeof token !== 'string') return null;\n\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n\n try {\n const header = JSON.parse(base64UrlDecode(parts[0]!)) as DecodedJWTHeader;\n const claims = JSON.parse(base64UrlDecode(parts[1]!)) as DecodedJWTClaims;\n if (typeof header !== 'object' || header === null) return null;\n if (typeof claims !== 'object' || claims === null) return null;\n return { header, claims, signature: parts[2]! };\n } catch {\n return null;\n }\n}\n\n/**\n * Result of audience validation.\n */\nexport interface TokenAudienceResult {\n ok: boolean;\n /** Human-readable reason when `ok` is false. */\n reason?: string;\n /** The `aud` claim value actually found in the token (undefined if missing or token unparseable). */\n actualAudience?: string | string[];\n}\n\n/**\n * Check whether an access token's `aud` claim matches an expected resource URL.\n *\n * Returns `{ ok: true }` when the `aud` claim is a string equal to\n * `expectedResource` (after URL normalization), or is an array containing\n * such a string. Returns `{ ok: false, reason }` otherwise, including when\n * the token is opaque (not a JWT) or has no `aud` claim.\n *\n * URL normalization: lowercased scheme and host, default ports (80 for http,\n * 443 for https) stripped, trailing slash on the path stripped. Query and\n * fragment are preserved verbatim. Non-URL audience strings are compared\n * byte-for-byte.\n *\n * Defense-in-depth helper. A server that mis-issues a token with the wrong\n * `aud` would still be accepted by the resource server; this helper flags\n * the mismatch on the client side for diagnostics.\n */\nexport function validateTokenAudience(token: string | null | undefined, expectedResource: string): TokenAudienceResult {\n const decoded = decodeAccessTokenClaims(token ?? undefined);\n if (!decoded) {\n return { ok: false, reason: 'Token is opaque or not a valid JWT; audience cannot be inspected' };\n }\n\n const aud = decoded.claims.aud;\n if (aud === undefined) {\n return { ok: false, reason: 'Token has no `aud` claim (RFC 8707 violation for resource-indicator flows)' };\n }\n\n const expected = normalizeResource(expectedResource);\n const audList = Array.isArray(aud) ? aud : [aud];\n\n for (const candidate of audList) {\n if (typeof candidate !== 'string') continue;\n if (normalizeResource(candidate) === expected) {\n return { ok: true, actualAudience: aud };\n }\n }\n\n return {\n ok: false,\n reason: `Token \\`aud\\` claim does not match expected resource \"${expectedResource}\"`,\n actualAudience: aud,\n };\n}\n\nfunction normalizeResource(value: string): string {\n try {\n const u = new URL(value);\n const scheme = u.protocol.toLowerCase();\n const host = u.hostname.toLowerCase();\n const port = u.port && !isDefaultPort(scheme, u.port) ? `:${u.port}` : '';\n const path = u.pathname.length > 1 && u.pathname.endsWith('/') ? u.pathname.slice(0, -1) : u.pathname;\n return `${scheme}//${host}${port}${path}${u.search}${u.hash}`;\n } catch {\n return value;\n }\n}\n\nfunction isDefaultPort(scheme: string, port: string): boolean {\n if (scheme === 'https:' && port === '443') return true;\n if (scheme === 'http:' && port === '80') return true;\n return false;\n}\n\nfunction base64UrlDecode(input: string): string {\n const padded = input.replace(/-/g, '+').replace(/_/g, '/');\n const padLen = padded.length % 4;\n const full = padLen ? padded + '='.repeat(4 - padLen) : padded;\n return Buffer.from(full, 'base64').toString('utf8');\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/auth/oauth/diagnostics.ts"],"sourcesContent":["/**\n * OAuth diagnostics utilities.\n *\n * Small, pure helpers used by `adcp diagnose-auth` and by consumers who want\n * to introspect OAuth wire-level state without rolling their own parsers.\n *\n * None of these helpers perform cryptographic validation. `decodeAccessTokenClaims`\n * reads a JWT without checking its signature, and `validateTokenAudience` only\n * compares the `aud` claim — it does not verify the token is authentic.\n */\n\n/**\n * Parsed WWW-Authenticate challenge (RFC 9110 §11.6.1, RFC 6750, RFC 9728).\n *\n * Only the fields most relevant to OAuth diagnostics are surfaced; unknown\n * auth-params are preserved under `params` so callers can inspect them.\n */\nexport interface WWWAuthenticateChallenge {\n /** Auth-scheme token, e.g. \"Bearer\" or \"DPoP\". Always lowercased. */\n scheme: string;\n /** `realm` auth-param, if present. */\n realm?: string;\n /** `error` auth-param (RFC 6750 §3), e.g. \"invalid_token\". */\n error?: string;\n /** `error_description` auth-param (RFC 6750 §3). */\n error_description?: string;\n /** `scope` auth-param (RFC 6750 §3). */\n scope?: string;\n /** `resource_metadata` auth-param (RFC 9728 §5.3) — URL of the protected-resource metadata document. */\n resource_metadata?: string;\n /** All auth-params (lowercased keys), including unknown ones. */\n params: Record<string, string>;\n}\n\n/**\n * Parse a `WWW-Authenticate` header into its auth-scheme and parameters.\n *\n * Handles the single-challenge case used by MCP servers in practice: one scheme\n * (typically `Bearer`) followed by comma-separated `key=value` or `key=\"value\"`\n * auth-params. Returns `null` for an empty or malformed header.\n *\n * Quoted-string values may contain escaped quotes (`\\\"`) and backslashes (`\\\\`),\n * per RFC 9110 §5.6.4; these are unescaped on the way out.\n *\n * @example\n * ```ts\n * const c = parseWWWAuthenticate(\n * 'Bearer realm=\"api\", error=\"invalid_token\", resource_metadata=\"https://api.example.com/.well-known/oauth-protected-resource\"'\n * );\n * // c.scheme === 'bearer'\n * // c.error === 'invalid_token'\n * // c.resource_metadata === 'https://api.example.com/.well-known/oauth-protected-resource'\n * ```\n */\nexport function parseWWWAuthenticate(header: string | null | undefined): WWWAuthenticateChallenge | null {\n if (!header) return null;\n const trimmed = header.trim();\n if (!trimmed) return null;\n\n // Scheme is the leading token, up to the first whitespace (or end of string for bare schemes).\n const schemeMatch = /^([!#$%&'*+\\-.^_`|~0-9A-Za-z]+)(?:\\s+([\\s\\S]*))?$/.exec(trimmed);\n if (!schemeMatch) return null;\n\n const scheme = schemeMatch[1]!.toLowerCase();\n const rest = schemeMatch[2] ?? '';\n const params: Record<string, string> = {};\n\n // Walk the param list, handling quoted values with backslash escapes.\n let i = 0;\n while (i < rest.length) {\n // skip leading whitespace and commas\n while (i < rest.length && (rest[i] === ' ' || rest[i] === '\\t' || rest[i] === ',')) i++;\n if (i >= rest.length) break;\n\n // read key\n const keyStart = i;\n while (i < rest.length && /[!#$%&'*+\\-.^_`|~0-9A-Za-z]/.test(rest[i]!)) i++;\n const key = rest.slice(keyStart, i).toLowerCase();\n if (!key) break;\n\n // expect '='\n while (i < rest.length && (rest[i] === ' ' || rest[i] === '\\t')) i++;\n if (rest[i] !== '=') {\n // Not a key=value pair — could be a token68. Ignore and move on.\n while (i < rest.length && rest[i] !== ',') i++;\n continue;\n }\n i++; // skip '='\n while (i < rest.length && (rest[i] === ' ' || rest[i] === '\\t')) i++;\n\n // read value: either quoted-string or token\n let value = '';\n if (rest[i] === '\"') {\n i++;\n while (i < rest.length && rest[i] !== '\"') {\n if (rest[i] === '\\\\' && i + 1 < rest.length) {\n value += rest[i + 1];\n i += 2;\n } else {\n value += rest[i];\n i++;\n }\n }\n if (rest[i] === '\"') i++;\n } else {\n const valStart = i;\n while (i < rest.length && rest[i] !== ',' && rest[i] !== ' ' && rest[i] !== '\\t') i++;\n value = rest.slice(valStart, i);\n }\n\n params[key] = value;\n }\n\n return {\n scheme,\n realm: params.realm,\n error: params.error,\n error_description: params.error_description,\n scope: params.scope,\n resource_metadata: params.resource_metadata,\n params,\n };\n}\n\n/**\n * JWT header section (first segment), decoded but unverified.\n */\nexport interface DecodedJWTHeader {\n alg?: string;\n typ?: string;\n kid?: string;\n [key: string]: unknown;\n}\n\n/**\n * JWT claims section (second segment), decoded but unverified.\n *\n * Standard registered claims (RFC 7519 §4.1) are typed; everything else\n * flows through as `unknown`.\n */\nexport interface DecodedJWTClaims {\n iss?: string;\n sub?: string;\n /** Audience — string or array of strings per RFC 7519. */\n aud?: string | string[];\n exp?: number;\n nbf?: number;\n iat?: number;\n jti?: string;\n scope?: string;\n [key: string]: unknown;\n}\n\n/**\n * Result of decoding an access token.\n */\nexport interface DecodedAccessToken {\n header: DecodedJWTHeader;\n claims: DecodedJWTClaims;\n /** Raw signature segment (base64url, unverified). */\n signature: string;\n}\n\n/**\n * Decode a JWT access token without verifying its signature.\n *\n * For diagnostics only — the returned claims MUST NOT be trusted for\n * authorization decisions. Returns `null` if the token is not a well-formed\n * three-part JWT, or if the header/claims segments are not valid JSON.\n *\n * Opaque (non-JWT) tokens always return `null`, which is the expected outcome\n * for servers that issue reference tokens rather than JWTs.\n */\nexport function decodeAccessTokenClaims(token: string | null | undefined): DecodedAccessToken | null {\n if (!token || typeof token !== 'string') return null;\n\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n\n try {\n const header = JSON.parse(base64UrlDecode(parts[0]!)) as DecodedJWTHeader;\n const claims = JSON.parse(base64UrlDecode(parts[1]!)) as DecodedJWTClaims;\n if (typeof header !== 'object' || header === null) return null;\n if (typeof claims !== 'object' || claims === null) return null;\n return { header, claims, signature: parts[2]! };\n } catch {\n return null;\n }\n}\n\n/**\n * Result of audience validation.\n */\nexport interface TokenAudienceResult {\n ok: boolean;\n /** Human-readable reason when `ok` is false. */\n reason?: string;\n /** The `aud` claim value actually found in the token (undefined if missing or token unparseable). */\n actualAudience?: string | string[];\n}\n\n/**\n * Check whether an access token's `aud` claim matches an expected resource URL.\n *\n * Returns `{ ok: true }` when the `aud` claim is a string equal to\n * `expectedResource` (after URL normalization), or is an array containing\n * such a string. Returns `{ ok: false, reason }` otherwise, including when\n * the token is opaque (not a JWT) or has no `aud` claim.\n *\n * URL normalization: lowercased scheme and host, default ports (80 for http,\n * 443 for https) stripped, trailing slash on the path stripped. Query and\n * fragment are preserved verbatim. Non-URL audience strings are compared\n * byte-for-byte.\n *\n * Defense-in-depth helper. A server that mis-issues a token with the wrong\n * `aud` would still be accepted by the resource server; this helper flags\n * the mismatch on the client side for diagnostics.\n */\nexport function validateTokenAudience(token: string | null | undefined, expectedResource: string): TokenAudienceResult {\n const decoded = decodeAccessTokenClaims(token ?? undefined);\n if (!decoded) {\n return { ok: false, reason: 'Token is opaque or not a valid JWT; audience cannot be inspected' };\n }\n\n const aud = decoded.claims.aud;\n if (aud === undefined) {\n return { ok: false, reason: 'Token has no `aud` claim (RFC 8707 violation for resource-indicator flows)' };\n }\n\n const expected = normalizeResource(expectedResource);\n const audList = Array.isArray(aud) ? aud : [aud];\n\n for (const candidate of audList) {\n if (typeof candidate !== 'string') continue;\n if (normalizeResource(candidate) === expected) {\n return { ok: true, actualAudience: aud };\n }\n }\n\n return {\n ok: false,\n reason: `Token \\`aud\\` claim does not match expected resource \"${expectedResource}\"`,\n actualAudience: aud,\n };\n}\n\nfunction normalizeResource(value: string): string {\n try {\n const u = new URL(value);\n const scheme = u.protocol.toLowerCase();\n const host = u.hostname.toLowerCase();\n const port = u.port && !isDefaultPort(scheme, u.port) ? `:${u.port}` : '';\n const path = u.pathname.length > 1 && u.pathname.endsWith('/') ? u.pathname.slice(0, -1) : u.pathname;\n return `${scheme}//${host}${port}${path}${u.search}${u.hash}`;\n } catch {\n return value;\n }\n}\n\nfunction isDefaultPort(scheme: string, port: string): boolean {\n if (scheme === 'https:' && port === '443') return true;\n if (scheme === 'http:' && port === '80') return true;\n return false;\n}\n\nfunction base64UrlDecode(input: string): string {\n const padded = input.replace(/-/g, '+').replace(/_/g, '/');\n const padLen = padded.length % 4;\n const full = padLen ? padded + '='.repeat(4 - padLen) : padded;\n return Buffer.from(full, 'base64').toString('utf8');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { wrapFetchWithSizeLimit } from "../../protocols/responseSizeLimit.mjs";
|
|
8
2
|
async function discoverOAuthMetadata(agentUrl, options = {}) {
|
|
9
3
|
const { timeout = 5e3, fetch: providedFetch = fetch } = options;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/auth/oauth/discovery.ts"],"sourcesContent":["/**\n * OAuth Discovery utilities\n *\n * Functions to discover OAuth capabilities of MCP servers.\n * MCP servers expose OAuth metadata at /.well-known/oauth-authorization-server\n */\n\nimport { wrapFetchWithSizeLimit } from '../../protocols/responseSizeLimit';\n\n/**\n * OAuth Authorization Server Metadata\n * @see https://datatracker.ietf.org/doc/html/rfc8414\n */\nexport interface OAuthMetadata {\n /** URL of the authorization endpoint */\n authorization_endpoint: string;\n /** URL of the token endpoint */\n token_endpoint: string;\n /** URL of the dynamic client registration endpoint (optional) */\n registration_endpoint?: string;\n /** Issuer identifier */\n issuer?: string;\n /** PKCE code challenge methods supported */\n code_challenge_methods_supported?: string[];\n /** Response types supported */\n response_types_supported?: string[];\n /** Grant types supported */\n grant_types_supported?: string[];\n /** Token endpoint auth methods supported */\n token_endpoint_auth_methods_supported?: string[];\n /** Scopes supported */\n scopes_supported?: string[];\n}\n\n/**\n * Options for OAuth discovery\n */\nexport interface DiscoveryOptions {\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Custom fetch function (for testing or custom HTTP handling) */\n fetch?: typeof fetch;\n}\n\n/**\n * Discover OAuth metadata from an MCP server\n *\n * Fetches the OAuth Authorization Server Metadata from the well-known endpoint.\n * Returns null if the server doesn't support OAuth or the metadata isn't available.\n *\n * @param agentUrl - The MCP server URL\n * @param options - Discovery options\n * @returns OAuth metadata or null if not available\n *\n * @example\n * ```typescript\n * const metadata = await discoverOAuthMetadata('https://agent.example.com/mcp');\n * if (metadata) {\n * console.log('Authorization URL:', metadata.authorization_endpoint);\n * console.log('Supports dynamic registration:', !!metadata.registration_endpoint);\n * }\n * ```\n */\nexport async function discoverOAuthMetadata(\n agentUrl: string,\n options: DiscoveryOptions = {}\n): Promise<OAuthMetadata | null> {\n // Wrap with the response-size-limit guard so a hostile metadata endpoint\n // can't buffer-bomb during discovery. Pass-through when no\n // `withResponseSizeLimit` slot is active. (#1175)\n const { timeout = 5000, fetch: providedFetch = fetch } = options;\n const customFetch = wrapFetchWithSizeLimit(providedFetch);\n\n try {\n const baseUrl = new URL(agentUrl);\n\n // RFC 8414 path-aware discovery: try path-suffixed URL first, then fall back to root.\n // For https://example.com/mcp, try:\n // 1. https://example.com/.well-known/oauth-authorization-server/mcp\n // 2. https://example.com/.well-known/oauth-authorization-server (fallback)\n const pathname = baseUrl.pathname.endsWith('/') ? baseUrl.pathname.slice(0, -1) : baseUrl.pathname;\n const hasPath = pathname !== '' && pathname !== '/';\n\n const pathAwareUrl = hasPath ? new URL(`/.well-known/oauth-authorization-server${pathname}`, baseUrl.origin) : null;\n const rootUrl = new URL('/.well-known/oauth-authorization-server', baseUrl.origin);\n\n const urlsToTry = pathAwareUrl ? [pathAwareUrl, rootUrl] : [rootUrl];\n\n for (const metadataUrl of urlsToTry) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await customFetch(metadataUrl.toString(), {\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n continue;\n }\n\n const metadata = (await response.json()) as OAuthMetadata;\n\n // Validate required fields\n if (!metadata.authorization_endpoint || !metadata.token_endpoint) {\n continue;\n }\n\n return metadata;\n } catch {\n // Parse error, timeout, or network error on this URL -- try next\n continue;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n return null;\n } catch {\n // Network error, timeout, or invalid URL - agent doesn't support OAuth\n return null;\n }\n}\n\n/**\n * Check if an MCP server supports OAuth authentication\n *\n * This is a simple boolean check - use discoverOAuthMetadata() if you need\n * the actual endpoints.\n *\n * @param agentUrl - The MCP server URL\n * @param options - Discovery options\n * @returns true if the server supports OAuth\n *\n * @example\n * ```typescript\n * if (await supportsOAuth('https://agent.example.com/mcp')) {\n * console.log('Agent requires OAuth authentication');\n * }\n * ```\n */\nexport async function supportsOAuth(agentUrl: string, options: DiscoveryOptions = {}): Promise<boolean> {\n const metadata = await discoverOAuthMetadata(agentUrl, options);\n return metadata !== null;\n}\n\n/**\n * Check if an MCP server supports dynamic client registration\n *\n * Servers that support dynamic registration allow clients to register\n * automatically without pre-configured credentials.\n *\n * @param agentUrl - The MCP server URL\n * @param options - Discovery options\n * @returns true if the server supports dynamic client registration\n *\n * @example\n * ```typescript\n * if (await supportsDynamicRegistration('https://agent.example.com/mcp')) {\n * console.log('Agent supports automatic client registration');\n * }\n * ```\n */\nexport async function supportsDynamicRegistration(agentUrl: string, options: DiscoveryOptions = {}): Promise<boolean> {\n const metadata = await discoverOAuthMetadata(agentUrl, options);\n return metadata?.registration_endpoint !== undefined;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/auth/oauth/discovery.ts"],"sourcesContent":["/**\n * OAuth Discovery utilities\n *\n * Functions to discover OAuth capabilities of MCP servers.\n * MCP servers expose OAuth metadata at /.well-known/oauth-authorization-server\n */\n\nimport { wrapFetchWithSizeLimit } from '../../protocols/responseSizeLimit';\n\n/**\n * OAuth Authorization Server Metadata\n * @see https://datatracker.ietf.org/doc/html/rfc8414\n */\nexport interface OAuthMetadata {\n /** URL of the authorization endpoint */\n authorization_endpoint: string;\n /** URL of the token endpoint */\n token_endpoint: string;\n /** URL of the dynamic client registration endpoint (optional) */\n registration_endpoint?: string;\n /** Issuer identifier */\n issuer?: string;\n /** PKCE code challenge methods supported */\n code_challenge_methods_supported?: string[];\n /** Response types supported */\n response_types_supported?: string[];\n /** Grant types supported */\n grant_types_supported?: string[];\n /** Token endpoint auth methods supported */\n token_endpoint_auth_methods_supported?: string[];\n /** Scopes supported */\n scopes_supported?: string[];\n}\n\n/**\n * Options for OAuth discovery\n */\nexport interface DiscoveryOptions {\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Custom fetch function (for testing or custom HTTP handling) */\n fetch?: typeof fetch;\n}\n\n/**\n * Discover OAuth metadata from an MCP server\n *\n * Fetches the OAuth Authorization Server Metadata from the well-known endpoint.\n * Returns null if the server doesn't support OAuth or the metadata isn't available.\n *\n * @param agentUrl - The MCP server URL\n * @param options - Discovery options\n * @returns OAuth metadata or null if not available\n *\n * @example\n * ```typescript\n * const metadata = await discoverOAuthMetadata('https://agent.example.com/mcp');\n * if (metadata) {\n * console.log('Authorization URL:', metadata.authorization_endpoint);\n * console.log('Supports dynamic registration:', !!metadata.registration_endpoint);\n * }\n * ```\n */\nexport async function discoverOAuthMetadata(\n agentUrl: string,\n options: DiscoveryOptions = {}\n): Promise<OAuthMetadata | null> {\n // Wrap with the response-size-limit guard so a hostile metadata endpoint\n // can't buffer-bomb during discovery. Pass-through when no\n // `withResponseSizeLimit` slot is active. (#1175)\n const { timeout = 5000, fetch: providedFetch = fetch } = options;\n const customFetch = wrapFetchWithSizeLimit(providedFetch);\n\n try {\n const baseUrl = new URL(agentUrl);\n\n // RFC 8414 path-aware discovery: try path-suffixed URL first, then fall back to root.\n // For https://example.com/mcp, try:\n // 1. https://example.com/.well-known/oauth-authorization-server/mcp\n // 2. https://example.com/.well-known/oauth-authorization-server (fallback)\n const pathname = baseUrl.pathname.endsWith('/') ? baseUrl.pathname.slice(0, -1) : baseUrl.pathname;\n const hasPath = pathname !== '' && pathname !== '/';\n\n const pathAwareUrl = hasPath ? new URL(`/.well-known/oauth-authorization-server${pathname}`, baseUrl.origin) : null;\n const rootUrl = new URL('/.well-known/oauth-authorization-server', baseUrl.origin);\n\n const urlsToTry = pathAwareUrl ? [pathAwareUrl, rootUrl] : [rootUrl];\n\n for (const metadataUrl of urlsToTry) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await customFetch(metadataUrl.toString(), {\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n continue;\n }\n\n const metadata = (await response.json()) as OAuthMetadata;\n\n // Validate required fields\n if (!metadata.authorization_endpoint || !metadata.token_endpoint) {\n continue;\n }\n\n return metadata;\n } catch {\n // Parse error, timeout, or network error on this URL -- try next\n continue;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n return null;\n } catch {\n // Network error, timeout, or invalid URL - agent doesn't support OAuth\n return null;\n }\n}\n\n/**\n * Check if an MCP server supports OAuth authentication\n *\n * This is a simple boolean check - use discoverOAuthMetadata() if you need\n * the actual endpoints.\n *\n * @param agentUrl - The MCP server URL\n * @param options - Discovery options\n * @returns true if the server supports OAuth\n *\n * @example\n * ```typescript\n * if (await supportsOAuth('https://agent.example.com/mcp')) {\n * console.log('Agent requires OAuth authentication');\n * }\n * ```\n */\nexport async function supportsOAuth(agentUrl: string, options: DiscoveryOptions = {}): Promise<boolean> {\n const metadata = await discoverOAuthMetadata(agentUrl, options);\n return metadata !== null;\n}\n\n/**\n * Check if an MCP server supports dynamic client registration\n *\n * Servers that support dynamic registration allow clients to register\n * automatically without pre-configured credentials.\n *\n * @param agentUrl - The MCP server URL\n * @param options - Discovery options\n * @returns true if the server supports dynamic client registration\n *\n * @example\n * ```typescript\n * if (await supportsDynamicRegistration('https://agent.example.com/mcp')) {\n * console.log('Agent supports automatic client registration');\n * }\n * ```\n */\nexport async function supportsDynamicRegistration(agentUrl: string, options: DiscoveryOptions = {}): Promise<boolean> {\n const metadata = await discoverOAuthMetadata(agentUrl, options);\n return metadata?.registration_endpoint !== undefined;\n}\n"],"mappings":"AAOA,SAAS,8BAA8B;AAwDvC,eAAsB,sBACpB,UACA,UAA4B,CAAC,GACE;AAI/B,QAAM,EAAE,UAAU,KAAM,OAAO,gBAAgB,MAAM,IAAI;AACzD,QAAM,cAAc,uBAAuB,aAAa;AAExD,MAAI;AACF,UAAM,UAAU,IAAI,IAAI,QAAQ;AAMhC,UAAM,WAAW,QAAQ,SAAS,SAAS,GAAG,IAAI,QAAQ,SAAS,MAAM,GAAG,EAAE,IAAI,QAAQ;AAC1F,UAAM,UAAU,aAAa,MAAM,aAAa;AAEhD,UAAM,eAAe,UAAU,IAAI,IAAI,0CAA0C,QAAQ,IAAI,QAAQ,MAAM,IAAI;AAC/G,UAAM,UAAU,IAAI,IAAI,2CAA2C,QAAQ,MAAM;AAEjF,UAAM,YAAY,eAAe,CAAC,cAAc,OAAO,IAAI,CAAC,OAAO;AAEnE,eAAW,eAAe,WAAW;AACnC,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,UAAI;AACF,cAAM,WAAW,MAAM,YAAY,YAAY,SAAS,GAAG;AAAA,UACzD,SAAS,EAAE,QAAQ,mBAAmB;AAAA,UACtC,QAAQ,WAAW;AAAA,QACrB,CAAC;AAED,YAAI,CAAC,SAAS,IAAI;AAChB;AAAA,QACF;AAEA,cAAM,WAAY,MAAM,SAAS,KAAK;AAGtC,YAAI,CAAC,SAAS,0BAA0B,CAAC,SAAS,gBAAgB;AAChE;AAAA,QACF;AAEA,eAAO;AAAA,MACT,QAAQ;AAEN;AAAA,MACF,UAAE;AACA,qBAAa,SAAS;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,EACT,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAmBA,eAAsB,cAAc,UAAkB,UAA4B,CAAC,GAAqB;AACtG,QAAM,WAAW,MAAM,sBAAsB,UAAU,OAAO;AAC9D,SAAO,aAAa;AACtB;AAmBA,eAAsB,4BAA4B,UAAkB,UAA4B,CAAC,GAAqB;AACpH,QAAM,WAAW,MAAM,sBAAsB,UAAU,OAAO;AAC9D,SAAO,UAAU,0BAA0B;AAC7C;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { promises as fs } from "fs";
|
|
8
2
|
import path from "path";
|
|
9
3
|
import { randomBytes } from "crypto";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/auth/oauth/file-storage.ts"],"sourcesContent":["/**\n * File-backed {@link OAuthConfigStorage} implementation.\n *\n * Persists refreshed OAuth tokens back to a JSON file so that subsequent\n * `callTool` invocations use the latest access + refresh tokens without\n * triggering a redundant refresh (or worse, a re-login).\n *\n * On-disk format is the shape used by the `adcp` CLI's agents.json — agents\n * keyed by alias, each with `{ url, protocol, auth_token?, oauth_tokens?, oauth_client? }`.\n * Library consumers who want a different shape can implement\n * {@link OAuthConfigStorage} directly.\n */\nimport { promises as fs } from 'fs';\nimport path from 'path';\nimport { randomBytes } from 'crypto';\nimport type { AgentConfig } from '../../types/adcp';\nimport type { OAuthConfigStorage } from './types';\n\n/**\n * Shape of a single agent record on disk (matches the `adcp` CLI's config format).\n */\ninterface StoredAgent {\n url: string;\n protocol?: 'mcp' | 'a2a';\n auth_token?: string;\n oauth_tokens?: AgentConfig['oauth_tokens'];\n oauth_client?: AgentConfig['oauth_client'];\n oauth_client_credentials?: AgentConfig['oauth_client_credentials'];\n oauth_code_verifier?: string;\n}\n\n/**\n * Shape of the whole config file.\n */\ninterface StoredConfig {\n agents: Record<string, StoredAgent>;\n defaults?: Record<string, unknown>;\n}\n\n/**\n * Options for {@link createFileOAuthStorage}.\n */\nexport interface FileOAuthStorageOptions {\n /** Absolute path to the config JSON file. Must be a file, not a directory. */\n configPath: string;\n /**\n * Key persistence under a stable alias regardless of `agent.id`. Use this\n * when the in-memory `AgentConfig` carries an ephemeral identifier (e.g.\n * a per-request tenant id, or a synthetic `cli-agent`) but on-disk storage\n * should be organized by a long-lived alias chosen by the operator.\n */\n agentKey?: string;\n /**\n * Create the parent directory (with mode 0o700) if it doesn't exist.\n * Default true — CLI users expect first-run to create `~/.adcp/`.\n */\n autoCreateDir?: boolean;\n /** File mode for the config file when we create it. Default 0o600. */\n fileMode?: number;\n}\n\n/**\n * Create a file-backed `OAuthConfigStorage`.\n *\n * Reads are cheap: a single JSON.parse per call. Writes are atomic via\n * `write-then-rename` so a crash mid-save cannot produce a half-written file.\n *\n * Does NOT touch fields outside the OAuth envelope (`auth_token`, custom\n * fields) when saving — the MCP provider only mutates `oauth_tokens`,\n * `oauth_client`, and `oauth_code_verifier`, and we preserve everything else.\n *\n * @example\n * ```ts\n * const storage = createFileOAuthStorage({ configPath: '/home/user/.adcp/config.json' });\n * const provider = createNonInteractiveOAuthProvider(agent, { storage });\n * ```\n */\nexport function createFileOAuthStorage(options: FileOAuthStorageOptions): OAuthConfigStorage {\n const configPath = options.configPath;\n const agentKeyOverride = options.agentKey;\n const autoCreateDir = options.autoCreateDir ?? true;\n const fileMode = options.fileMode ?? 0o600;\n\n async function readConfig(): Promise<StoredConfig> {\n try {\n const raw = await fs.readFile(configPath, 'utf-8');\n const parsed = JSON.parse(raw) as StoredConfig;\n if (!parsed.agents || typeof parsed.agents !== 'object') parsed.agents = {};\n return parsed;\n } catch (err) {\n if (isNotFound(err)) return { agents: {} };\n throw err;\n }\n }\n\n async function writeConfig(config: StoredConfig): Promise<void> {\n if (autoCreateDir) {\n await fs.mkdir(path.dirname(configPath), { recursive: true, mode: 0o700 });\n }\n // Atomic write: stage next to the target, then rename. Same filesystem →\n // rename is atomic on POSIX and on NTFS (MoveFileEx under the hood).\n // Random suffix guards against two concurrent saves in the same process\n // racing on the same temp path.\n const tempPath = `${configPath}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`;\n const serialized = JSON.stringify(config, null, 2);\n await fs.writeFile(tempPath, serialized, { mode: fileMode });\n await fs.rename(tempPath, configPath);\n }\n\n return {\n async loadAgent(agentId: string): Promise<AgentConfig | undefined> {\n const key = agentKeyOverride ?? agentId;\n const config = await readConfig();\n const stored = config.agents[key];\n if (!stored) return undefined;\n return {\n id: agentId,\n name: agentId,\n agent_uri: stored.url,\n protocol: stored.protocol ?? 'mcp',\n auth_token: stored.auth_token,\n oauth_tokens: stored.oauth_tokens,\n oauth_client: stored.oauth_client,\n oauth_client_credentials: stored.oauth_client_credentials,\n oauth_code_verifier: stored.oauth_code_verifier,\n };\n },\n\n async saveAgent(agent: AgentConfig): Promise<void> {\n const key = agentKeyOverride ?? agent.id;\n const config = await readConfig();\n const existing = config.agents[key] ?? { url: agent.agent_uri };\n config.agents[key] = {\n ...existing,\n url: agent.agent_uri,\n protocol: agent.protocol,\n ...(agent.auth_token !== undefined ? { auth_token: agent.auth_token } : {}),\n ...(agent.oauth_tokens !== undefined ? { oauth_tokens: agent.oauth_tokens } : {}),\n ...(agent.oauth_client !== undefined ? { oauth_client: agent.oauth_client } : {}),\n ...(agent.oauth_client_credentials !== undefined\n ? { oauth_client_credentials: agent.oauth_client_credentials }\n : {}),\n ...(agent.oauth_code_verifier !== undefined ? { oauth_code_verifier: agent.oauth_code_verifier } : {}),\n };\n await writeConfig(config);\n },\n };\n}\n\nfunction isNotFound(err: unknown): boolean {\n return Boolean(err && typeof err === 'object' && (err as { code?: string }).code === 'ENOENT');\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/auth/oauth/file-storage.ts"],"sourcesContent":["/**\n * File-backed {@link OAuthConfigStorage} implementation.\n *\n * Persists refreshed OAuth tokens back to a JSON file so that subsequent\n * `callTool` invocations use the latest access + refresh tokens without\n * triggering a redundant refresh (or worse, a re-login).\n *\n * On-disk format is the shape used by the `adcp` CLI's agents.json — agents\n * keyed by alias, each with `{ url, protocol, auth_token?, oauth_tokens?, oauth_client? }`.\n * Library consumers who want a different shape can implement\n * {@link OAuthConfigStorage} directly.\n */\nimport { promises as fs } from 'fs';\nimport path from 'path';\nimport { randomBytes } from 'crypto';\nimport type { AgentConfig } from '../../types/adcp';\nimport type { OAuthConfigStorage } from './types';\n\n/**\n * Shape of a single agent record on disk (matches the `adcp` CLI's config format).\n */\ninterface StoredAgent {\n url: string;\n protocol?: 'mcp' | 'a2a';\n auth_token?: string;\n oauth_tokens?: AgentConfig['oauth_tokens'];\n oauth_client?: AgentConfig['oauth_client'];\n oauth_client_credentials?: AgentConfig['oauth_client_credentials'];\n oauth_code_verifier?: string;\n}\n\n/**\n * Shape of the whole config file.\n */\ninterface StoredConfig {\n agents: Record<string, StoredAgent>;\n defaults?: Record<string, unknown>;\n}\n\n/**\n * Options for {@link createFileOAuthStorage}.\n */\nexport interface FileOAuthStorageOptions {\n /** Absolute path to the config JSON file. Must be a file, not a directory. */\n configPath: string;\n /**\n * Key persistence under a stable alias regardless of `agent.id`. Use this\n * when the in-memory `AgentConfig` carries an ephemeral identifier (e.g.\n * a per-request tenant id, or a synthetic `cli-agent`) but on-disk storage\n * should be organized by a long-lived alias chosen by the operator.\n */\n agentKey?: string;\n /**\n * Create the parent directory (with mode 0o700) if it doesn't exist.\n * Default true — CLI users expect first-run to create `~/.adcp/`.\n */\n autoCreateDir?: boolean;\n /** File mode for the config file when we create it. Default 0o600. */\n fileMode?: number;\n}\n\n/**\n * Create a file-backed `OAuthConfigStorage`.\n *\n * Reads are cheap: a single JSON.parse per call. Writes are atomic via\n * `write-then-rename` so a crash mid-save cannot produce a half-written file.\n *\n * Does NOT touch fields outside the OAuth envelope (`auth_token`, custom\n * fields) when saving — the MCP provider only mutates `oauth_tokens`,\n * `oauth_client`, and `oauth_code_verifier`, and we preserve everything else.\n *\n * @example\n * ```ts\n * const storage = createFileOAuthStorage({ configPath: '/home/user/.adcp/config.json' });\n * const provider = createNonInteractiveOAuthProvider(agent, { storage });\n * ```\n */\nexport function createFileOAuthStorage(options: FileOAuthStorageOptions): OAuthConfigStorage {\n const configPath = options.configPath;\n const agentKeyOverride = options.agentKey;\n const autoCreateDir = options.autoCreateDir ?? true;\n const fileMode = options.fileMode ?? 0o600;\n\n async function readConfig(): Promise<StoredConfig> {\n try {\n const raw = await fs.readFile(configPath, 'utf-8');\n const parsed = JSON.parse(raw) as StoredConfig;\n if (!parsed.agents || typeof parsed.agents !== 'object') parsed.agents = {};\n return parsed;\n } catch (err) {\n if (isNotFound(err)) return { agents: {} };\n throw err;\n }\n }\n\n async function writeConfig(config: StoredConfig): Promise<void> {\n if (autoCreateDir) {\n await fs.mkdir(path.dirname(configPath), { recursive: true, mode: 0o700 });\n }\n // Atomic write: stage next to the target, then rename. Same filesystem →\n // rename is atomic on POSIX and on NTFS (MoveFileEx under the hood).\n // Random suffix guards against two concurrent saves in the same process\n // racing on the same temp path.\n const tempPath = `${configPath}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`;\n const serialized = JSON.stringify(config, null, 2);\n await fs.writeFile(tempPath, serialized, { mode: fileMode });\n await fs.rename(tempPath, configPath);\n }\n\n return {\n async loadAgent(agentId: string): Promise<AgentConfig | undefined> {\n const key = agentKeyOverride ?? agentId;\n const config = await readConfig();\n const stored = config.agents[key];\n if (!stored) return undefined;\n return {\n id: agentId,\n name: agentId,\n agent_uri: stored.url,\n protocol: stored.protocol ?? 'mcp',\n auth_token: stored.auth_token,\n oauth_tokens: stored.oauth_tokens,\n oauth_client: stored.oauth_client,\n oauth_client_credentials: stored.oauth_client_credentials,\n oauth_code_verifier: stored.oauth_code_verifier,\n };\n },\n\n async saveAgent(agent: AgentConfig): Promise<void> {\n const key = agentKeyOverride ?? agent.id;\n const config = await readConfig();\n const existing = config.agents[key] ?? { url: agent.agent_uri };\n config.agents[key] = {\n ...existing,\n url: agent.agent_uri,\n protocol: agent.protocol,\n ...(agent.auth_token !== undefined ? { auth_token: agent.auth_token } : {}),\n ...(agent.oauth_tokens !== undefined ? { oauth_tokens: agent.oauth_tokens } : {}),\n ...(agent.oauth_client !== undefined ? { oauth_client: agent.oauth_client } : {}),\n ...(agent.oauth_client_credentials !== undefined\n ? { oauth_client_credentials: agent.oauth_client_credentials }\n : {}),\n ...(agent.oauth_code_verifier !== undefined ? { oauth_code_verifier: agent.oauth_code_verifier } : {}),\n };\n await writeConfig(config);\n },\n };\n}\n\nfunction isNotFound(err: unknown): boolean {\n return Boolean(err && typeof err === 'object' && (err as { code?: string }).code === 'ENOENT');\n}\n"],"mappings":"AAYA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AACjB,SAAS,mBAAmB;AA+DrB,SAAS,uBAAuB,SAAsD;AAC3F,QAAM,aAAa,QAAQ;AAC3B,QAAM,mBAAmB,QAAQ;AACjC,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,WAAW,QAAQ,YAAY;AAErC,iBAAe,aAAoC;AACjD,QAAI;AACF,YAAM,MAAM,MAAM,GAAG,SAAS,YAAY,OAAO;AACjD,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,SAAU,QAAO,SAAS,CAAC;AAC1E,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,WAAW,GAAG,EAAG,QAAO,EAAE,QAAQ,CAAC,EAAE;AACzC,YAAM;AAAA,IACR;AAAA,EACF;AAEA,iBAAe,YAAY,QAAqC;AAC9D,QAAI,eAAe;AACjB,YAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,IAC3E;AAKA,UAAM,WAAW,GAAG,UAAU,IAAI,QAAQ,GAAG,IAAI,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AAC/E,UAAM,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC;AACjD,UAAM,GAAG,UAAU,UAAU,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3D,UAAM,GAAG,OAAO,UAAU,UAAU;AAAA,EACtC;AAEA,SAAO;AAAA,IACL,MAAM,UAAU,SAAmD;AACjE,YAAM,MAAM,oBAAoB;AAChC,YAAM,SAAS,MAAM,WAAW;AAChC,YAAM,SAAS,OAAO,OAAO,GAAG;AAChC,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAW,OAAO;AAAA,QAClB,UAAU,OAAO,YAAY;AAAA,QAC7B,YAAY,OAAO;AAAA,QACnB,cAAc,OAAO;AAAA,QACrB,cAAc,OAAO;AAAA,QACrB,0BAA0B,OAAO;AAAA,QACjC,qBAAqB,OAAO;AAAA,MAC9B;AAAA,IACF;AAAA,IAEA,MAAM,UAAU,OAAmC;AACjD,YAAM,MAAM,oBAAoB,MAAM;AACtC,YAAM,SAAS,MAAM,WAAW;AAChC,YAAM,WAAW,OAAO,OAAO,GAAG,KAAK,EAAE,KAAK,MAAM,UAAU;AAC9D,aAAO,OAAO,GAAG,IAAI;AAAA,QACnB,GAAG;AAAA,QACH,KAAK,MAAM;AAAA,QACX,UAAU,MAAM;AAAA,QAChB,GAAI,MAAM,eAAe,SAAY,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;AAAA,QACzE,GAAI,MAAM,iBAAiB,SAAY,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;AAAA,QAC/E,GAAI,MAAM,iBAAiB,SAAY,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;AAAA,QAC/E,GAAI,MAAM,6BAA6B,SACnC,EAAE,0BAA0B,MAAM,yBAAyB,IAC3D,CAAC;AAAA,QACL,GAAI,MAAM,wBAAwB,SAAY,EAAE,qBAAqB,MAAM,oBAAoB,IAAI,CAAC;AAAA,MACtG;AACA,YAAM,YAAY,MAAM;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,WAAW,KAAuB;AACzC,SAAO,QAAQ,OAAO,OAAO,QAAQ,YAAa,IAA0B,SAAS,QAAQ;AAC/F;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import {
|
|
8
2
|
DEFAULT_CLIENT_METADATA,
|
|
9
3
|
OAuthError,
|