@frontmcp/sdk 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -18
- package/package.json +20 -5
- package/src/app/app.registry.d.ts +3 -2
- package/src/app/app.registry.js +3 -1
- package/src/app/app.registry.js.map +1 -1
- package/src/app/instances/app.local.instance.js +2 -2
- package/src/app/instances/app.local.instance.js.map +1 -1
- package/src/auth/auth.registry.d.ts +34 -2
- package/src/auth/auth.registry.js +162 -24
- package/src/auth/auth.registry.js.map +1 -1
- package/src/auth/auth.utils.js +8 -9
- package/src/auth/auth.utils.js.map +1 -1
- package/src/auth/authorization/authorization.class.d.ts +125 -0
- package/src/auth/authorization/authorization.class.js +224 -0
- package/src/auth/authorization/authorization.class.js.map +1 -0
- package/src/auth/authorization/authorization.types.d.ts +300 -0
- package/src/auth/authorization/authorization.types.js +79 -0
- package/src/auth/authorization/authorization.types.js.map +1 -0
- package/src/auth/authorization/index.d.ts +5 -0
- package/src/auth/authorization/index.js +19 -0
- package/src/auth/authorization/index.js.map +1 -0
- package/src/auth/authorization/orchestrated.authorization.d.ts +242 -0
- package/src/auth/authorization/orchestrated.authorization.js +306 -0
- package/src/auth/authorization/orchestrated.authorization.js.map +1 -0
- package/src/auth/authorization/public.authorization.d.ts +91 -0
- package/src/auth/authorization/public.authorization.js +132 -0
- package/src/auth/authorization/public.authorization.js.map +1 -0
- package/src/auth/authorization/transparent.authorization.d.ts +130 -0
- package/src/auth/authorization/transparent.authorization.js +147 -0
- package/src/auth/authorization/transparent.authorization.js.map +1 -0
- package/src/auth/consent/consent.types.d.ts +111 -0
- package/src/auth/consent/consent.types.js +119 -0
- package/src/auth/consent/consent.types.js.map +1 -0
- package/src/auth/consent/index.d.ts +1 -0
- package/src/auth/consent/index.js +13 -0
- package/src/auth/consent/index.js.map +1 -0
- package/src/auth/detection/auth-provider-detection.d.ts +84 -0
- package/src/auth/detection/auth-provider-detection.js +230 -0
- package/src/auth/detection/auth-provider-detection.js.map +1 -0
- package/src/auth/detection/index.d.ts +1 -0
- package/src/auth/detection/index.js +15 -0
- package/src/auth/detection/index.js.map +1 -0
- package/src/auth/flows/auth.verify.flow.d.ts +110 -0
- package/src/auth/flows/auth.verify.flow.js +379 -0
- package/src/auth/flows/auth.verify.flow.js.map +1 -0
- package/src/auth/flows/oauth.authorize.flow.d.ts +118 -164
- package/src/auth/flows/oauth.authorize.flow.js +701 -33
- package/src/auth/flows/oauth.authorize.flow.js.map +1 -1
- package/src/auth/flows/oauth.callback.flow.d.ts +117 -0
- package/src/auth/flows/oauth.callback.flow.js +357 -0
- package/src/auth/flows/oauth.callback.flow.js.map +1 -0
- package/src/auth/flows/oauth.register.flow.d.ts +32 -125
- package/src/auth/flows/oauth.token.flow.d.ts +52 -154
- package/src/auth/flows/oauth.token.flow.js +193 -55
- package/src/auth/flows/oauth.token.flow.js.map +1 -1
- package/src/auth/flows/session.verify.flow.d.ts +66 -321
- package/src/auth/flows/session.verify.flow.js +107 -18
- package/src/auth/flows/session.verify.flow.js.map +1 -1
- package/src/auth/flows/well-known.jwks.flow.d.ts +34 -205
- package/src/auth/flows/well-known.jwks.flow.js +15 -8
- package/src/auth/flows/well-known.jwks.flow.js.map +1 -1
- package/src/auth/flows/well-known.oauth-authorization-server.flow.d.ts +48 -223
- package/src/auth/flows/well-known.oauth-authorization-server.flow.js +2 -3
- package/src/auth/flows/well-known.oauth-authorization-server.flow.js.map +1 -1
- package/src/auth/flows/well-known.prm.flow.d.ts +19 -120
- package/src/auth/flows/well-known.prm.flow.js +3 -4
- package/src/auth/flows/well-known.prm.flow.js.map +1 -1
- package/src/auth/instances/instance.local-primary-auth.d.ts +91 -4
- package/src/auth/instances/instance.local-primary-auth.js +236 -6
- package/src/auth/instances/instance.local-primary-auth.js.map +1 -1
- package/src/auth/instances/instance.remote-primary-auth.d.ts +4 -3
- package/src/auth/instances/instance.remote-primary-auth.js +2 -2
- package/src/auth/instances/instance.remote-primary-auth.js.map +1 -1
- package/src/auth/session/authorization-vault.d.ts +611 -0
- package/src/auth/session/authorization-vault.js +817 -0
- package/src/auth/session/authorization-vault.js.map +1 -0
- package/src/auth/session/authorization.store.d.ts +301 -0
- package/src/auth/session/authorization.store.js +323 -0
- package/src/auth/session/authorization.store.js.map +1 -0
- package/src/auth/session/encrypted-authorization-vault.d.ts +181 -0
- package/src/auth/session/encrypted-authorization-vault.js +493 -0
- package/src/auth/session/encrypted-authorization-vault.js.map +1 -0
- package/src/auth/session/index.d.ts +4 -4
- package/src/auth/session/index.js +11 -7
- package/src/auth/session/index.js.map +1 -1
- package/src/auth/session/session.schema.d.ts +1 -1
- package/src/auth/session/session.service.d.ts +1 -1
- package/src/auth/session/transport-session.manager.d.ts +101 -0
- package/src/auth/session/transport-session.manager.js +300 -0
- package/src/auth/session/transport-session.manager.js.map +1 -0
- package/src/auth/session/transport-session.types.d.ts +457 -0
- package/src/auth/session/transport-session.types.js +110 -0
- package/src/auth/session/transport-session.types.js.map +1 -0
- package/src/auth/session/utils/session-id.utils.d.ts +14 -2
- package/src/auth/session/utils/session-id.utils.js +68 -19
- package/src/auth/session/utils/session-id.utils.js.map +1 -1
- package/src/auth/session/vault-encryption.d.ts +189 -0
- package/src/auth/session/vault-encryption.js +263 -0
- package/src/auth/session/vault-encryption.js.map +1 -0
- package/src/auth/ui/base-layout.d.ts +188 -0
- package/src/auth/ui/base-layout.js +292 -0
- package/src/auth/ui/base-layout.js.map +1 -0
- package/src/auth/ui/htmx-templates.d.ts +135 -0
- package/src/auth/ui/htmx-templates.js +433 -0
- package/src/auth/ui/htmx-templates.js.map +1 -0
- package/src/auth/ui/index.d.ts +11 -0
- package/src/auth/ui/index.js +35 -0
- package/src/auth/ui/index.js.map +1 -0
- package/src/auth/utils/audience.validator.d.ts +129 -0
- package/src/auth/utils/audience.validator.js +196 -0
- package/src/auth/utils/audience.validator.js.map +1 -0
- package/src/auth/utils/index.d.ts +2 -0
- package/src/auth/utils/index.js +7 -0
- package/src/auth/utils/index.js.map +1 -0
- package/src/auth/utils/www-authenticate.utils.d.ts +97 -0
- package/src/auth/utils/www-authenticate.utils.js +183 -0
- package/src/auth/utils/www-authenticate.utils.js.map +1 -0
- package/src/common/common.schema.d.ts +2 -16
- package/src/common/constants.d.ts +3 -0
- package/src/common/constants.js +6 -1
- package/src/common/constants.js.map +1 -1
- package/src/common/decorators/decorator-utils.d.ts +131 -0
- package/src/common/decorators/decorator-utils.js +195 -0
- package/src/common/decorators/decorator-utils.js.map +1 -0
- package/src/common/decorators/front-mcp.decorator.js +3 -2
- package/src/common/decorators/front-mcp.decorator.js.map +1 -1
- package/src/common/decorators/hook.decorator.d.ts +58 -2
- package/src/common/decorators/hook.decorator.js +127 -17
- package/src/common/decorators/hook.decorator.js.map +1 -1
- package/src/common/decorators/plugin.decorator.d.ts +1 -1
- package/src/common/decorators/plugin.decorator.js +11 -10
- package/src/common/decorators/plugin.decorator.js.map +1 -1
- package/src/common/decorators/resource.decorator.d.ts +32 -3
- package/src/common/decorators/resource.decorator.js +46 -4
- package/src/common/decorators/resource.decorator.js.map +1 -1
- package/src/common/decorators/tool.decorator.d.ts +54 -5
- package/src/common/decorators/tool.decorator.js.map +1 -1
- package/src/common/dynamic/dynamic.plugin.d.ts +22 -11
- package/src/common/dynamic/dynamic.plugin.js +7 -1
- package/src/common/dynamic/dynamic.plugin.js.map +1 -1
- package/src/common/entries/prompt.entry.d.ts +46 -2
- package/src/common/entries/prompt.entry.js +10 -0
- package/src/common/entries/prompt.entry.js.map +1 -1
- package/src/common/entries/resource.entry.d.ts +69 -6
- package/src/common/entries/resource.entry.js +27 -3
- package/src/common/entries/resource.entry.js.map +1 -1
- package/src/common/entries/scope.entry.d.ts +5 -1
- package/src/common/entries/scope.entry.js +3 -3
- package/src/common/entries/scope.entry.js.map +1 -1
- package/src/common/flow/flow.utils.d.ts +56 -0
- package/src/common/flow/flow.utils.js +96 -0
- package/src/common/flow/flow.utils.js.map +1 -0
- package/src/common/index.d.ts +2 -2
- package/src/common/index.js +2 -2
- package/src/common/index.js.map +1 -1
- package/src/common/interfaces/execution-context.interface.d.ts +59 -0
- package/src/common/interfaces/execution-context.interface.js +81 -0
- package/src/common/interfaces/execution-context.interface.js.map +1 -0
- package/src/common/interfaces/flow.interface.d.ts +1 -1
- package/src/common/interfaces/flow.interface.js.map +1 -1
- package/src/common/interfaces/index.d.ts +1 -0
- package/src/common/interfaces/index.js +1 -0
- package/src/common/interfaces/index.js.map +1 -1
- package/src/common/interfaces/internal/primary-auth-provider.interface.d.ts +17 -2
- package/src/common/interfaces/internal/primary-auth-provider.interface.js +52 -4
- package/src/common/interfaces/internal/primary-auth-provider.interface.js.map +1 -1
- package/src/common/interfaces/internal/registry.interface.d.ts +16 -2
- package/src/common/interfaces/internal/registry.interface.js.map +1 -1
- package/src/common/interfaces/plugin.interface.js.map +1 -1
- package/src/common/interfaces/prompt.interface.d.ts +53 -4
- package/src/common/interfaces/prompt.interface.js +78 -0
- package/src/common/interfaces/prompt.interface.js.map +1 -1
- package/src/common/interfaces/resource.interface.d.ts +47 -17
- package/src/common/interfaces/resource.interface.js +53 -0
- package/src/common/interfaces/resource.interface.js.map +1 -1
- package/src/common/interfaces/tool.interface.d.ts +39 -22
- package/src/common/interfaces/tool.interface.js +61 -34
- package/src/common/interfaces/tool.interface.js.map +1 -1
- package/src/common/metadata/adapter.metadata.d.ts +1 -9
- package/src/common/metadata/app.metadata.d.ts +425 -730
- package/src/common/metadata/auth-provider.metadata.d.ts +2 -12
- package/src/common/metadata/flow.metadata.d.ts +10 -25
- package/src/common/metadata/front-mcp.metadata.d.ts +602 -1023
- package/src/common/metadata/front-mcp.metadata.js +6 -4
- package/src/common/metadata/front-mcp.metadata.js.map +1 -1
- package/src/common/metadata/hook.metadata.d.ts +1 -1
- package/src/common/metadata/hook.metadata.js.map +1 -1
- package/src/common/metadata/index.d.ts +1 -0
- package/src/common/metadata/index.js +1 -0
- package/src/common/metadata/index.js.map +1 -1
- package/src/common/metadata/logger.metadata.d.ts +1 -9
- package/src/common/metadata/plugin.metadata.d.ts +8 -30
- package/src/common/metadata/prompt.metadata.d.ts +4 -161
- package/src/common/metadata/provider.metadata.d.ts +2 -12
- package/src/common/metadata/resource.metadata.d.ts +6 -98
- package/src/common/metadata/resource.metadata.js +15 -6
- package/src/common/metadata/resource.metadata.js.map +1 -1
- package/src/common/metadata/tool-ui.metadata.d.ts +10 -0
- package/src/common/metadata/tool-ui.metadata.js +12 -0
- package/src/common/metadata/tool-ui.metadata.js.map +1 -0
- package/src/common/metadata/tool.metadata.d.ts +78 -199
- package/src/common/metadata/tool.metadata.js +11 -14
- package/src/common/metadata/tool.metadata.js.map +1 -1
- package/src/common/providers/base-config.provider.d.ts +84 -0
- package/src/common/providers/base-config.provider.js +128 -0
- package/src/common/providers/base-config.provider.js.map +1 -0
- package/src/common/records/plugin.record.d.ts +5 -6
- package/src/common/records/plugin.record.js.map +1 -1
- package/src/common/records/prompt.record.js.map +1 -1
- package/src/common/records/resource.record.d.ts +17 -1
- package/src/common/records/resource.record.js +12 -6
- package/src/common/records/resource.record.js.map +1 -1
- package/src/common/records/tool.record.js.map +1 -1
- package/src/common/schemas/annotated-class.schema.d.ts +9 -9
- package/src/common/schemas/annotated-class.schema.js +92 -27
- package/src/common/schemas/annotated-class.schema.js.map +1 -1
- package/src/common/schemas/http-input.schema.d.ts +6 -30
- package/src/common/schemas/http-output.schema.d.ts +326 -1630
- package/src/common/schemas/http-output.schema.js +39 -1
- package/src/common/schemas/http-output.schema.js.map +1 -1
- package/src/common/tokens/front-mcp.tokens.js +4 -1
- package/src/common/tokens/front-mcp.tokens.js.map +1 -1
- package/src/common/tokens/resource.tokens.d.ts +2 -0
- package/src/common/tokens/resource.tokens.js +4 -1
- package/src/common/tokens/resource.tokens.js.map +1 -1
- package/src/common/tokens/tool.tokens.d.ts +2 -0
- package/src/common/tokens/tool.tokens.js +2 -0
- package/src/common/tokens/tool.tokens.js.map +1 -1
- package/src/common/types/auth/jwt.types.d.ts +5 -31
- package/src/common/types/auth/session.types.d.ts +97 -192
- package/src/common/types/auth/session.types.js +24 -11
- package/src/common/types/auth/session.types.js.map +1 -1
- package/src/common/types/options/auth.options.d.ts +1013 -490
- package/src/common/types/options/auth.options.js +554 -36
- package/src/common/types/options/auth.options.js.map +1 -1
- package/src/common/types/options/http.options.d.ts +1 -9
- package/src/common/types/options/logging.options.d.ts +7 -13
- package/src/common/types/options/logging.options.js +4 -0
- package/src/common/types/options/logging.options.js.map +1 -1
- package/src/common/types/options/server-info.options.d.ts +3 -31
- package/src/common/types/options/session.options.d.ts +90 -10
- package/src/common/types/options/session.options.js +26 -3
- package/src/common/types/options/session.options.js.map +1 -1
- package/src/common/utils/decide-request-intent.utils.d.ts +8 -46
- package/src/common/utils/decide-request-intent.utils.js +88 -23
- package/src/common/utils/decide-request-intent.utils.js.map +1 -1
- package/src/completion/flows/complete.flow.d.ts +74 -0
- package/src/completion/flows/complete.flow.js +199 -0
- package/src/completion/flows/complete.flow.js.map +1 -0
- package/src/errors/authorization-required.error.d.ts +189 -0
- package/src/errors/authorization-required.error.js +274 -0
- package/src/errors/authorization-required.error.js.map +1 -0
- package/src/errors/index.d.ts +2 -1
- package/src/errors/index.js +17 -1
- package/src/errors/index.js.map +1 -1
- package/src/errors/mcp.error.d.ts +101 -1
- package/src/errors/mcp.error.js +147 -2
- package/src/errors/mcp.error.js.map +1 -1
- package/src/flows/flow.instance.js +4 -3
- package/src/flows/flow.instance.js.map +1 -1
- package/src/flows/flow.registry.js.map +1 -1
- package/src/flows/flow.stages.js +14 -11
- package/src/flows/flow.stages.js.map +1 -1
- package/src/front-mcp/front-mcp.providers.d.ts +464 -102
- package/src/front-mcp/front-mcp.providers.js +3 -5
- package/src/front-mcp/front-mcp.providers.js.map +1 -1
- package/src/hooks/hook.instance.d.ts +1 -1
- package/src/hooks/hook.instance.js +5 -2
- package/src/hooks/hook.instance.js.map +1 -1
- package/src/hooks/hook.registry.js +7 -5
- package/src/hooks/hook.registry.js.map +1 -1
- package/src/index.d.ts +28 -9
- package/src/index.js +5 -1
- package/src/index.js.map +1 -1
- package/src/logger/instances/instance.logger.js +3 -2
- package/src/logger/instances/instance.logger.js.map +1 -1
- package/src/logger/logger.registry.js +7 -2
- package/src/logger/logger.registry.js.map +1 -1
- package/src/logging/flows/set-level.flow.d.ts +62 -0
- package/src/logging/flows/set-level.flow.js +108 -0
- package/src/logging/flows/set-level.flow.js.map +1 -0
- package/src/mcp-apps/csp.d.ts +111 -0
- package/src/mcp-apps/csp.js +267 -0
- package/src/mcp-apps/csp.js.map +1 -0
- package/src/mcp-apps/index.d.ts +23 -0
- package/src/mcp-apps/index.js +91 -0
- package/src/mcp-apps/index.js.map +1 -0
- package/src/mcp-apps/schemas.d.ts +403 -0
- package/src/mcp-apps/schemas.js +345 -0
- package/src/mcp-apps/schemas.js.map +1 -0
- package/src/mcp-apps/template.d.ts +94 -0
- package/src/mcp-apps/template.js +419 -0
- package/src/mcp-apps/template.js.map +1 -0
- package/src/mcp-apps/types.d.ts +323 -0
- package/src/mcp-apps/types.js +59 -0
- package/src/mcp-apps/types.js.map +1 -0
- package/src/notification/index.d.ts +1 -0
- package/src/notification/index.js +13 -0
- package/src/notification/index.js.map +1 -0
- package/src/notification/notification.service.d.ts +378 -0
- package/src/notification/notification.service.js +727 -0
- package/src/notification/notification.service.js.map +1 -0
- package/src/plugin/plugin.registry.js +12 -9
- package/src/plugin/plugin.registry.js.map +1 -1
- package/src/prompt/flows/get-prompt.flow.d.ts +153 -0
- package/src/prompt/flows/get-prompt.flow.js +214 -0
- package/src/prompt/flows/get-prompt.flow.js.map +1 -0
- package/src/prompt/flows/prompts-list.flow.d.ts +67 -0
- package/src/prompt/flows/prompts-list.flow.js +176 -0
- package/src/prompt/flows/prompts-list.flow.js.map +1 -0
- package/src/prompt/index.d.ts +7 -0
- package/src/prompt/index.js +17 -0
- package/src/prompt/index.js.map +1 -0
- package/src/prompt/prompt.events.d.ts +17 -0
- package/src/prompt/prompt.events.js +25 -0
- package/src/prompt/prompt.events.js.map +1 -0
- package/src/prompt/prompt.instance.d.ts +30 -0
- package/src/prompt/prompt.instance.js +120 -0
- package/src/prompt/prompt.instance.js.map +1 -0
- package/src/prompt/prompt.registry.d.ts +79 -12
- package/src/prompt/prompt.registry.js +360 -15
- package/src/prompt/prompt.registry.js.map +1 -1
- package/src/prompt/prompt.types.d.ts +26 -0
- package/src/prompt/prompt.types.js +11 -0
- package/src/prompt/prompt.types.js.map +1 -0
- package/src/prompt/prompt.utils.d.ts +26 -0
- package/src/prompt/prompt.utils.js +136 -0
- package/src/prompt/prompt.utils.js.map +1 -0
- package/src/provider/provider.registry.d.ts +12 -5
- package/src/provider/provider.registry.js +30 -138
- package/src/provider/provider.registry.js.map +1 -1
- package/src/regsitry/registry.base.d.ts +1 -1
- package/src/regsitry/registry.base.js.map +1 -1
- package/src/resource/flows/read-resource.flow.d.ts +91 -0
- package/src/resource/flows/read-resource.flow.js +270 -0
- package/src/resource/flows/read-resource.flow.js.map +1 -0
- package/src/resource/flows/resource-templates-list.flow.d.ts +64 -0
- package/src/resource/flows/resource-templates-list.flow.js +191 -0
- package/src/resource/flows/resource-templates-list.flow.js.map +1 -0
- package/src/resource/flows/resources-list.flow.d.ts +64 -0
- package/src/resource/flows/resources-list.flow.js +196 -0
- package/src/resource/flows/resources-list.flow.js.map +1 -0
- package/src/resource/flows/subscribe-resource.flow.d.ts +45 -0
- package/src/resource/flows/subscribe-resource.flow.js +123 -0
- package/src/resource/flows/subscribe-resource.flow.js.map +1 -0
- package/src/resource/flows/unsubscribe-resource.flow.d.ts +44 -0
- package/src/resource/flows/unsubscribe-resource.flow.js +107 -0
- package/src/resource/flows/unsubscribe-resource.flow.js.map +1 -0
- package/src/resource/index.d.ts +8 -0
- package/src/resource/index.js +20 -0
- package/src/resource/index.js.map +1 -0
- package/src/resource/resource.events.d.ts +24 -0
- package/src/resource/resource.events.js +17 -0
- package/src/resource/resource.events.js.map +1 -0
- package/src/resource/resource.instance.d.ts +35 -0
- package/src/resource/resource.instance.js +163 -0
- package/src/resource/resource.instance.js.map +1 -0
- package/src/resource/resource.registry.d.ts +106 -12
- package/src/resource/resource.registry.js +449 -13
- package/src/resource/resource.registry.js.map +1 -1
- package/src/resource/resource.types.d.ts +35 -0
- package/src/resource/resource.types.js +11 -0
- package/src/resource/resource.types.js.map +1 -0
- package/src/resource/resource.utils.d.ts +30 -0
- package/src/resource/resource.utils.js +151 -0
- package/src/resource/resource.utils.js.map +1 -0
- package/src/scope/flows/http.request.flow.d.ts +48 -330
- package/src/scope/flows/http.request.flow.js +306 -78
- package/src/scope/flows/http.request.flow.js.map +1 -1
- package/src/scope/scope.instance.d.ts +12 -0
- package/src/scope/scope.instance.js +145 -15
- package/src/scope/scope.instance.js.map +1 -1
- package/src/tool/flows/call-tool.flow.d.ts +64 -1110
- package/src/tool/flows/call-tool.flow.js +303 -15
- package/src/tool/flows/call-tool.flow.js.map +1 -1
- package/src/tool/flows/tools-list.flow.d.ts +32 -473
- package/src/tool/flows/tools-list.flow.js +121 -40
- package/src/tool/flows/tools-list.flow.js.map +1 -1
- package/src/tool/tool.events.d.ts +8 -1
- package/src/tool/tool.events.js.map +1 -1
- package/src/tool/tool.instance.d.ts +3 -1
- package/src/tool/tool.instance.js +17 -3
- package/src/tool/tool.instance.js.map +1 -1
- package/src/tool/tool.registry.d.ts +7 -1
- package/src/tool/tool.registry.js +26 -10
- package/src/tool/tool.registry.js.map +1 -1
- package/src/tool/tool.types.d.ts +4 -4
- package/src/tool/tool.types.js.map +1 -1
- package/src/tool/tool.utils.d.ts +3 -12
- package/src/tool/tool.utils.js +39 -193
- package/src/tool/tool.utils.js.map +1 -1
- package/src/tool/ui/index.d.ts +22 -0
- package/src/tool/ui/index.js +63 -0
- package/src/tool/ui/index.js.map +1 -0
- package/src/tool/ui/platform-adapters.d.ts +10 -0
- package/src/tool/ui/platform-adapters.js +18 -0
- package/src/tool/ui/platform-adapters.js.map +1 -0
- package/src/tool/ui/template-helpers.d.ts +46 -0
- package/src/tool/ui/template-helpers.js +112 -0
- package/src/tool/ui/template-helpers.js.map +1 -0
- package/src/tool/ui/ui-resource-template.d.ts +34 -0
- package/src/tool/ui/ui-resource-template.js +64 -0
- package/src/tool/ui/ui-resource-template.js.map +1 -0
- package/src/tool/ui/ui-resource.handler.d.ts +74 -0
- package/src/tool/ui/ui-resource.handler.js +129 -0
- package/src/tool/ui/ui-resource.handler.js.map +1 -0
- package/src/transport/adapters/transport.local.adapter.d.ts +2 -2
- package/src/transport/adapters/transport.local.adapter.js +28 -7
- package/src/transport/adapters/transport.local.adapter.js.map +1 -1
- package/src/transport/adapters/transport.sse.adapter.d.ts +2 -2
- package/src/transport/adapters/transport.sse.adapter.js +4 -3
- package/src/transport/adapters/transport.sse.adapter.js.map +1 -1
- package/src/transport/adapters/transport.streamable-http.adapter.d.ts +10 -3
- package/src/transport/adapters/transport.streamable-http.adapter.js +54 -8
- package/src/transport/adapters/transport.streamable-http.adapter.js.map +1 -1
- package/src/transport/flows/handle.sse.flow.d.ts +29 -63
- package/src/transport/flows/handle.sse.flow.js +78 -10
- package/src/transport/flows/handle.sse.flow.js.map +1 -1
- package/src/transport/flows/handle.stateless-http.flow.d.ts +29 -0
- package/src/transport/flows/handle.stateless-http.flow.js +102 -0
- package/src/transport/flows/handle.stateless-http.flow.js.map +1 -0
- package/src/transport/flows/handle.streamable-http.flow.d.ts +32 -64
- package/src/transport/flows/handle.streamable-http.flow.js +158 -26
- package/src/transport/flows/handle.streamable-http.flow.js.map +1 -1
- package/src/transport/legacy/legacy.sse.tranporter.d.ts +9 -0
- package/src/transport/legacy/legacy.sse.tranporter.js +17 -2
- package/src/transport/legacy/legacy.sse.tranporter.js.map +1 -1
- package/src/transport/mcp-handlers/call-tool-request.handler.js +27 -1
- package/src/transport/mcp-handlers/call-tool-request.handler.js.map +1 -1
- package/src/transport/mcp-handlers/complete-request.handler.d.ts +69 -0
- package/src/transport/mcp-handlers/complete-request.handler.js +11 -0
- package/src/transport/mcp-handlers/complete-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/get-prompt-request.handler.d.ts +87 -0
- package/src/transport/mcp-handlers/get-prompt-request.handler.js +11 -0
- package/src/transport/mcp-handlers/get-prompt-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/index.d.ts +517 -208
- package/src/transport/mcp-handlers/index.js +39 -2
- package/src/transport/mcp-handlers/index.js.map +1 -1
- package/src/transport/mcp-handlers/initialize-request.handler.d.ts +1 -1
- package/src/transport/mcp-handlers/initialize-request.handler.js +73 -7
- package/src/transport/mcp-handlers/initialize-request.handler.js.map +1 -1
- package/src/transport/mcp-handlers/list-prompts-request.handler.d.ts +54 -0
- package/src/transport/mcp-handlers/list-prompts-request.handler.js +11 -0
- package/src/transport/mcp-handlers/list-prompts-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/list-resource-templates-request.handler.d.ts +51 -0
- package/src/transport/mcp-handlers/list-resource-templates-request.handler.js +12 -0
- package/src/transport/mcp-handlers/list-resource-templates-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/list-resources-request.handler.d.ts +51 -0
- package/src/transport/mcp-handlers/list-resources-request.handler.js +12 -0
- package/src/transport/mcp-handlers/list-resources-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/list-tools-request.handler.d.ts +19 -146
- package/src/transport/mcp-handlers/logging-set-level-request.handler.d.ts +46 -0
- package/src/transport/mcp-handlers/logging-set-level-request.handler.js +34 -0
- package/src/transport/mcp-handlers/logging-set-level-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/mcp-handlers.types.d.ts +3 -7
- package/src/transport/mcp-handlers/mcp-handlers.types.js.map +1 -1
- package/src/transport/mcp-handlers/read-resource-request.handler.d.ts +46 -0
- package/src/transport/mcp-handlers/read-resource-request.handler.js +12 -0
- package/src/transport/mcp-handlers/read-resource-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/roots-list-changed-notification.handler.d.ts +11 -0
- package/src/transport/mcp-handlers/roots-list-changed-notification.handler.js +26 -0
- package/src/transport/mcp-handlers/roots-list-changed-notification.handler.js.map +1 -0
- package/src/transport/mcp-handlers/subscribe-request.handler.d.ts +37 -0
- package/src/transport/mcp-handlers/subscribe-request.handler.js +34 -0
- package/src/transport/mcp-handlers/subscribe-request.handler.js.map +1 -0
- package/src/transport/mcp-handlers/unsubscribe-request.handler.d.ts +37 -0
- package/src/transport/mcp-handlers/unsubscribe-request.handler.js +34 -0
- package/src/transport/mcp-handlers/unsubscribe-request.handler.js.map +1 -0
- package/src/transport/transport.local.js +7 -2
- package/src/transport/transport.local.js.map +1 -1
- package/src/transport/transport.registry.d.ts +30 -0
- package/src/transport/transport.registry.js +84 -1
- package/src/transport/transport.registry.js.map +1 -1
- package/src/transport/transport.types.d.ts +3 -3
- package/src/transport/transport.types.js.map +1 -1
- package/src/utils/content.utils.d.ts +48 -0
- package/src/utils/content.utils.js +194 -0
- package/src/utils/content.utils.js.map +1 -0
- package/src/utils/index.d.ts +8 -0
- package/src/utils/index.js +55 -0
- package/src/utils/index.js.map +1 -0
- package/src/utils/lineage.utils.d.ts +40 -0
- package/src/utils/lineage.utils.js +82 -0
- package/src/utils/lineage.utils.js.map +1 -0
- package/src/utils/naming.utils.d.ts +46 -0
- package/src/utils/naming.utils.js +136 -0
- package/src/utils/naming.utils.js.map +1 -0
- package/src/utils/types.utils.d.ts +2 -2
- package/src/utils/types.utils.js.map +1 -1
- package/src/utils/uri-template.utils.d.ts +57 -0
- package/src/utils/uri-template.utils.js +113 -0
- package/src/utils/uri-template.utils.js.map +1 -0
- package/src/utils/uri-validation.utils.d.ts +40 -0
- package/src/utils/uri-validation.utils.js +76 -0
- package/src/utils/uri-validation.utils.js.map +1 -0
- package/src/__test-utils__/fixtures/hook.fixtures.d.ts +0 -46
- package/src/__test-utils__/fixtures/hook.fixtures.js +0 -114
- package/src/__test-utils__/fixtures/hook.fixtures.js.map +0 -1
- package/src/__test-utils__/fixtures/index.d.ts +0 -7
- package/src/__test-utils__/fixtures/index.js +0 -11
- package/src/__test-utils__/fixtures/index.js.map +0 -1
- package/src/__test-utils__/fixtures/plugin.fixtures.d.ts +0 -46
- package/src/__test-utils__/fixtures/plugin.fixtures.js +0 -127
- package/src/__test-utils__/fixtures/plugin.fixtures.js.map +0 -1
- package/src/__test-utils__/fixtures/provider.fixtures.d.ts +0 -69
- package/src/__test-utils__/fixtures/provider.fixtures.js +0 -131
- package/src/__test-utils__/fixtures/provider.fixtures.js.map +0 -1
- package/src/__test-utils__/fixtures/scope.fixtures.d.ts +0 -14
- package/src/__test-utils__/fixtures/scope.fixtures.js +0 -59
- package/src/__test-utils__/fixtures/scope.fixtures.js.map +0 -1
- package/src/__test-utils__/fixtures/tool.fixtures.d.ts +0 -36
- package/src/__test-utils__/fixtures/tool.fixtures.js +0 -91
- package/src/__test-utils__/fixtures/tool.fixtures.js.map +0 -1
- package/src/__test-utils__/helpers/assertion.helpers.d.ts +0 -45
- package/src/__test-utils__/helpers/assertion.helpers.js +0 -153
- package/src/__test-utils__/helpers/assertion.helpers.js.map +0 -1
- package/src/__test-utils__/helpers/async.helpers.d.ts +0 -48
- package/src/__test-utils__/helpers/async.helpers.js +0 -112
- package/src/__test-utils__/helpers/async.helpers.js.map +0 -1
- package/src/__test-utils__/helpers/index.d.ts +0 -6
- package/src/__test-utils__/helpers/index.js +0 -10
- package/src/__test-utils__/helpers/index.js.map +0 -1
- package/src/__test-utils__/helpers/setup.helpers.d.ts +0 -54
- package/src/__test-utils__/helpers/setup.helpers.js +0 -106
- package/src/__test-utils__/helpers/setup.helpers.js.map +0 -1
- package/src/__test-utils__/index.d.ts +0 -9
- package/src/__test-utils__/index.js +0 -14
- package/src/__test-utils__/index.js.map +0 -1
- package/src/__test-utils__/mocks/flow-instance.mock.d.ts +0 -50
- package/src/__test-utils__/mocks/flow-instance.mock.js +0 -72
- package/src/__test-utils__/mocks/flow-instance.mock.js.map +0 -1
- package/src/__test-utils__/mocks/hook-registry.mock.d.ts +0 -25
- package/src/__test-utils__/mocks/hook-registry.mock.js +0 -65
- package/src/__test-utils__/mocks/hook-registry.mock.js.map +0 -1
- package/src/__test-utils__/mocks/index.d.ts +0 -8
- package/src/__test-utils__/mocks/index.js +0 -12
- package/src/__test-utils__/mocks/index.js.map +0 -1
- package/src/__test-utils__/mocks/plugin-registry.mock.d.ts +0 -43
- package/src/__test-utils__/mocks/plugin-registry.mock.js +0 -70
- package/src/__test-utils__/mocks/plugin-registry.mock.js.map +0 -1
- package/src/__test-utils__/mocks/provider-registry.mock.d.ts +0 -39
- package/src/__test-utils__/mocks/provider-registry.mock.js +0 -72
- package/src/__test-utils__/mocks/provider-registry.mock.js.map +0 -1
- package/src/__test-utils__/mocks/tool-registry.mock.d.ts +0 -43
- package/src/__test-utils__/mocks/tool-registry.mock.js +0 -79
- package/src/__test-utils__/mocks/tool-registry.mock.js.map +0 -1
- package/src/auth/path.utils.d.ts +0 -20
- package/src/auth/path.utils.js +0 -71
- package/src/auth/path.utils.js.map +0 -1
- package/src/common/decorators-old/async-with.decorator.d.ts +0 -10
- package/src/common/decorators-old/async-with.decorator.js +0 -24
- package/src/common/decorators-old/async-with.decorator.js.map +0 -1
- package/src/common/decorators-old/auth-hook.decorator.d.ts +0 -14
- package/src/common/decorators-old/auth-hook.decorator.js +0 -27
- package/src/common/decorators-old/auth-hook.decorator.js.map +0 -1
- package/src/common/decorators-old/session-hook.decorator.d.ts +0 -14
- package/src/common/decorators-old/session-hook.decorator.js +0 -27
- package/src/common/decorators-old/session-hook.decorator.js.map +0 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audience Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates JWT audience claims per RFC 7519 and MCP Authorization spec.
|
|
5
|
+
* The audience (aud) claim identifies the recipients that the JWT is intended for.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validation result
|
|
9
|
+
*/
|
|
10
|
+
export interface AudienceValidationResult {
|
|
11
|
+
/** Whether the audience is valid */
|
|
12
|
+
valid: boolean;
|
|
13
|
+
/** Error message if invalid */
|
|
14
|
+
error?: string;
|
|
15
|
+
/** Matched audience value (if valid) */
|
|
16
|
+
matchedAudience?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Audience validator options
|
|
20
|
+
*/
|
|
21
|
+
export interface AudienceValidatorOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Expected audience values
|
|
24
|
+
* Token must contain at least one of these audiences
|
|
25
|
+
*/
|
|
26
|
+
expectedAudiences: string[];
|
|
27
|
+
/**
|
|
28
|
+
* Whether to allow tokens with no audience claim
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
allowNoAudience?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Case-sensitive comparison
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
caseSensitive?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Allow wildcard matching (e.g., *.example.com)
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
allowWildcards?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate JWT audience claim
|
|
45
|
+
*
|
|
46
|
+
* @param tokenAudience - The audience claim from the JWT (can be string or array)
|
|
47
|
+
* @param options - Validation options
|
|
48
|
+
* @returns Validation result
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Single expected audience
|
|
53
|
+
* validateAudience('https://api.example.com', {
|
|
54
|
+
* expectedAudiences: ['https://api.example.com'],
|
|
55
|
+
* });
|
|
56
|
+
* // => { valid: true, matchedAudience: 'https://api.example.com' }
|
|
57
|
+
*
|
|
58
|
+
* // Multiple audiences in token
|
|
59
|
+
* validateAudience(['aud1', 'aud2', 'aud3'], {
|
|
60
|
+
* expectedAudiences: ['aud2'],
|
|
61
|
+
* });
|
|
62
|
+
* // => { valid: true, matchedAudience: 'aud2' }
|
|
63
|
+
*
|
|
64
|
+
* // No match
|
|
65
|
+
* validateAudience('wrong-aud', {
|
|
66
|
+
* expectedAudiences: ['expected-aud'],
|
|
67
|
+
* });
|
|
68
|
+
* // => { valid: false, error: 'Token audience does not match expected audiences' }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function validateAudience(tokenAudience: string | string[] | undefined, options: AudienceValidatorOptions): AudienceValidationResult;
|
|
72
|
+
/**
|
|
73
|
+
* Create an audience validator function
|
|
74
|
+
*
|
|
75
|
+
* @param options - Validator options
|
|
76
|
+
* @returns A validation function that takes token audience and returns validation result
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const validator = createAudienceValidator({
|
|
81
|
+
* expectedAudiences: ['https://api.example.com', 'https://api.example.org'],
|
|
82
|
+
* });
|
|
83
|
+
*
|
|
84
|
+
* validator('https://api.example.com'); // => { valid: true, ... }
|
|
85
|
+
* validator('wrong-aud'); // => { valid: false, ... }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function createAudienceValidator(options: AudienceValidatorOptions): (audience: string | string[] | undefined) => AudienceValidationResult;
|
|
89
|
+
/**
|
|
90
|
+
* Derive expected audience from the resource URL
|
|
91
|
+
*
|
|
92
|
+
* Per MCP Authorization spec, the audience should typically be the
|
|
93
|
+
* resource server URL (the MCP server URL).
|
|
94
|
+
*
|
|
95
|
+
* @param resourceUrl - The resource server URL
|
|
96
|
+
* @returns Array of expected audiences
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* deriveExpectedAudience('https://api.example.com/v1/mcp');
|
|
101
|
+
* // => ['https://api.example.com/v1/mcp', 'https://api.example.com', 'api.example.com']
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function deriveExpectedAudience(resourceUrl: string): string[];
|
|
105
|
+
/**
|
|
106
|
+
* AudienceValidator class for reusable validation with configuration
|
|
107
|
+
*/
|
|
108
|
+
export declare class AudienceValidator {
|
|
109
|
+
private options;
|
|
110
|
+
constructor(options?: Partial<AudienceValidatorOptions> & {
|
|
111
|
+
expectedAudiences?: string[];
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Validate an audience claim
|
|
115
|
+
*/
|
|
116
|
+
validate(audience: string | string[] | undefined): AudienceValidationResult;
|
|
117
|
+
/**
|
|
118
|
+
* Add expected audiences
|
|
119
|
+
*/
|
|
120
|
+
addAudiences(...audiences: string[]): void;
|
|
121
|
+
/**
|
|
122
|
+
* Set expected audiences (replace existing)
|
|
123
|
+
*/
|
|
124
|
+
setAudiences(audiences: string[]): void;
|
|
125
|
+
/**
|
|
126
|
+
* Create validator from resource URL
|
|
127
|
+
*/
|
|
128
|
+
static fromResourceUrl(resourceUrl: string, options?: Omit<AudienceValidatorOptions, 'expectedAudiences'>): AudienceValidator;
|
|
129
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// auth/utils/audience.validator.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AudienceValidator = void 0;
|
|
5
|
+
exports.validateAudience = validateAudience;
|
|
6
|
+
exports.createAudienceValidator = createAudienceValidator;
|
|
7
|
+
exports.deriveExpectedAudience = deriveExpectedAudience;
|
|
8
|
+
/**
|
|
9
|
+
* Validate JWT audience claim
|
|
10
|
+
*
|
|
11
|
+
* @param tokenAudience - The audience claim from the JWT (can be string or array)
|
|
12
|
+
* @param options - Validation options
|
|
13
|
+
* @returns Validation result
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Single expected audience
|
|
18
|
+
* validateAudience('https://api.example.com', {
|
|
19
|
+
* expectedAudiences: ['https://api.example.com'],
|
|
20
|
+
* });
|
|
21
|
+
* // => { valid: true, matchedAudience: 'https://api.example.com' }
|
|
22
|
+
*
|
|
23
|
+
* // Multiple audiences in token
|
|
24
|
+
* validateAudience(['aud1', 'aud2', 'aud3'], {
|
|
25
|
+
* expectedAudiences: ['aud2'],
|
|
26
|
+
* });
|
|
27
|
+
* // => { valid: true, matchedAudience: 'aud2' }
|
|
28
|
+
*
|
|
29
|
+
* // No match
|
|
30
|
+
* validateAudience('wrong-aud', {
|
|
31
|
+
* expectedAudiences: ['expected-aud'],
|
|
32
|
+
* });
|
|
33
|
+
* // => { valid: false, error: 'Token audience does not match expected audiences' }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function validateAudience(tokenAudience, options) {
|
|
37
|
+
const { expectedAudiences, allowNoAudience = false, caseSensitive = true, allowWildcards = false } = options;
|
|
38
|
+
// Handle missing audience
|
|
39
|
+
if (tokenAudience === undefined || tokenAudience === null) {
|
|
40
|
+
if (allowNoAudience) {
|
|
41
|
+
return { valid: true };
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
valid: false,
|
|
45
|
+
error: 'Token is missing audience claim',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Handle empty expected audiences (accept any)
|
|
49
|
+
if (expectedAudiences.length === 0) {
|
|
50
|
+
const firstAud = Array.isArray(tokenAudience) ? tokenAudience[0] : tokenAudience;
|
|
51
|
+
return {
|
|
52
|
+
valid: false,
|
|
53
|
+
error: 'No expected audiences configured - cannot validate token',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Normalize token audience to array
|
|
57
|
+
const tokenAuds = Array.isArray(tokenAudience) ? tokenAudience : [tokenAudience];
|
|
58
|
+
// Check each token audience against expected audiences
|
|
59
|
+
for (const tokenAud of tokenAuds) {
|
|
60
|
+
for (const expectedAud of expectedAudiences) {
|
|
61
|
+
if (matchesAudience(tokenAud, expectedAud, caseSensitive, allowWildcards)) {
|
|
62
|
+
return { valid: true, matchedAudience: tokenAud };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
valid: false,
|
|
68
|
+
error: `Token audience does not match expected audiences. Got: ${tokenAuds.join(', ')}. Expected one of: ${expectedAudiences.join(', ')}`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a token audience matches an expected audience
|
|
73
|
+
*/
|
|
74
|
+
function matchesAudience(tokenAud, expectedAud, caseSensitive, allowWildcards) {
|
|
75
|
+
// Direct match
|
|
76
|
+
if (caseSensitive) {
|
|
77
|
+
if (tokenAud === expectedAud)
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (tokenAud.toLowerCase() === expectedAud.toLowerCase())
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// Wildcard matching with ReDoS protection
|
|
85
|
+
if (allowWildcards && expectedAud.includes('*')) {
|
|
86
|
+
// Limit wildcards to prevent potential ReDoS from complex patterns
|
|
87
|
+
const wildcardCount = (expectedAud.match(/\*/g) || []).length;
|
|
88
|
+
if (wildcardCount > 2) {
|
|
89
|
+
// Reject patterns with more than 2 wildcards for safety
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const pattern = expectedAud
|
|
93
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars
|
|
94
|
+
.replace(/\*/g, '[^.]*'); // Convert * to non-greedy segment match (safer than .*)
|
|
95
|
+
const regex = new RegExp(`^${pattern}$`, caseSensitive ? '' : 'i');
|
|
96
|
+
if (regex.test(tokenAud))
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create an audience validator function
|
|
103
|
+
*
|
|
104
|
+
* @param options - Validator options
|
|
105
|
+
* @returns A validation function that takes token audience and returns validation result
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const validator = createAudienceValidator({
|
|
110
|
+
* expectedAudiences: ['https://api.example.com', 'https://api.example.org'],
|
|
111
|
+
* });
|
|
112
|
+
*
|
|
113
|
+
* validator('https://api.example.com'); // => { valid: true, ... }
|
|
114
|
+
* validator('wrong-aud'); // => { valid: false, ... }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
function createAudienceValidator(options) {
|
|
118
|
+
return (audience) => validateAudience(audience, options);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Derive expected audience from the resource URL
|
|
122
|
+
*
|
|
123
|
+
* Per MCP Authorization spec, the audience should typically be the
|
|
124
|
+
* resource server URL (the MCP server URL).
|
|
125
|
+
*
|
|
126
|
+
* @param resourceUrl - The resource server URL
|
|
127
|
+
* @returns Array of expected audiences
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* deriveExpectedAudience('https://api.example.com/v1/mcp');
|
|
132
|
+
* // => ['https://api.example.com/v1/mcp', 'https://api.example.com', 'api.example.com']
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
function deriveExpectedAudience(resourceUrl) {
|
|
136
|
+
const audiences = [];
|
|
137
|
+
try {
|
|
138
|
+
const url = new URL(resourceUrl);
|
|
139
|
+
// Full URL (most specific)
|
|
140
|
+
audiences.push(resourceUrl.replace(/\/$/, ''));
|
|
141
|
+
// Origin only (e.g., https://api.example.com)
|
|
142
|
+
if (url.pathname !== '/' && url.pathname !== '') {
|
|
143
|
+
audiences.push(url.origin);
|
|
144
|
+
}
|
|
145
|
+
// Host only (e.g., api.example.com)
|
|
146
|
+
audiences.push(url.host);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// If not a valid URL, use as-is
|
|
150
|
+
audiences.push(resourceUrl);
|
|
151
|
+
}
|
|
152
|
+
return audiences;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* AudienceValidator class for reusable validation with configuration
|
|
156
|
+
*/
|
|
157
|
+
class AudienceValidator {
|
|
158
|
+
options;
|
|
159
|
+
constructor(options = {}) {
|
|
160
|
+
this.options = {
|
|
161
|
+
expectedAudiences: [...(options.expectedAudiences ?? [])],
|
|
162
|
+
allowNoAudience: options.allowNoAudience ?? false,
|
|
163
|
+
caseSensitive: options.caseSensitive ?? true,
|
|
164
|
+
allowWildcards: options.allowWildcards ?? false,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Validate an audience claim
|
|
169
|
+
*/
|
|
170
|
+
validate(audience) {
|
|
171
|
+
return validateAudience(audience, this.options);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Add expected audiences
|
|
175
|
+
*/
|
|
176
|
+
addAudiences(...audiences) {
|
|
177
|
+
this.options.expectedAudiences.push(...audiences);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Set expected audiences (replace existing)
|
|
181
|
+
*/
|
|
182
|
+
setAudiences(audiences) {
|
|
183
|
+
this.options.expectedAudiences = audiences;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Create validator from resource URL
|
|
187
|
+
*/
|
|
188
|
+
static fromResourceUrl(resourceUrl, options = {}) {
|
|
189
|
+
return new AudienceValidator({
|
|
190
|
+
...options,
|
|
191
|
+
expectedAudiences: deriveExpectedAudience(resourceUrl),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
exports.AudienceValidator = AudienceValidator;
|
|
196
|
+
//# sourceMappingURL=audience.validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audience.validator.js","sourceRoot":"","sources":["../../../../src/auth/utils/audience.validator.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/utils/audience.validator.ts\n\n/**\n * Audience Validator\n *\n * Validates JWT audience claims per RFC 7519 and MCP Authorization spec.\n * The audience (aud) claim identifies the recipients that the JWT is intended for.\n */\n\n/**\n * Validation result\n */\nexport interface AudienceValidationResult {\n /** Whether the audience is valid */\n valid: boolean;\n /** Error message if invalid */\n error?: string;\n /** Matched audience value (if valid) */\n matchedAudience?: string;\n}\n\n/**\n * Audience validator options\n */\nexport interface AudienceValidatorOptions {\n /**\n * Expected audience values\n * Token must contain at least one of these audiences\n */\n expectedAudiences: string[];\n\n /**\n * Whether to allow tokens with no audience claim\n * @default false\n */\n allowNoAudience?: boolean;\n\n /**\n * Case-sensitive comparison\n * @default true\n */\n caseSensitive?: boolean;\n\n /**\n * Allow wildcard matching (e.g., *.example.com)\n * @default false\n */\n allowWildcards?: boolean;\n}\n\n/**\n * Validate JWT audience claim\n *\n * @param tokenAudience - The audience claim from the JWT (can be string or array)\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * ```typescript\n * // Single expected audience\n * validateAudience('https://api.example.com', {\n * expectedAudiences: ['https://api.example.com'],\n * });\n * // => { valid: true, matchedAudience: 'https://api.example.com' }\n *\n * // Multiple audiences in token\n * validateAudience(['aud1', 'aud2', 'aud3'], {\n * expectedAudiences: ['aud2'],\n * });\n * // => { valid: true, matchedAudience: 'aud2' }\n *\n * // No match\n * validateAudience('wrong-aud', {\n * expectedAudiences: ['expected-aud'],\n * });\n * // => { valid: false, error: 'Token audience does not match expected audiences' }\n * ```\n */\nexport function validateAudience(\n tokenAudience: string | string[] | undefined,\n options: AudienceValidatorOptions,\n): AudienceValidationResult {\n const { expectedAudiences, allowNoAudience = false, caseSensitive = true, allowWildcards = false } = options;\n\n // Handle missing audience\n if (tokenAudience === undefined || tokenAudience === null) {\n if (allowNoAudience) {\n return { valid: true };\n }\n return {\n valid: false,\n error: 'Token is missing audience claim',\n };\n }\n\n // Handle empty expected audiences (accept any)\n if (expectedAudiences.length === 0) {\n const firstAud = Array.isArray(tokenAudience) ? tokenAudience[0] : tokenAudience;\n return {\n valid: false,\n error: 'No expected audiences configured - cannot validate token',\n };\n }\n\n // Normalize token audience to array\n const tokenAuds = Array.isArray(tokenAudience) ? tokenAudience : [tokenAudience];\n\n // Check each token audience against expected audiences\n for (const tokenAud of tokenAuds) {\n for (const expectedAud of expectedAudiences) {\n if (matchesAudience(tokenAud, expectedAud, caseSensitive, allowWildcards)) {\n return { valid: true, matchedAudience: tokenAud };\n }\n }\n }\n\n return {\n valid: false,\n error: `Token audience does not match expected audiences. Got: ${tokenAuds.join(\n ', ',\n )}. Expected one of: ${expectedAudiences.join(', ')}`,\n };\n}\n\n/**\n * Check if a token audience matches an expected audience\n */\nfunction matchesAudience(\n tokenAud: string,\n expectedAud: string,\n caseSensitive: boolean,\n allowWildcards: boolean,\n): boolean {\n // Direct match\n if (caseSensitive) {\n if (tokenAud === expectedAud) return true;\n } else {\n if (tokenAud.toLowerCase() === expectedAud.toLowerCase()) return true;\n }\n\n // Wildcard matching with ReDoS protection\n if (allowWildcards && expectedAud.includes('*')) {\n // Limit wildcards to prevent potential ReDoS from complex patterns\n const wildcardCount = (expectedAud.match(/\\*/g) || []).length;\n if (wildcardCount > 2) {\n // Reject patterns with more than 2 wildcards for safety\n return false;\n }\n\n const pattern = expectedAud\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex special chars\n .replace(/\\*/g, '[^.]*'); // Convert * to non-greedy segment match (safer than .*)\n const regex = new RegExp(`^${pattern}$`, caseSensitive ? '' : 'i');\n if (regex.test(tokenAud)) return true;\n }\n\n return false;\n}\n\n/**\n * Create an audience validator function\n *\n * @param options - Validator options\n * @returns A validation function that takes token audience and returns validation result\n *\n * @example\n * ```typescript\n * const validator = createAudienceValidator({\n * expectedAudiences: ['https://api.example.com', 'https://api.example.org'],\n * });\n *\n * validator('https://api.example.com'); // => { valid: true, ... }\n * validator('wrong-aud'); // => { valid: false, ... }\n * ```\n */\nexport function createAudienceValidator(\n options: AudienceValidatorOptions,\n): (audience: string | string[] | undefined) => AudienceValidationResult {\n return (audience) => validateAudience(audience, options);\n}\n\n/**\n * Derive expected audience from the resource URL\n *\n * Per MCP Authorization spec, the audience should typically be the\n * resource server URL (the MCP server URL).\n *\n * @param resourceUrl - The resource server URL\n * @returns Array of expected audiences\n *\n * @example\n * ```typescript\n * deriveExpectedAudience('https://api.example.com/v1/mcp');\n * // => ['https://api.example.com/v1/mcp', 'https://api.example.com', 'api.example.com']\n * ```\n */\nexport function deriveExpectedAudience(resourceUrl: string): string[] {\n const audiences: string[] = [];\n\n try {\n const url = new URL(resourceUrl);\n\n // Full URL (most specific)\n audiences.push(resourceUrl.replace(/\\/$/, ''));\n\n // Origin only (e.g., https://api.example.com)\n if (url.pathname !== '/' && url.pathname !== '') {\n audiences.push(url.origin);\n }\n\n // Host only (e.g., api.example.com)\n audiences.push(url.host);\n } catch {\n // If not a valid URL, use as-is\n audiences.push(resourceUrl);\n }\n\n return audiences;\n}\n\n/**\n * AudienceValidator class for reusable validation with configuration\n */\nexport class AudienceValidator {\n private options: AudienceValidatorOptions;\n\n constructor(options: Partial<AudienceValidatorOptions> & { expectedAudiences?: string[] } = {}) {\n this.options = {\n expectedAudiences: [...(options.expectedAudiences ?? [])],\n allowNoAudience: options.allowNoAudience ?? false,\n caseSensitive: options.caseSensitive ?? true,\n allowWildcards: options.allowWildcards ?? false,\n };\n }\n\n /**\n * Validate an audience claim\n */\n validate(audience: string | string[] | undefined): AudienceValidationResult {\n return validateAudience(audience, this.options);\n }\n\n /**\n * Add expected audiences\n */\n addAudiences(...audiences: string[]): void {\n this.options.expectedAudiences.push(...audiences);\n }\n\n /**\n * Set expected audiences (replace existing)\n */\n setAudiences(audiences: string[]): void {\n this.options.expectedAudiences = audiences;\n }\n\n /**\n * Create validator from resource URL\n */\n static fromResourceUrl(\n resourceUrl: string,\n options: Omit<AudienceValidatorOptions, 'expectedAudiences'> = {},\n ): AudienceValidator {\n return new AudienceValidator({\n ...options,\n expectedAudiences: deriveExpectedAudience(resourceUrl),\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// auth/utils/index.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
tslib_1.__exportStar(require("./www-authenticate.utils"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./audience.validator"), exports);
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/auth/utils/index.ts"],"names":[],"mappings":";AAAA,sBAAsB;;;AAEtB,mEAAyC;AACzC,+DAAqC","sourcesContent":["// auth/utils/index.ts\n\nexport * from './www-authenticate.utils';\nexport * from './audience.validator';\n"]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WWW-Authenticate Header Builder
|
|
3
|
+
*
|
|
4
|
+
* Implements RFC 9728 (OAuth 2.0 Protected Resource Metadata) and
|
|
5
|
+
* RFC 6750 (Bearer Token Usage) compliant WWW-Authenticate headers.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Error codes per RFC 6750 Section 3.1
|
|
9
|
+
*/
|
|
10
|
+
export type BearerErrorCode = 'invalid_request' | 'invalid_token' | 'insufficient_scope';
|
|
11
|
+
/**
|
|
12
|
+
* Options for building WWW-Authenticate header
|
|
13
|
+
*/
|
|
14
|
+
export interface WwwAuthenticateOptions {
|
|
15
|
+
/**
|
|
16
|
+
* The resource_metadata URL pointing to the PRM document
|
|
17
|
+
* Per RFC 9728, this is the primary mechanism for resource discovery
|
|
18
|
+
*/
|
|
19
|
+
resourceMetadataUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* OAuth 2.0 realm (optional per RFC 6750)
|
|
22
|
+
*/
|
|
23
|
+
realm?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Required scopes for the resource (space-delimited)
|
|
26
|
+
*/
|
|
27
|
+
scope?: string | string[];
|
|
28
|
+
/**
|
|
29
|
+
* Error code when authentication fails
|
|
30
|
+
*/
|
|
31
|
+
error?: BearerErrorCode;
|
|
32
|
+
/**
|
|
33
|
+
* Human-readable error description
|
|
34
|
+
*/
|
|
35
|
+
errorDescription?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Error URI pointing to additional information
|
|
38
|
+
*/
|
|
39
|
+
errorUri?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a WWW-Authenticate header for Bearer authentication
|
|
43
|
+
*
|
|
44
|
+
* @param options - Header options
|
|
45
|
+
* @returns The formatted WWW-Authenticate header value
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Basic protected resource metadata header
|
|
50
|
+
* buildWwwAuthenticate({
|
|
51
|
+
* resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource',
|
|
52
|
+
* });
|
|
53
|
+
* // => 'Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"'
|
|
54
|
+
*
|
|
55
|
+
* // With error information
|
|
56
|
+
* buildWwwAuthenticate({
|
|
57
|
+
* resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource',
|
|
58
|
+
* error: 'insufficient_scope',
|
|
59
|
+
* scope: ['read', 'write'],
|
|
60
|
+
* errorDescription: 'Additional permissions required',
|
|
61
|
+
* });
|
|
62
|
+
* // => 'Bearer resource_metadata="...", error="insufficient_scope", scope="read write", error_description="..."'
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildWwwAuthenticate(options?: WwwAuthenticateOptions): string;
|
|
66
|
+
/**
|
|
67
|
+
* Build the Protected Resource Metadata URL for a given base URL and path
|
|
68
|
+
*
|
|
69
|
+
* @param baseUrl - The server base URL
|
|
70
|
+
* @param entryPath - The entry path prefix
|
|
71
|
+
* @param routeBase - The route base path
|
|
72
|
+
* @returns The full PRM URL
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildPrmUrl(baseUrl: string, entryPath: string, routeBase: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Build WWW-Authenticate header for unauthorized requests (no token)
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildUnauthorizedHeader(prmUrl: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Build WWW-Authenticate header for invalid token errors
|
|
81
|
+
*/
|
|
82
|
+
export declare function buildInvalidTokenHeader(prmUrl: string, description?: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Build WWW-Authenticate header for insufficient scope errors
|
|
85
|
+
*/
|
|
86
|
+
export declare function buildInsufficientScopeHeader(prmUrl: string, requiredScopes: string[], description?: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Build WWW-Authenticate header for invalid request errors
|
|
89
|
+
*/
|
|
90
|
+
export declare function buildInvalidRequestHeader(prmUrl: string, description?: string): string;
|
|
91
|
+
/**
|
|
92
|
+
* Parse a WWW-Authenticate header value
|
|
93
|
+
*
|
|
94
|
+
* @param header - The WWW-Authenticate header value
|
|
95
|
+
* @returns Parsed header options
|
|
96
|
+
*/
|
|
97
|
+
export declare function parseWwwAuthenticate(header: string): WwwAuthenticateOptions;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// auth/utils/www-authenticate.utils.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.buildWwwAuthenticate = buildWwwAuthenticate;
|
|
5
|
+
exports.buildPrmUrl = buildPrmUrl;
|
|
6
|
+
exports.buildUnauthorizedHeader = buildUnauthorizedHeader;
|
|
7
|
+
exports.buildInvalidTokenHeader = buildInvalidTokenHeader;
|
|
8
|
+
exports.buildInsufficientScopeHeader = buildInsufficientScopeHeader;
|
|
9
|
+
exports.buildInvalidRequestHeader = buildInvalidRequestHeader;
|
|
10
|
+
exports.parseWwwAuthenticate = parseWwwAuthenticate;
|
|
11
|
+
/**
|
|
12
|
+
* Build a WWW-Authenticate header for Bearer authentication
|
|
13
|
+
*
|
|
14
|
+
* @param options - Header options
|
|
15
|
+
* @returns The formatted WWW-Authenticate header value
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Basic protected resource metadata header
|
|
20
|
+
* buildWwwAuthenticate({
|
|
21
|
+
* resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource',
|
|
22
|
+
* });
|
|
23
|
+
* // => 'Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"'
|
|
24
|
+
*
|
|
25
|
+
* // With error information
|
|
26
|
+
* buildWwwAuthenticate({
|
|
27
|
+
* resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource',
|
|
28
|
+
* error: 'insufficient_scope',
|
|
29
|
+
* scope: ['read', 'write'],
|
|
30
|
+
* errorDescription: 'Additional permissions required',
|
|
31
|
+
* });
|
|
32
|
+
* // => 'Bearer resource_metadata="...", error="insufficient_scope", scope="read write", error_description="..."'
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function buildWwwAuthenticate(options = {}) {
|
|
36
|
+
const parts = ['Bearer'];
|
|
37
|
+
const params = [];
|
|
38
|
+
// Resource metadata URL (RFC 9728)
|
|
39
|
+
if (options.resourceMetadataUrl) {
|
|
40
|
+
params.push(`resource_metadata="${escapeQuotedString(options.resourceMetadataUrl)}"`);
|
|
41
|
+
}
|
|
42
|
+
// Realm (RFC 6750)
|
|
43
|
+
if (options.realm) {
|
|
44
|
+
params.push(`realm="${escapeQuotedString(options.realm)}"`);
|
|
45
|
+
}
|
|
46
|
+
// Error code (RFC 6750)
|
|
47
|
+
if (options.error) {
|
|
48
|
+
params.push(`error="${options.error}"`);
|
|
49
|
+
}
|
|
50
|
+
// Error description (RFC 6750)
|
|
51
|
+
if (options.errorDescription) {
|
|
52
|
+
params.push(`error_description="${escapeQuotedString(options.errorDescription)}"`);
|
|
53
|
+
}
|
|
54
|
+
// Error URI (RFC 6750)
|
|
55
|
+
if (options.errorUri) {
|
|
56
|
+
params.push(`error_uri="${escapeQuotedString(options.errorUri)}"`);
|
|
57
|
+
}
|
|
58
|
+
// Scope (RFC 6750) - space-delimited
|
|
59
|
+
if (options.scope) {
|
|
60
|
+
const scopeValue = Array.isArray(options.scope) ? options.scope.join(' ') : options.scope;
|
|
61
|
+
params.push(`scope="${escapeQuotedString(scopeValue)}"`);
|
|
62
|
+
}
|
|
63
|
+
if (params.length > 0) {
|
|
64
|
+
parts.push(params.join(', '));
|
|
65
|
+
}
|
|
66
|
+
return parts.join(' ');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build the Protected Resource Metadata URL for a given base URL and path
|
|
70
|
+
*
|
|
71
|
+
* @param baseUrl - The server base URL
|
|
72
|
+
* @param entryPath - The entry path prefix
|
|
73
|
+
* @param routeBase - The route base path
|
|
74
|
+
* @returns The full PRM URL
|
|
75
|
+
*/
|
|
76
|
+
function buildPrmUrl(baseUrl, entryPath, routeBase) {
|
|
77
|
+
const normalizedEntry = normalizePathSegment(entryPath);
|
|
78
|
+
const normalizedRoute = normalizePathSegment(routeBase);
|
|
79
|
+
return `${baseUrl}/.well-known/oauth-protected-resource${normalizedEntry}${normalizedRoute}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build WWW-Authenticate header for unauthorized requests (no token)
|
|
83
|
+
*/
|
|
84
|
+
function buildUnauthorizedHeader(prmUrl) {
|
|
85
|
+
return buildWwwAuthenticate({
|
|
86
|
+
resourceMetadataUrl: prmUrl,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build WWW-Authenticate header for invalid token errors
|
|
91
|
+
*/
|
|
92
|
+
function buildInvalidTokenHeader(prmUrl, description) {
|
|
93
|
+
return buildWwwAuthenticate({
|
|
94
|
+
resourceMetadataUrl: prmUrl,
|
|
95
|
+
error: 'invalid_token',
|
|
96
|
+
errorDescription: description ?? 'The access token is invalid or expired',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build WWW-Authenticate header for insufficient scope errors
|
|
101
|
+
*/
|
|
102
|
+
function buildInsufficientScopeHeader(prmUrl, requiredScopes, description) {
|
|
103
|
+
return buildWwwAuthenticate({
|
|
104
|
+
resourceMetadataUrl: prmUrl,
|
|
105
|
+
error: 'insufficient_scope',
|
|
106
|
+
scope: requiredScopes,
|
|
107
|
+
errorDescription: description ?? 'The request requires higher privileges',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Build WWW-Authenticate header for invalid request errors
|
|
112
|
+
*/
|
|
113
|
+
function buildInvalidRequestHeader(prmUrl, description) {
|
|
114
|
+
return buildWwwAuthenticate({
|
|
115
|
+
resourceMetadataUrl: prmUrl,
|
|
116
|
+
error: 'invalid_request',
|
|
117
|
+
errorDescription: description ?? 'The request is missing required parameters',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Parse a WWW-Authenticate header value
|
|
122
|
+
*
|
|
123
|
+
* @param header - The WWW-Authenticate header value
|
|
124
|
+
* @returns Parsed header options
|
|
125
|
+
*/
|
|
126
|
+
function parseWwwAuthenticate(header) {
|
|
127
|
+
const result = {};
|
|
128
|
+
// Check for Bearer scheme
|
|
129
|
+
if (!header.toLowerCase().startsWith('bearer')) {
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
// Extract parameters
|
|
133
|
+
const paramString = header.substring(6).trim();
|
|
134
|
+
const paramRegex = /(\w+)="([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
135
|
+
let match;
|
|
136
|
+
while ((match = paramRegex.exec(paramString)) !== null) {
|
|
137
|
+
const [, key, value] = match;
|
|
138
|
+
const unescapedValue = unescapeQuotedString(value);
|
|
139
|
+
switch (key.toLowerCase()) {
|
|
140
|
+
case 'resource_metadata':
|
|
141
|
+
result.resourceMetadataUrl = unescapedValue;
|
|
142
|
+
break;
|
|
143
|
+
case 'realm':
|
|
144
|
+
result.realm = unescapedValue;
|
|
145
|
+
break;
|
|
146
|
+
case 'error':
|
|
147
|
+
result.error = unescapedValue;
|
|
148
|
+
break;
|
|
149
|
+
case 'error_description':
|
|
150
|
+
result.errorDescription = unescapedValue;
|
|
151
|
+
break;
|
|
152
|
+
case 'error_uri':
|
|
153
|
+
result.errorUri = unescapedValue;
|
|
154
|
+
break;
|
|
155
|
+
case 'scope':
|
|
156
|
+
result.scope = unescapedValue;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Escape special characters for quoted-string per RFC 7230
|
|
164
|
+
*/
|
|
165
|
+
function escapeQuotedString(value) {
|
|
166
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Unescape quoted-string per RFC 7230
|
|
170
|
+
*/
|
|
171
|
+
function unescapeQuotedString(value) {
|
|
172
|
+
return value.replace(/\\(.)/g, '$1');
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Normalize a path segment (ensure leading slash, no trailing slash)
|
|
176
|
+
*/
|
|
177
|
+
function normalizePathSegment(path) {
|
|
178
|
+
if (!path || path === '/')
|
|
179
|
+
return '';
|
|
180
|
+
const normalized = path.startsWith('/') ? path : `/${path}`;
|
|
181
|
+
return normalized.replace(/\/+$/, '');
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=www-authenticate.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"www-authenticate.utils.js","sourceRoot":"","sources":["../../../../src/auth/utils/www-authenticate.utils.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/utils/www-authenticate.utils.ts\n\n/**\n * WWW-Authenticate Header Builder\n *\n * Implements RFC 9728 (OAuth 2.0 Protected Resource Metadata) and\n * RFC 6750 (Bearer Token Usage) compliant WWW-Authenticate headers.\n */\n\n/**\n * Error codes per RFC 6750 Section 3.1\n */\nexport type BearerErrorCode = 'invalid_request' | 'invalid_token' | 'insufficient_scope';\n\n/**\n * Options for building WWW-Authenticate header\n */\nexport interface WwwAuthenticateOptions {\n /**\n * The resource_metadata URL pointing to the PRM document\n * Per RFC 9728, this is the primary mechanism for resource discovery\n */\n resourceMetadataUrl?: string;\n\n /**\n * OAuth 2.0 realm (optional per RFC 6750)\n */\n realm?: string;\n\n /**\n * Required scopes for the resource (space-delimited)\n */\n scope?: string | string[];\n\n /**\n * Error code when authentication fails\n */\n error?: BearerErrorCode;\n\n /**\n * Human-readable error description\n */\n errorDescription?: string;\n\n /**\n * Error URI pointing to additional information\n */\n errorUri?: string;\n}\n\n/**\n * Build a WWW-Authenticate header for Bearer authentication\n *\n * @param options - Header options\n * @returns The formatted WWW-Authenticate header value\n *\n * @example\n * ```typescript\n * // Basic protected resource metadata header\n * buildWwwAuthenticate({\n * resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource',\n * });\n * // => 'Bearer resource_metadata=\"https://api.example.com/.well-known/oauth-protected-resource\"'\n *\n * // With error information\n * buildWwwAuthenticate({\n * resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource',\n * error: 'insufficient_scope',\n * scope: ['read', 'write'],\n * errorDescription: 'Additional permissions required',\n * });\n * // => 'Bearer resource_metadata=\"...\", error=\"insufficient_scope\", scope=\"read write\", error_description=\"...\"'\n * ```\n */\nexport function buildWwwAuthenticate(options: WwwAuthenticateOptions = {}): string {\n const parts: string[] = ['Bearer'];\n const params: string[] = [];\n\n // Resource metadata URL (RFC 9728)\n if (options.resourceMetadataUrl) {\n params.push(`resource_metadata=\"${escapeQuotedString(options.resourceMetadataUrl)}\"`);\n }\n\n // Realm (RFC 6750)\n if (options.realm) {\n params.push(`realm=\"${escapeQuotedString(options.realm)}\"`);\n }\n\n // Error code (RFC 6750)\n if (options.error) {\n params.push(`error=\"${options.error}\"`);\n }\n\n // Error description (RFC 6750)\n if (options.errorDescription) {\n params.push(`error_description=\"${escapeQuotedString(options.errorDescription)}\"`);\n }\n\n // Error URI (RFC 6750)\n if (options.errorUri) {\n params.push(`error_uri=\"${escapeQuotedString(options.errorUri)}\"`);\n }\n\n // Scope (RFC 6750) - space-delimited\n if (options.scope) {\n const scopeValue = Array.isArray(options.scope) ? options.scope.join(' ') : options.scope;\n params.push(`scope=\"${escapeQuotedString(scopeValue)}\"`);\n }\n\n if (params.length > 0) {\n parts.push(params.join(', '));\n }\n\n return parts.join(' ');\n}\n\n/**\n * Build the Protected Resource Metadata URL for a given base URL and path\n *\n * @param baseUrl - The server base URL\n * @param entryPath - The entry path prefix\n * @param routeBase - The route base path\n * @returns The full PRM URL\n */\nexport function buildPrmUrl(baseUrl: string, entryPath: string, routeBase: string): string {\n const normalizedEntry = normalizePathSegment(entryPath);\n const normalizedRoute = normalizePathSegment(routeBase);\n return `${baseUrl}/.well-known/oauth-protected-resource${normalizedEntry}${normalizedRoute}`;\n}\n\n/**\n * Build WWW-Authenticate header for unauthorized requests (no token)\n */\nexport function buildUnauthorizedHeader(prmUrl: string): string {\n return buildWwwAuthenticate({\n resourceMetadataUrl: prmUrl,\n });\n}\n\n/**\n * Build WWW-Authenticate header for invalid token errors\n */\nexport function buildInvalidTokenHeader(prmUrl: string, description?: string): string {\n return buildWwwAuthenticate({\n resourceMetadataUrl: prmUrl,\n error: 'invalid_token',\n errorDescription: description ?? 'The access token is invalid or expired',\n });\n}\n\n/**\n * Build WWW-Authenticate header for insufficient scope errors\n */\nexport function buildInsufficientScopeHeader(prmUrl: string, requiredScopes: string[], description?: string): string {\n return buildWwwAuthenticate({\n resourceMetadataUrl: prmUrl,\n error: 'insufficient_scope',\n scope: requiredScopes,\n errorDescription: description ?? 'The request requires higher privileges',\n });\n}\n\n/**\n * Build WWW-Authenticate header for invalid request errors\n */\nexport function buildInvalidRequestHeader(prmUrl: string, description?: string): string {\n return buildWwwAuthenticate({\n resourceMetadataUrl: prmUrl,\n error: 'invalid_request',\n errorDescription: description ?? 'The request is missing required parameters',\n });\n}\n\n/**\n * Parse a WWW-Authenticate header value\n *\n * @param header - The WWW-Authenticate header value\n * @returns Parsed header options\n */\nexport function parseWwwAuthenticate(header: string): WwwAuthenticateOptions {\n const result: WwwAuthenticateOptions = {};\n\n // Check for Bearer scheme\n if (!header.toLowerCase().startsWith('bearer')) {\n return result;\n }\n\n // Extract parameters\n const paramString = header.substring(6).trim();\n const paramRegex = /(\\w+)=\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"/g;\n let match: RegExpExecArray | null;\n\n while ((match = paramRegex.exec(paramString)) !== null) {\n const [, key, value] = match;\n const unescapedValue = unescapeQuotedString(value);\n\n switch (key.toLowerCase()) {\n case 'resource_metadata':\n result.resourceMetadataUrl = unescapedValue;\n break;\n case 'realm':\n result.realm = unescapedValue;\n break;\n case 'error':\n result.error = unescapedValue as BearerErrorCode;\n break;\n case 'error_description':\n result.errorDescription = unescapedValue;\n break;\n case 'error_uri':\n result.errorUri = unescapedValue;\n break;\n case 'scope':\n result.scope = unescapedValue;\n break;\n }\n }\n\n return result;\n}\n\n/**\n * Escape special characters for quoted-string per RFC 7230\n */\nfunction escapeQuotedString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Unescape quoted-string per RFC 7230\n */\nfunction unescapeQuotedString(value: string): string {\n return value.replace(/\\\\(.)/g, '$1');\n}\n\n/**\n * Normalize a path segment (ensure leading slash, no trailing slash)\n */\nfunction normalizePathSegment(path: string): string {\n if (!path || path === '/') return '';\n const normalized = path.startsWith('/') ? path : `/${path}`;\n return normalized.replace(/\\/+$/, '');\n}\n"]}
|