@toa.io/extensions.exposition 1.0.0-alpha.282 → 1.0.0-alpha.284
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/CHANGELOG.md +56 -0
- package/components/exposition.octets/manifest.toa.yaml +11 -0
- package/components/exposition.octets/types/index.d.ts +3 -0
- package/components/exposition.octets/types/toa.d.ts +41 -0
- package/components/identity.bans/operations/transit.js +1 -1
- package/components/identity.bans/tsconfig.json +1 -1
- package/components/identity.bans/tsconfig.tsbuildinfo +1 -1
- package/components/identity.bans/types/index.d.ts +3 -0
- package/components/identity.bans/types/toa.d.ts +34 -0
- package/components/identity.basic/manifest.toa.yaml +13 -2
- package/components/identity.basic/operations/add.js +1 -1
- package/components/identity.basic/operations/authenticate.js +1 -1
- package/components/identity.basic/operations/check.js +1 -1
- package/components/identity.basic/operations/delete.js +1 -1
- package/components/identity.basic/operations/incept.js +18 -2
- package/components/identity.basic/operations/info.js +1 -1
- package/components/identity.basic/operations/lib/credentials.js +1 -1
- package/components/identity.basic/operations/transit.js +3 -2
- package/components/identity.basic/source/add.ts +3 -3
- package/components/identity.basic/source/authenticate.ts +2 -2
- package/components/identity.basic/source/check.ts +1 -1
- package/components/identity.basic/source/delete.ts +2 -2
- package/components/identity.basic/source/incept.ts +31 -3
- package/components/identity.basic/source/info.ts +1 -1
- package/components/identity.basic/source/transit.ts +6 -4
- package/components/identity.basic/tsconfig.json +1 -1
- package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
- package/components/identity.basic/types/index.d.ts +22 -0
- package/components/identity.basic/types/toa.d.ts +121 -0
- package/components/identity.clients/manifest.toa.yaml +243 -0
- package/components/identity.clients/operations/describe.js +44 -0
- package/components/identity.clients/operations/lib/Context.js +2 -0
- package/components/identity.clients/operations/lib/Entity.js +2 -0
- package/components/identity.clients/operations/lib/canonical.js +24 -0
- package/components/identity.clients/operations/lib/cimd.js +86 -0
- package/components/identity.clients/operations/lib/errors.js +5 -0
- package/components/identity.clients/operations/lib/index.js +7 -0
- package/components/identity.clients/operations/lib/redirect.js +32 -0
- package/components/identity.clients/operations/register.js +84 -0
- package/components/identity.clients/source/describe.ts +65 -0
- package/components/identity.clients/source/lib/Context.ts +38 -0
- package/components/identity.clients/source/lib/Entity.ts +31 -0
- package/components/identity.clients/source/lib/canonical.test.ts +43 -0
- package/components/identity.clients/source/lib/canonical.ts +27 -0
- package/components/identity.clients/source/lib/cimd.test.ts +120 -0
- package/components/identity.clients/source/lib/cimd.ts +107 -0
- package/components/identity.clients/source/lib/errors.ts +4 -0
- package/components/identity.clients/source/lib/index.ts +6 -0
- package/components/identity.clients/source/lib/redirect.test.ts +41 -0
- package/components/identity.clients/source/lib/redirect.ts +35 -0
- package/components/identity.clients/source/register.ts +125 -0
- package/components/identity.clients/tsconfig.json +10 -0
- package/components/identity.clients/tsconfig.tsbuildinfo +1 -0
- package/components/identity.clients/types/index.d.ts +3 -0
- package/components/identity.clients/types/toa.d.ts +109 -0
- package/components/identity.credentials/operations/list.js +1 -1
- package/components/identity.credentials/source/list.ts +2 -1
- package/components/identity.credentials/tsconfig.json +4 -2
- package/components/identity.credentials/tsconfig.tsbuildinfo +1 -1
- package/components/identity.credentials/types/index.d.ts +3 -0
- package/components/identity.credentials/types/toa.d.ts +30 -0
- package/components/identity.federation/manifest.toa.yaml +37 -1
- package/components/identity.federation/operations/authenticate.js +7 -3
- package/components/identity.federation/operations/create.js +1 -1
- package/components/identity.federation/operations/decode.js +1 -1
- package/components/identity.federation/operations/delete.js +1 -1
- package/components/identity.federation/operations/incept.js +6 -2
- package/components/identity.federation/operations/lib/Configuration.js +1 -1
- package/components/identity.federation/operations/lib/Ctx.js +1 -1
- package/components/identity.federation/operations/lib/Payload.js +1 -1
- package/components/identity.federation/operations/lib/decode.js +23 -9
- package/components/identity.federation/operations/lib/discovery.js +1 -1
- package/components/identity.federation/operations/lib/errors.js +5 -1
- package/components/identity.federation/operations/lib/exchange.js +12 -7
- package/components/identity.federation/operations/lib/index.js +2 -1
- package/components/identity.federation/operations/lib/jose.js +1 -1
- package/components/identity.federation/operations/lib/principal.js +14 -0
- package/components/identity.federation/operations/lib/resolve.js +1 -1
- package/components/identity.federation/operations/list.js +1 -1
- package/components/identity.federation/operations/types/Scheme.js +1 -1
- package/components/identity.federation/operations/types/configuration.js +1 -1
- package/components/identity.federation/operations/types/context.js +1 -1
- package/components/identity.federation/operations/types/entity.js +1 -1
- package/components/identity.federation/operations/types/index.js +1 -1
- package/components/identity.federation/source/authenticate.ts +9 -3
- package/components/identity.federation/source/incept.ts +7 -1
- package/components/identity.federation/source/lib/Ctx.ts +2 -2
- package/components/identity.federation/source/lib/decode.ts +25 -9
- package/components/identity.federation/source/lib/errors.ts +5 -0
- package/components/identity.federation/source/lib/exchange.ts +11 -7
- package/components/identity.federation/source/lib/index.ts +1 -0
- package/components/identity.federation/source/lib/principal.ts +17 -0
- package/components/identity.federation/source/types/configuration.ts +1 -1
- package/components/identity.federation/source/types/context.ts +6 -2
- package/components/identity.federation/tsconfig.json +4 -2
- package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/types/index.d.ts +3 -0
- package/components/identity.federation/types/toa.d.ts +127 -0
- package/components/identity.grants/manifest.toa.yaml +245 -0
- package/components/identity.grants/operations/authorize.js +54 -0
- package/components/identity.grants/operations/exchange.js +89 -0
- package/components/identity.grants/operations/lib/code.js +30 -0
- package/components/identity.grants/operations/lib/identify.js +15 -0
- package/components/identity.grants/operations/lib/index.js +3 -0
- package/components/identity.grants/operations/list.js +18 -0
- package/components/identity.grants/operations/revoke.js +25 -0
- package/components/identity.grants/source/authorize.ts +90 -0
- package/components/identity.grants/source/exchange.ts +130 -0
- package/components/identity.grants/source/lib/code.ts +39 -0
- package/components/identity.grants/source/lib/identify.ts +16 -0
- package/components/identity.grants/source/lib/index.ts +3 -0
- package/components/identity.grants/source/list.ts +28 -0
- package/components/identity.grants/source/revoke.ts +38 -0
- package/components/identity.grants/tsconfig.json +10 -0
- package/components/identity.grants/tsconfig.tsbuildinfo +1 -0
- package/components/identity.grants/types/index.d.ts +60 -0
- package/components/identity.grants/types/toa.d.ts +108 -0
- package/components/identity.keys/manifest.toa.yaml +1 -0
- package/components/identity.keys/operations/create.js +1 -1
- package/components/identity.keys/operations/disable.js +1 -1
- package/components/identity.keys/operations/revoke.js +1 -1
- package/components/identity.keys/source/create.ts +1 -1
- package/components/identity.keys/source/revoke.ts +1 -1
- package/components/identity.keys/tsconfig.json +1 -1
- package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.keys/types/index.d.ts +3 -0
- package/components/identity.keys/types/toa.d.ts +47 -0
- package/components/identity.otp/operations/authenticate.js +1 -1
- package/components/identity.otp/operations/issue.js +1 -1
- package/components/identity.otp/operations/lib/Context.js +1 -1
- package/components/identity.otp/operations/lib/Entity.js +1 -1
- package/components/identity.otp/operations/lib/index.js +1 -1
- package/components/identity.otp/source/lib/Context.ts +4 -2
- package/components/identity.otp/tsconfig.json +1 -1
- package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
- package/components/identity.otp/types/index.d.ts +3 -0
- package/components/identity.otp/types/toa.d.ts +56 -0
- package/components/identity.passkeys/manifest.toa.yaml +3 -0
- package/components/identity.passkeys/operations/authenticate.js +1 -1
- package/components/identity.passkeys/operations/challenge.js +1 -1
- package/components/identity.passkeys/operations/create.js +1 -1
- package/components/identity.passkeys/operations/delete.js +1 -1
- package/components/identity.passkeys/operations/lib/const.js +1 -1
- package/components/identity.passkeys/operations/list.js +1 -1
- package/components/identity.passkeys/operations/types/Configuration.js +1 -1
- package/components/identity.passkeys/operations/types/Context.js +1 -1
- package/components/identity.passkeys/operations/types/Passkey.js +1 -1
- package/components/identity.passkeys/operations/types/index.js +1 -1
- package/components/identity.passkeys/operations/use.js +1 -1
- package/components/identity.passkeys/source/authenticate.ts +1 -1
- package/components/identity.passkeys/source/challenge.ts +1 -1
- package/components/identity.passkeys/source/create.ts +1 -1
- package/components/identity.passkeys/source/types/Context.ts +2 -1
- package/components/identity.passkeys/source/use.ts +1 -1
- package/components/identity.passkeys/tsconfig.json +1 -1
- package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/types/index.d.ts +3 -0
- package/components/identity.passkeys/types/toa.d.ts +120 -0
- package/components/identity.roles/operations/grant.js +1 -1
- package/components/identity.roles/operations/lib/Entity.js +1 -1
- package/components/identity.roles/operations/list.js +1 -1
- package/components/identity.roles/operations/principal.js +29 -5
- package/components/identity.roles/source/principal.ts +33 -5
- package/components/identity.roles/tsconfig.json +1 -1
- package/components/identity.roles/tsconfig.tsbuildinfo +1 -1
- package/components/identity.roles/types/index.d.ts +3 -0
- package/components/identity.roles/types/toa.d.ts +43 -0
- package/components/identity.tokens/manifest.toa.yaml +34 -3
- package/components/identity.tokens/operations/authenticate.js +9 -1
- package/components/identity.tokens/operations/decrypt.js +3 -3
- package/components/identity.tokens/operations/encrypt.js +1 -1
- package/components/identity.tokens/operations/issue.js +3 -2
- package/components/identity.tokens/operations/lib/form.js +24 -0
- package/components/identity.tokens/operations/lib/index.js +2 -2
- package/components/identity.tokens/operations/lib/jose.js +1 -1
- package/components/identity.tokens/operations/lib/key.js +1 -1
- package/components/identity.tokens/operations/lib/pad.js +1 -1
- package/components/identity.tokens/operations/revoke.js +1 -1
- package/components/identity.tokens/source/authenticate.test.ts +8 -5
- package/components/identity.tokens/source/authenticate.ts +12 -1
- package/components/identity.tokens/source/decrypt.test.ts +1 -1
- package/components/identity.tokens/source/decrypt.ts +4 -3
- package/components/identity.tokens/source/encrypt.test.ts +1 -1
- package/components/identity.tokens/source/encrypt.ts +2 -1
- package/components/identity.tokens/source/issue.test.ts +71 -0
- package/components/identity.tokens/source/issue.ts +4 -2
- package/components/identity.tokens/source/lib/form.test.ts +25 -0
- package/components/identity.tokens/source/lib/form.ts +31 -0
- package/components/identity.tokens/source/lib/index.ts +2 -1
- package/components/identity.tokens/tsconfig.json +1 -1
- package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
- package/components/identity.tokens/types/index.d.ts +61 -0
- package/components/identity.tokens/types/toa.d.ts +113 -0
- package/documentation/access.md +13 -0
- package/documentation/cache.md +10 -1
- package/documentation/components.md +3 -0
- package/documentation/consent.md +101 -0
- package/documentation/identity.md +3 -1
- package/documentation/introspection.md +13 -5
- package/documentation/io.md +42 -2
- package/documentation/map.md +8 -0
- package/documentation/mcp.md +180 -0
- package/documentation/oauth.md +180 -0
- package/documentation/protocol.md +4 -0
- package/documentation/rpc.md +140 -0
- package/documentation/tree.md +3 -0
- package/features/auth.bearer.feature +120 -0
- package/features/cache.feature +46 -0
- package/features/identity.basic.feature +44 -0
- package/features/identity.federation.feature +2 -5
- package/features/introspection.feature +333 -0
- package/features/mcp.feature +687 -0
- package/features/oauth.clients.feature +195 -0
- package/features/oauth.discovery.feature +164 -0
- package/features/oauth.grants.feature +299 -0
- package/features/octets.cloudinary.feature +1 -0
- package/features/rpc.feature +429 -0
- package/features/steps/Cloudinary.ts +13 -0
- package/features/steps/Components.ts +0 -4
- package/features/steps/Gateway.ts +23 -2
- package/features/steps/Parameters.ts +22 -7
- package/features/steps/Realtime.ts +3 -0
- package/features/steps/components/echo/manifest.toa.yaml +3 -0
- package/features/steps/components/octets.tester/manifest.toa.yaml +2 -0
- package/features/steps/components/octets.tester/operations/redirect.js +6 -1
- package/features/steps/components/pots/manifest.toa.yaml +2 -0
- package/features/steps/components/users/manifest.toa.yaml +1 -0
- package/package.json +8 -6
- package/readme.md +6 -0
- package/schemas/annotation.cos.yaml +63 -0
- package/schemas/call.cos.yaml +18 -0
- package/source/Annotation.ts +54 -0
- package/source/Directive.test.ts +15 -15
- package/source/Directive.ts +77 -9
- package/source/Endpoint.test.ts +67 -0
- package/source/Endpoint.ts +31 -11
- package/source/Factory.ts +6 -2
- package/source/Gateway.ts +73 -11
- package/source/HTTP/Context.ts +7 -0
- package/source/HTTP/Server.ts +15 -5
- package/source/HTTP/exceptions.ts +2 -2
- package/source/HTTP/formats/form.ts +21 -0
- package/source/HTTP/formats/index.ts +17 -2
- package/source/HTTP/messages.test.ts +25 -1
- package/source/HTTP/messages.ts +20 -4
- package/source/Interception.ts +8 -2
- package/source/Introspection.ts +102 -0
- package/source/MCP/Server.ts +275 -0
- package/source/MCP/discover.ts +52 -0
- package/source/MCP/errors.ts +81 -0
- package/source/MCP/index.ts +1 -0
- package/source/MCP/schema.test.ts +85 -0
- package/source/MCP/schema.ts +87 -0
- package/source/MCP/tools.ts +156 -0
- package/source/MCP/types.ts +81 -0
- package/source/Query.ts +48 -8
- package/source/RPC/Context.ts +78 -0
- package/source/RPC/Dispatcher.ts +130 -0
- package/source/RPC/errors.test.ts +52 -0
- package/source/RPC/errors.ts +83 -0
- package/source/RPC/index.ts +1 -0
- package/source/RPC/names.test.ts +122 -0
- package/source/RPC/names.ts +182 -0
- package/source/RPC/types.ts +34 -0
- package/source/RTD/Directives.ts +52 -2
- package/source/RTD/Endpoint.ts +2 -1
- package/source/RTD/Method.ts +14 -11
- package/source/RTD/Node.ts +38 -7
- package/source/RTD/Route.ts +30 -3
- package/source/RTD/Tree.ts +37 -1
- package/source/RTD/expiration.test.ts +1 -1
- package/source/RTD/walk.test.ts +110 -0
- package/source/const.ts +15 -0
- package/source/deployment.ts +11 -1
- package/source/directives/auth/Anonymous.test.ts +38 -0
- package/source/directives/auth/Anonymous.ts +12 -0
- package/source/directives/auth/Anyone.ts +4 -0
- package/source/directives/auth/Authorization.ts +108 -22
- package/source/directives/auth/Delegate.test.ts +36 -0
- package/source/directives/auth/Delegate.ts +14 -0
- package/source/directives/auth/Echo.ts +5 -0
- package/source/directives/auth/Incept.ts +16 -10
- package/source/directives/auth/Role.ts +13 -0
- package/source/directives/auth/Rule.ts +29 -0
- package/source/directives/auth/Scheme.ts +5 -0
- package/source/directives/auth/schemes.test.ts +8 -4
- package/source/directives/auth/schemes.ts +22 -9
- package/source/directives/auth/types.ts +26 -2
- package/source/directives/cache/Cache.ts +14 -5
- package/source/directives/cache/Control.ts +0 -3
- package/source/directives/dev/Development.ts +1 -1
- package/source/directives/flow/Fetch.ts +1 -1
- package/source/directives/flow/Flow.ts +1 -1
- package/source/directives/index.ts +5 -2
- package/source/directives/io/Directive.ts +1 -1
- package/source/directives/io/IO.ts +34 -3
- package/source/directives/io/Input.ts +3 -2
- package/source/directives/io/Output.ts +10 -4
- package/source/directives/io/Status.test.ts +84 -0
- package/source/directives/io/Status.ts +46 -0
- package/source/directives/io/Throttle.ts +1 -1
- package/source/directives/io/lib/throttle/Quotas.ts +2 -2
- package/source/directives/map/Headers.ts +11 -0
- package/source/directives/map/Map.ts +10 -1
- package/source/directives/map/Mapping.ts +23 -0
- package/source/directives/map/Segments.ts +12 -0
- package/source/directives/mcp/MCP.ts +33 -0
- package/source/directives/mcp/Tool.test.ts +66 -0
- package/source/directives/mcp/Tool.ts +28 -0
- package/source/directives/mcp/index.ts +6 -0
- package/source/directives/oauth/Discovery.ts +70 -0
- package/source/directives/oauth/documents.ts +113 -0
- package/source/directives/oauth/index.ts +3 -0
- package/source/directives/octets/Delete.ts +1 -1
- package/source/directives/octets/Get.ts +1 -1
- package/source/directives/octets/Octets.ts +1 -1
- package/source/directives/octets/Workflow.ts +1 -1
- package/source/directives/octets/workflows/Execution.ts +1 -1
- package/source/directives/require/Directive.ts +1 -1
- package/source/directives/require/Headers.ts +1 -1
- package/source/directives/require/Require.ts +2 -2
- package/source/index.ts +6 -0
- package/source/octets.d.ts +17 -0
- package/source/schemas.ts +2 -0
- package/source/userland.ts +47 -0
- package/transpiled/Annotation.d.ts +41 -0
- package/transpiled/Directive.d.ts +18 -1
- package/transpiled/Directive.js +50 -6
- package/transpiled/Directive.js.map +1 -1
- package/transpiled/Endpoint.d.ts +9 -0
- package/transpiled/Endpoint.js +27 -11
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Factory.js +6 -2
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Gateway.d.ts +19 -2
- package/transpiled/Gateway.js +51 -9
- package/transpiled/Gateway.js.map +1 -1
- package/transpiled/HTTP/Context.d.ts +6 -0
- package/transpiled/HTTP/Context.js +6 -0
- package/transpiled/HTTP/Context.js.map +1 -1
- package/transpiled/HTTP/Server.d.ts +8 -1
- package/transpiled/HTTP/Server.js +4 -3
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/HTTP/exceptions.d.ts +1 -1
- package/transpiled/HTTP/exceptions.js +2 -2
- package/transpiled/HTTP/exceptions.js.map +1 -1
- package/transpiled/HTTP/formats/form.d.ts +9 -0
- package/transpiled/HTTP/formats/form.js +18 -0
- package/transpiled/HTTP/formats/form.js.map +1 -0
- package/transpiled/HTTP/formats/index.d.ts +10 -2
- package/transpiled/HTTP/formats/index.js +10 -0
- package/transpiled/HTTP/formats/index.js.map +1 -1
- package/transpiled/HTTP/messages.d.ts +12 -0
- package/transpiled/HTTP/messages.js +12 -4
- package/transpiled/HTTP/messages.js.map +1 -1
- package/transpiled/Interception.d.ts +4 -1
- package/transpiled/Interception.js +4 -2
- package/transpiled/Interception.js.map +1 -1
- package/transpiled/Introspection.d.ts +40 -0
- package/transpiled/Introspection.js +50 -1
- package/transpiled/Introspection.js.map +1 -1
- package/transpiled/MCP/Server.d.ts +41 -0
- package/transpiled/MCP/Server.js +180 -0
- package/transpiled/MCP/Server.js.map +1 -0
- package/transpiled/MCP/discover.d.ts +13 -0
- package/transpiled/MCP/discover.js +29 -0
- package/transpiled/MCP/discover.js.map +1 -0
- package/transpiled/MCP/errors.d.ts +24 -0
- package/transpiled/MCP/errors.js +67 -0
- package/transpiled/MCP/errors.js.map +1 -0
- package/transpiled/MCP/index.d.ts +1 -0
- package/transpiled/MCP/index.js +2 -0
- package/transpiled/MCP/index.js.map +1 -0
- package/transpiled/MCP/schema.d.ts +18 -0
- package/transpiled/MCP/schema.js +64 -0
- package/transpiled/MCP/schema.js.map +1 -0
- package/transpiled/MCP/tools.d.ts +31 -0
- package/transpiled/MCP/tools.js +121 -0
- package/transpiled/MCP/tools.js.map +1 -0
- package/transpiled/MCP/types.d.ts +64 -0
- package/transpiled/MCP/types.js +13 -0
- package/transpiled/MCP/types.js.map +1 -0
- package/transpiled/Query.d.ts +7 -0
- package/transpiled/Query.js +38 -7
- package/transpiled/Query.js.map +1 -1
- package/transpiled/RPC/Context.d.ts +11 -0
- package/transpiled/RPC/Context.js +63 -0
- package/transpiled/RPC/Context.js.map +1 -0
- package/transpiled/RPC/Dispatcher.d.ts +19 -0
- package/transpiled/RPC/Dispatcher.js +100 -0
- package/transpiled/RPC/Dispatcher.js.map +1 -0
- package/transpiled/RPC/errors.d.ts +18 -0
- package/transpiled/RPC/errors.js +67 -0
- package/transpiled/RPC/errors.js.map +1 -0
- package/transpiled/RPC/index.d.ts +1 -0
- package/transpiled/RPC/index.js +2 -0
- package/transpiled/RPC/index.js.map +1 -0
- package/transpiled/RPC/names.d.ts +30 -0
- package/transpiled/RPC/names.js +121 -0
- package/transpiled/RPC/names.js.map +1 -0
- package/transpiled/RPC/types.d.ts +27 -0
- package/transpiled/RPC/types.js +8 -0
- package/transpiled/RPC/types.js.map +1 -0
- package/transpiled/RTD/Directives.d.ts +41 -2
- package/transpiled/RTD/Endpoint.d.ts +2 -1
- package/transpiled/RTD/Method.d.ts +8 -3
- package/transpiled/RTD/Method.js +12 -10
- package/transpiled/RTD/Method.js.map +1 -1
- package/transpiled/RTD/Node.d.ts +20 -2
- package/transpiled/RTD/Node.js +22 -4
- package/transpiled/RTD/Node.js.map +1 -1
- package/transpiled/RTD/Route.d.ts +2 -1
- package/transpiled/RTD/Route.js +27 -2
- package/transpiled/RTD/Route.js.map +1 -1
- package/transpiled/RTD/Tree.d.ts +3 -1
- package/transpiled/RTD/Tree.js +26 -0
- package/transpiled/RTD/Tree.js.map +1 -1
- package/transpiled/const.d.ts +12 -0
- package/transpiled/const.js +12 -0
- package/transpiled/const.js.map +1 -1
- package/transpiled/deployment.js +6 -0
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/directives/auth/Anonymous.d.ts +6 -0
- package/transpiled/directives/auth/Anonymous.js +10 -0
- package/transpiled/directives/auth/Anonymous.js.map +1 -1
- package/transpiled/directives/auth/Anyone.d.ts +1 -0
- package/transpiled/directives/auth/Anyone.js +3 -0
- package/transpiled/directives/auth/Anyone.js.map +1 -1
- package/transpiled/directives/auth/Authorization.d.ts +28 -2
- package/transpiled/directives/auth/Authorization.js +87 -19
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/auth/Delegate.d.ts +5 -0
- package/transpiled/directives/auth/Delegate.js +10 -0
- package/transpiled/directives/auth/Delegate.js.map +1 -1
- package/transpiled/directives/auth/Echo.d.ts +2 -0
- package/transpiled/directives/auth/Echo.js +4 -0
- package/transpiled/directives/auth/Echo.js.map +1 -1
- package/transpiled/directives/auth/Incept.d.ts +1 -1
- package/transpiled/directives/auth/Incept.js +13 -8
- package/transpiled/directives/auth/Incept.js.map +1 -1
- package/transpiled/directives/auth/Role.d.ts +2 -0
- package/transpiled/directives/auth/Role.js +9 -0
- package/transpiled/directives/auth/Role.js.map +1 -1
- package/transpiled/directives/auth/Rule.d.ts +5 -0
- package/transpiled/directives/auth/Rule.js +20 -0
- package/transpiled/directives/auth/Rule.js.map +1 -1
- package/transpiled/directives/auth/Scheme.d.ts +2 -0
- package/transpiled/directives/auth/Scheme.js +4 -0
- package/transpiled/directives/auth/Scheme.js.map +1 -1
- package/transpiled/directives/auth/schemes.d.ts +16 -4
- package/transpiled/directives/auth/schemes.js +20 -8
- package/transpiled/directives/auth/schemes.js.map +1 -1
- package/transpiled/directives/auth/types.d.ts +20 -2
- package/transpiled/directives/cache/Cache.d.ts +1 -1
- package/transpiled/directives/cache/Cache.js +10 -3
- package/transpiled/directives/cache/Cache.js.map +1 -1
- package/transpiled/directives/cache/Control.js +0 -2
- package/transpiled/directives/cache/Control.js.map +1 -1
- package/transpiled/directives/dev/Development.d.ts +1 -1
- package/transpiled/directives/dev/Development.js +1 -1
- package/transpiled/directives/dev/Development.js.map +1 -1
- package/transpiled/directives/flow/Flow.d.ts +1 -1
- package/transpiled/directives/flow/Flow.js +1 -1
- package/transpiled/directives/flow/Flow.js.map +1 -1
- package/transpiled/directives/index.js +4 -2
- package/transpiled/directives/index.js.map +1 -1
- package/transpiled/directives/io/Directive.d.ts +1 -1
- package/transpiled/directives/io/IO.d.ts +7 -1
- package/transpiled/directives/io/IO.js +25 -3
- package/transpiled/directives/io/IO.js.map +1 -1
- package/transpiled/directives/io/Input.d.ts +3 -2
- package/transpiled/directives/io/Input.js +2 -1
- package/transpiled/directives/io/Input.js.map +1 -1
- package/transpiled/directives/io/Output.d.ts +5 -3
- package/transpiled/directives/io/Output.js +8 -3
- package/transpiled/directives/io/Output.js.map +1 -1
- package/transpiled/directives/io/Status.d.ts +19 -0
- package/transpiled/directives/io/Status.js +34 -0
- package/transpiled/directives/io/Status.js.map +1 -0
- package/transpiled/directives/io/Throttle.d.ts +1 -1
- package/transpiled/directives/io/Throttle.js +1 -1
- package/transpiled/directives/io/Throttle.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Quotas.d.ts +1 -1
- package/transpiled/directives/io/lib/throttle/Quotas.js +2 -2
- package/transpiled/directives/io/lib/throttle/Quotas.js.map +1 -1
- package/transpiled/directives/map/Headers.d.ts +2 -0
- package/transpiled/directives/map/Headers.js +8 -0
- package/transpiled/directives/map/Headers.js.map +1 -1
- package/transpiled/directives/map/Map.d.ts +3 -1
- package/transpiled/directives/map/Map.js +7 -1
- package/transpiled/directives/map/Map.js.map +1 -1
- package/transpiled/directives/map/Mapping.d.ts +13 -0
- package/transpiled/directives/map/Mapping.js +20 -0
- package/transpiled/directives/map/Mapping.js.map +1 -1
- package/transpiled/directives/map/Segments.d.ts +3 -0
- package/transpiled/directives/map/Segments.js +9 -0
- package/transpiled/directives/map/Segments.js.map +1 -1
- package/transpiled/directives/mcp/MCP.d.ts +15 -0
- package/transpiled/directives/mcp/MCP.js +23 -0
- package/transpiled/directives/mcp/MCP.js.map +1 -0
- package/transpiled/directives/mcp/Tool.d.ts +13 -0
- package/transpiled/directives/mcp/Tool.js +23 -0
- package/transpiled/directives/mcp/Tool.js.map +1 -0
- package/transpiled/directives/mcp/index.d.ts +4 -0
- package/transpiled/directives/mcp/index.js +4 -0
- package/transpiled/directives/mcp/index.js.map +1 -0
- package/transpiled/directives/oauth/Discovery.d.ts +23 -0
- package/transpiled/directives/oauth/Discovery.js +55 -0
- package/transpiled/directives/oauth/Discovery.js.map +1 -0
- package/transpiled/directives/oauth/documents.d.ts +9 -0
- package/transpiled/directives/oauth/documents.js +82 -0
- package/transpiled/directives/oauth/documents.js.map +1 -0
- package/transpiled/directives/oauth/index.d.ts +2 -0
- package/transpiled/directives/oauth/index.js +3 -0
- package/transpiled/directives/oauth/index.js.map +1 -0
- package/transpiled/directives/octets/Octets.d.ts +1 -1
- package/transpiled/directives/octets/Octets.js +1 -1
- package/transpiled/directives/octets/Octets.js.map +1 -1
- package/transpiled/directives/require/Directive.d.ts +1 -1
- package/transpiled/directives/require/Headers.d.ts +1 -1
- package/transpiled/directives/require/Headers.js +1 -1
- package/transpiled/directives/require/Headers.js.map +1 -1
- package/transpiled/directives/require/Require.d.ts +1 -1
- package/transpiled/directives/require/Require.js +2 -2
- package/transpiled/directives/require/Require.js.map +1 -1
- package/transpiled/index.d.ts +2 -0
- package/transpiled/schemas.d.ts +2 -0
- package/transpiled/schemas.js +1 -0
- package/transpiled/schemas.js.map +1 -1
- package/transpiled/userland.d.ts +39 -0
- package/transpiled/userland.js +7 -0
- package/transpiled/userland.js.map +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/components/identity.bans/operations/transit.d.ts +0 -14
- package/components/identity.bans/operations/transit.js.map +0 -1
- package/components/identity.basic/operations/add.d.ts +0 -3
- package/components/identity.basic/operations/add.js.map +0 -1
- package/components/identity.basic/operations/authenticate.d.ts +0 -13
- package/components/identity.basic/operations/authenticate.js.map +0 -1
- package/components/identity.basic/operations/check.d.ts +0 -7
- package/components/identity.basic/operations/check.js.map +0 -1
- package/components/identity.basic/operations/delete.d.ts +0 -7
- package/components/identity.basic/operations/delete.js.map +0 -1
- package/components/identity.basic/operations/incept.d.ts +0 -12
- package/components/identity.basic/operations/incept.js.map +0 -1
- package/components/identity.basic/operations/info.d.ts +0 -10
- package/components/identity.basic/operations/info.js.map +0 -1
- package/components/identity.basic/operations/lib/credentials.d.ts +0 -5
- package/components/identity.basic/operations/lib/credentials.js.map +0 -1
- package/components/identity.basic/operations/transit.d.ts +0 -14
- package/components/identity.basic/operations/transit.js.map +0 -1
- package/components/identity.basic/operations/types.d.ts +0 -61
- package/components/identity.basic/operations/types.js +0 -2
- package/components/identity.basic/operations/types.js.map +0 -1
- package/components/identity.basic/source/types.ts +0 -69
- package/components/identity.credentials/operations/list.d.ts +0 -51
- package/components/identity.credentials/operations/list.js.map +0 -1
- package/components/identity.federation/operations/authenticate.d.ts +0 -16
- package/components/identity.federation/operations/authenticate.js.map +0 -1
- package/components/identity.federation/operations/create.d.ts +0 -8
- package/components/identity.federation/operations/create.js.map +0 -1
- package/components/identity.federation/operations/decode.d.ts +0 -3
- package/components/identity.federation/operations/decode.js.map +0 -1
- package/components/identity.federation/operations/delete.d.ts +0 -8
- package/components/identity.federation/operations/delete.js.map +0 -1
- package/components/identity.federation/operations/incept.d.ts +0 -11
- package/components/identity.federation/operations/incept.js.map +0 -1
- package/components/identity.federation/operations/lib/Configuration.d.ts +0 -39
- package/components/identity.federation/operations/lib/Configuration.js.map +0 -1
- package/components/identity.federation/operations/lib/Ctx.d.ts +0 -8
- package/components/identity.federation/operations/lib/Ctx.js.map +0 -1
- package/components/identity.federation/operations/lib/Payload.d.ts +0 -5
- package/components/identity.federation/operations/lib/Payload.js.map +0 -1
- package/components/identity.federation/operations/lib/decode.d.ts +0 -3
- package/components/identity.federation/operations/lib/decode.js.map +0 -1
- package/components/identity.federation/operations/lib/discovery.d.ts +0 -5
- package/components/identity.federation/operations/lib/discovery.js.map +0 -1
- package/components/identity.federation/operations/lib/errors.d.ts +0 -70
- package/components/identity.federation/operations/lib/errors.js.map +0 -1
- package/components/identity.federation/operations/lib/exchange.d.ts +0 -3
- package/components/identity.federation/operations/lib/exchange.js.map +0 -1
- package/components/identity.federation/operations/lib/index.d.ts +0 -4
- package/components/identity.federation/operations/lib/index.js.map +0 -1
- package/components/identity.federation/operations/lib/jose.d.ts +0 -1
- package/components/identity.federation/operations/lib/jose.js.map +0 -1
- package/components/identity.federation/operations/lib/resolve.d.ts +0 -3
- package/components/identity.federation/operations/lib/resolve.js.map +0 -1
- package/components/identity.federation/operations/list.d.ts +0 -7
- package/components/identity.federation/operations/list.js.map +0 -1
- package/components/identity.federation/operations/types/Scheme.d.ts +0 -1
- package/components/identity.federation/operations/types/Scheme.js.map +0 -1
- package/components/identity.federation/operations/types/configuration.d.ts +0 -22
- package/components/identity.federation/operations/types/configuration.js.map +0 -1
- package/components/identity.federation/operations/types/context.d.ts +0 -35
- package/components/identity.federation/operations/types/context.js.map +0 -1
- package/components/identity.federation/operations/types/entity.d.ts +0 -9
- package/components/identity.federation/operations/types/entity.js.map +0 -1
- package/components/identity.federation/operations/types/index.d.ts +0 -4
- package/components/identity.federation/operations/types/index.js.map +0 -1
- package/components/identity.keys/operations/create.d.ts +0 -22
- package/components/identity.keys/operations/create.js.map +0 -1
- package/components/identity.keys/operations/disable.d.ts +0 -5
- package/components/identity.keys/operations/disable.js.map +0 -1
- package/components/identity.keys/operations/revoke.d.ts +0 -15
- package/components/identity.keys/operations/revoke.js.map +0 -1
- package/components/identity.otp/operations/authenticate.d.ts +0 -12
- package/components/identity.otp/operations/authenticate.js.map +0 -1
- package/components/identity.otp/operations/issue.d.ts +0 -12
- package/components/identity.otp/operations/issue.js.map +0 -1
- package/components/identity.otp/operations/lib/Context.d.ts +0 -14
- package/components/identity.otp/operations/lib/Context.js.map +0 -1
- package/components/identity.otp/operations/lib/Entity.d.ts +0 -6
- package/components/identity.otp/operations/lib/Entity.js.map +0 -1
- package/components/identity.otp/operations/lib/index.d.ts +0 -1
- package/components/identity.otp/operations/lib/index.js.map +0 -1
- package/components/identity.passkeys/operations/authenticate.d.ts +0 -16
- package/components/identity.passkeys/operations/authenticate.js.map +0 -1
- package/components/identity.passkeys/operations/challenge.d.ts +0 -35
- package/components/identity.passkeys/operations/challenge.js.map +0 -1
- package/components/identity.passkeys/operations/create.d.ts +0 -19
- package/components/identity.passkeys/operations/create.js.map +0 -1
- package/components/identity.passkeys/operations/delete.d.ts +0 -8
- package/components/identity.passkeys/operations/delete.js.map +0 -1
- package/components/identity.passkeys/operations/lib/const.d.ts +0 -1
- package/components/identity.passkeys/operations/lib/const.js.map +0 -1
- package/components/identity.passkeys/operations/list.d.ts +0 -7
- package/components/identity.passkeys/operations/list.js.map +0 -1
- package/components/identity.passkeys/operations/types/Configuration.d.ts +0 -6
- package/components/identity.passkeys/operations/types/Configuration.js.map +0 -1
- package/components/identity.passkeys/operations/types/Context.d.ts +0 -15
- package/components/identity.passkeys/operations/types/Context.js.map +0 -1
- package/components/identity.passkeys/operations/types/Passkey.d.ts +0 -12
- package/components/identity.passkeys/operations/types/Passkey.js.map +0 -1
- package/components/identity.passkeys/operations/types/index.d.ts +0 -2
- package/components/identity.passkeys/operations/types/index.js.map +0 -1
- package/components/identity.passkeys/operations/use.d.ts +0 -16
- package/components/identity.passkeys/operations/use.js.map +0 -1
- package/components/identity.roles/operations/grant.d.ts +0 -10
- package/components/identity.roles/operations/grant.js.map +0 -1
- package/components/identity.roles/operations/lib/Entity.d.ts +0 -5
- package/components/identity.roles/operations/lib/Entity.js.map +0 -1
- package/components/identity.roles/operations/list.d.ts +0 -2
- package/components/identity.roles/operations/list.js.map +0 -1
- package/components/identity.roles/operations/principal.d.ts +0 -13
- package/components/identity.roles/operations/principal.js.map +0 -1
- package/components/identity.tokens/operations/authenticate.d.ts +0 -9
- package/components/identity.tokens/operations/authenticate.js.map +0 -1
- package/components/identity.tokens/operations/decrypt.d.ts +0 -15
- package/components/identity.tokens/operations/decrypt.js.map +0 -1
- package/components/identity.tokens/operations/encrypt.d.ts +0 -8
- package/components/identity.tokens/operations/encrypt.js.map +0 -1
- package/components/identity.tokens/operations/issue.d.ts +0 -24
- package/components/identity.tokens/operations/issue.js.map +0 -1
- package/components/identity.tokens/operations/lib/index.d.ts +0 -3
- package/components/identity.tokens/operations/lib/index.js.map +0 -1
- package/components/identity.tokens/operations/lib/jose.d.ts +0 -1
- package/components/identity.tokens/operations/lib/jose.js.map +0 -1
- package/components/identity.tokens/operations/lib/key.d.ts +0 -1
- package/components/identity.tokens/operations/lib/key.js.map +0 -1
- package/components/identity.tokens/operations/lib/pad.d.ts +0 -1
- package/components/identity.tokens/operations/lib/pad.js.map +0 -1
- package/components/identity.tokens/operations/lib/types.d.ts +0 -88
- package/components/identity.tokens/operations/lib/types.js +0 -2
- package/components/identity.tokens/operations/lib/types.js.map +0 -1
- package/components/identity.tokens/operations/revoke.d.ts +0 -2
- package/components/identity.tokens/operations/revoke.js.map +0 -1
- package/components/identity.tokens/source/lib/types.ts +0 -102
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ERR_UNKNOWN_CLIENT, permits, read } from './lib/index.js'
|
|
2
|
+
import type { Maybe } from '@toa.io/core'
|
|
3
|
+
import type { Operation } from '@toa.io/bridges.node'
|
|
4
|
+
import type { Client, Context } from './lib/index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What a client is, whichever way it came to be known: a `client_id` that is an https URL
|
|
8
|
+
* publishes its own metadata, and anything else was registered here. The caller is not told
|
|
9
|
+
* which, and neither mechanism is visible past this operation.
|
|
10
|
+
*
|
|
11
|
+
* Whether a redirect is one the client may receive a code at is answered here too. The rule
|
|
12
|
+
* is this component's — nothing else knows a loopback port is ignored — and asking for it
|
|
13
|
+
* with the client saves a second call on the path a person is waiting on.
|
|
14
|
+
*/
|
|
15
|
+
export class Computation implements Operation {
|
|
16
|
+
private context!: Context
|
|
17
|
+
|
|
18
|
+
public mount (context: Context): void {
|
|
19
|
+
this.context = context
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async execute (input: Input): Promise<Maybe<Output>> {
|
|
23
|
+
const client = input.id.startsWith(HTTPS)
|
|
24
|
+
? await read(input.id, this.context)
|
|
25
|
+
: await this.registered(input)
|
|
26
|
+
|
|
27
|
+
if (client instanceof Error)
|
|
28
|
+
return client
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
...client,
|
|
32
|
+
...(input.redirect === undefined
|
|
33
|
+
? {}
|
|
34
|
+
: { permitted: permits(client.redirect_uris, input.redirect) })
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private async registered (input: Input): Promise<Client | Error> {
|
|
39
|
+
const entity = await this.context.local.observe({ query: { id: input.id } })
|
|
40
|
+
|
|
41
|
+
// credentials are scoped to an authority, and so is what may act for them
|
|
42
|
+
if (entity === null || entity.authority !== input.authority)
|
|
43
|
+
return ERR_UNKNOWN_CLIENT
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
client_id: entity.id,
|
|
47
|
+
client_name: entity.name,
|
|
48
|
+
client_uri: entity.uri,
|
|
49
|
+
logo_uri: entity.logo,
|
|
50
|
+
redirect_uris: entity.uris
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const HTTPS = 'https://'
|
|
56
|
+
|
|
57
|
+
interface Input {
|
|
58
|
+
authority: string
|
|
59
|
+
id: string
|
|
60
|
+
redirect?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface Output extends Client {
|
|
64
|
+
permitted?: boolean
|
|
65
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Call, Observation, Transition } from '@toa.io/core'
|
|
2
|
+
import type { Stash } from '@toa.io/extensions.stash'
|
|
3
|
+
import type { Logs } from '@toa.io/extensions.telemetry'
|
|
4
|
+
import type { Entity } from './Entity.js'
|
|
5
|
+
|
|
6
|
+
export interface Context {
|
|
7
|
+
stash: Stash
|
|
8
|
+
fetch: Fetch
|
|
9
|
+
logs: Logs
|
|
10
|
+
local: {
|
|
11
|
+
observe: Observation<Entity>
|
|
12
|
+
transit: Call<Entity, TransitInput>
|
|
13
|
+
}
|
|
14
|
+
configuration: Configuration
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>
|
|
18
|
+
|
|
19
|
+
export type TransitInput = Omit<Entity, 'id' | '_created'>
|
|
20
|
+
|
|
21
|
+
export interface Configuration {
|
|
22
|
+
/** Origins whose Client ID Metadata Documents may be read. Empty admits nobody. */
|
|
23
|
+
readonly trust: string[]
|
|
24
|
+
|
|
25
|
+
/** Seconds a document is held before it is read again. */
|
|
26
|
+
readonly lifetime: number
|
|
27
|
+
|
|
28
|
+
/** Seconds a registration nothing has used is kept. */
|
|
29
|
+
readonly ttl: number
|
|
30
|
+
|
|
31
|
+
/** Bytes of a document that are read. */
|
|
32
|
+
readonly size: number
|
|
33
|
+
|
|
34
|
+
/** Milliseconds a document has to arrive. */
|
|
35
|
+
readonly timeout: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type { Transition }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface Entity {
|
|
2
|
+
id: string
|
|
3
|
+
authority: string
|
|
4
|
+
name?: string
|
|
5
|
+
uris: string[]
|
|
6
|
+
uri?: string
|
|
7
|
+
logo?: string
|
|
8
|
+
scope?: string
|
|
9
|
+
expires?: number
|
|
10
|
+
_created?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** What a client says about itself, in the shape both mechanisms carry it. */
|
|
14
|
+
export interface Metadata {
|
|
15
|
+
client_name?: string
|
|
16
|
+
client_uri?: string
|
|
17
|
+
logo_uri?: string
|
|
18
|
+
redirect_uris: string[]
|
|
19
|
+
scope?: string
|
|
20
|
+
token_endpoint_auth_method?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A client, however it came to be known. */
|
|
24
|
+
export interface Client {
|
|
25
|
+
client_id: string
|
|
26
|
+
client_name?: string
|
|
27
|
+
client_uri?: string
|
|
28
|
+
logo_uri?: string
|
|
29
|
+
redirect_uris: string[]
|
|
30
|
+
issued?: number
|
|
31
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { identify } from './canonical.js'
|
|
5
|
+
import type { Metadata } from './Entity.js'
|
|
6
|
+
|
|
7
|
+
const metadata: Metadata = {
|
|
8
|
+
client_name: 'Claude',
|
|
9
|
+
redirect_uris: ['https://claude.ai/api/mcp/auth_callback', 'http://localhost/callback']
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
it('should be the id the entity accepts', () => {
|
|
13
|
+
assert.match(identify('nex', metadata), /^[a-f0-9]{32}$/)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should give the same client the same id, however many times it registers', () => {
|
|
17
|
+
assert.equal(identify('nex', metadata), identify('nex', { ...metadata }))
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should not depend on the order the redirects were written in', () => {
|
|
21
|
+
const reordered = { ...metadata, redirect_uris: [...metadata.redirect_uris].reverse() }
|
|
22
|
+
|
|
23
|
+
assert.equal(identify('nex', metadata), identify('nex', reordered))
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should not depend on what is not honoured', () => {
|
|
27
|
+
const noisy = { ...metadata, token_endpoint_auth_method: 'none' } as Metadata
|
|
28
|
+
|
|
29
|
+
assert.equal(identify('nex', metadata), identify('nex', noisy))
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should separate authorities, which credentials are scoped to', () => {
|
|
33
|
+
assert.notEqual(identify('nex', metadata), identify('other', metadata))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should separate what differs', () => {
|
|
37
|
+
for (const changed of [
|
|
38
|
+
{ ...metadata, client_name: 'Not Claude' },
|
|
39
|
+
{ ...metadata, redirect_uris: ['https://claude.ai/api/mcp/auth_callback'] },
|
|
40
|
+
{ ...metadata, scope: 'app:notes' }
|
|
41
|
+
])
|
|
42
|
+
assert.notEqual(identify('nex', metadata), identify('nex', changed))
|
|
43
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import type { Metadata } from './Entity.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The id of a registration: a hash of what it says, so identical metadata is one client
|
|
6
|
+
* however many times it arrives. A client cannot remember that it registered here, and
|
|
7
|
+
* registers again on every fresh connection; without this the registry only grows.
|
|
8
|
+
*
|
|
9
|
+
* Only what is honoured is hashed. A field this component ignores would otherwise mint a
|
|
10
|
+
* client per release of whoever sends it, and the authority is in there because credentials
|
|
11
|
+
* are scoped to one.
|
|
12
|
+
*/
|
|
13
|
+
export function identify (authority: string, metadata: Metadata): string {
|
|
14
|
+
const canonical = JSON.stringify([
|
|
15
|
+
authority,
|
|
16
|
+
metadata.client_name ?? null,
|
|
17
|
+
metadata.client_uri ?? null,
|
|
18
|
+
metadata.logo_uri ?? null,
|
|
19
|
+
metadata.scope ?? null,
|
|
20
|
+
[...metadata.redirect_uris].sort()
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
return createHash('sha256').update(canonical).digest('hex').slice(0, LENGTH)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** What the entity prototype accepts as an id. */
|
|
27
|
+
const LENGTH = 32
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { it, beforeEach, mock } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { read } from './cimd.js'
|
|
5
|
+
import type { Context } from './Context.js'
|
|
6
|
+
|
|
7
|
+
const ID = 'https://claude.ai/oauth/claude-code-client-metadata'
|
|
8
|
+
|
|
9
|
+
const document = {
|
|
10
|
+
client_id: ID,
|
|
11
|
+
client_name: 'Claude Code',
|
|
12
|
+
redirect_uris: ['http://localhost/callback', 'http://127.0.0.1/callback']
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let context: Context
|
|
16
|
+
let fetched: string[]
|
|
17
|
+
let stored: Record<string, string>
|
|
18
|
+
|
|
19
|
+
function respond (body: unknown, ok = true): Response {
|
|
20
|
+
return {
|
|
21
|
+
ok,
|
|
22
|
+
text: async () => JSON.stringify(body)
|
|
23
|
+
} as unknown as Response
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
fetched = []
|
|
28
|
+
stored = {}
|
|
29
|
+
|
|
30
|
+
context = {
|
|
31
|
+
configuration: {
|
|
32
|
+
trust: ['https://claude.ai'],
|
|
33
|
+
lifetime: 3600,
|
|
34
|
+
ttl: 2592000,
|
|
35
|
+
size: 16384,
|
|
36
|
+
timeout: 3000
|
|
37
|
+
},
|
|
38
|
+
stash: {
|
|
39
|
+
get: mock.fn(async (key: string) => stored[key] ?? null),
|
|
40
|
+
set: mock.fn(async (key: string, value: string) => {
|
|
41
|
+
stored[key] = value
|
|
42
|
+
})
|
|
43
|
+
},
|
|
44
|
+
fetch: mock.fn(async (input: string) => {
|
|
45
|
+
fetched.push(input)
|
|
46
|
+
|
|
47
|
+
return respond(document)
|
|
48
|
+
}),
|
|
49
|
+
logs: { debug: mock.fn() }
|
|
50
|
+
} as unknown as Context
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should read what the client publishes', async () => {
|
|
54
|
+
const client = await read(ID, context)
|
|
55
|
+
|
|
56
|
+
assert.deepEqual(client, {
|
|
57
|
+
client_id: ID,
|
|
58
|
+
client_name: 'Claude Code',
|
|
59
|
+
client_uri: undefined,
|
|
60
|
+
logo_uri: undefined,
|
|
61
|
+
redirect_uris: document.redirect_uris
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should hold it, rather than read it per request', async () => {
|
|
66
|
+
await read(ID, context)
|
|
67
|
+
await read(ID, context)
|
|
68
|
+
|
|
69
|
+
assert.equal(fetched.length, 1)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should not reach an origin the configuration does not name', async () => {
|
|
73
|
+
const client = await read('https://evil.example/metadata', context)
|
|
74
|
+
|
|
75
|
+
assert.ok(client instanceof Error)
|
|
76
|
+
assert.equal(fetched.length, 0, 'an untrusted origin must not be fetched at all')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should not reach a host that merely starts like a trusted one', async () => {
|
|
80
|
+
const client = await read('https://claude.ai.evil.example/metadata', context)
|
|
81
|
+
|
|
82
|
+
assert.ok(client instanceof Error)
|
|
83
|
+
assert.equal(fetched.length, 0)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should refuse anything but https', async () => {
|
|
87
|
+
for (const id of ['http://claude.ai/metadata', 'file:///etc/passwd', 'not a url']) {
|
|
88
|
+
const client = await read(id, context)
|
|
89
|
+
|
|
90
|
+
assert.ok(client instanceof Error, id)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
assert.equal(fetched.length, 0)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should refuse a document that names another client', async () => {
|
|
97
|
+
context.fetch = mock.fn(async () => respond({ ...document, client_id: 'https://claude.ai/other' }))
|
|
98
|
+
|
|
99
|
+
assert.ok(await read(ID, context) instanceof Error)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should refuse a document with no redirect to send a code to', async () => {
|
|
103
|
+
context.fetch = mock.fn(async () => respond({ client_id: ID, redirect_uris: [] }))
|
|
104
|
+
|
|
105
|
+
assert.ok(await read(ID, context) instanceof Error)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should remember that a document did not answer', async () => {
|
|
109
|
+
let calls = 0
|
|
110
|
+
|
|
111
|
+
context.fetch = mock.fn(async () => {
|
|
112
|
+
calls++
|
|
113
|
+
|
|
114
|
+
return respond({}, false)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
assert.ok(await read(ID, context) instanceof Error)
|
|
118
|
+
assert.ok(await read(ID, context) instanceof Error)
|
|
119
|
+
assert.equal(calls, 1, 'a document that failed must not be refetched per request')
|
|
120
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { ERR_UNKNOWN_CLIENT } from './errors.js'
|
|
3
|
+
import type { Context } from './Context.js'
|
|
4
|
+
import type { Client, Metadata } from './Entity.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A Client ID Metadata Document: the client publishes what it is at an https URL, and that
|
|
8
|
+
* URL is its `client_id`. Control of the origin is what proves the identity, so nothing is
|
|
9
|
+
* registered and nothing is written — the document is read and held for a while.
|
|
10
|
+
*/
|
|
11
|
+
export async function read (id: string, context: Context): Promise<Client | Error> {
|
|
12
|
+
// before anything is fetched: the id is the caller's to choose, and this runs inside
|
|
13
|
+
// the cluster. An origin the configuration does not name is not reached at all.
|
|
14
|
+
if (!trusted(id, context.configuration.trust))
|
|
15
|
+
return ERR_UNKNOWN_CLIENT
|
|
16
|
+
|
|
17
|
+
const key = CACHE + createHash('sha256').update(id).digest('hex')
|
|
18
|
+
const cached = await context.stash.get(key)
|
|
19
|
+
|
|
20
|
+
if (cached !== null)
|
|
21
|
+
return cached === MISS ? ERR_UNKNOWN_CLIENT : JSON.parse(cached) as Client
|
|
22
|
+
|
|
23
|
+
const client = await fetch(id, context)
|
|
24
|
+
|
|
25
|
+
// a document that does not answer is remembered too, or a bad id is fetched per request
|
|
26
|
+
await context.stash.set(key, client instanceof Error ? MISS : JSON.stringify(client),
|
|
27
|
+
'EX', context.configuration.lifetime)
|
|
28
|
+
|
|
29
|
+
return client
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* An https URL whose origin the configuration names. Compared by origin so that a path on a
|
|
34
|
+
* trusted host cannot be escaped by one that merely starts the same way.
|
|
35
|
+
*/
|
|
36
|
+
function trusted (id: string, trust: string[]): boolean {
|
|
37
|
+
let url: URL
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
url = new URL(id)
|
|
41
|
+
} catch {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (url.protocol !== 'https:')
|
|
46
|
+
return false
|
|
47
|
+
|
|
48
|
+
return trust.some((origin) => {
|
|
49
|
+
try {
|
|
50
|
+
return new URL(origin).origin === url.origin
|
|
51
|
+
} catch {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function fetch (id: string, context: Context): Promise<Client | Error> {
|
|
58
|
+
const { size, timeout } = context.configuration
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const response = await context.fetch(id, {
|
|
62
|
+
redirect: 'error', // a redirect leaves the origin that was trusted
|
|
63
|
+
signal: AbortSignal.timeout(timeout),
|
|
64
|
+
headers: { accept: 'application/json' }
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
if (!response.ok)
|
|
68
|
+
return ERR_UNKNOWN_CLIENT
|
|
69
|
+
|
|
70
|
+
const text = await response.text()
|
|
71
|
+
|
|
72
|
+
if (text.length > size)
|
|
73
|
+
return ERR_UNKNOWN_CLIENT
|
|
74
|
+
|
|
75
|
+
return validate(id, JSON.parse(text) as Metadata & { client_id?: string })
|
|
76
|
+
} catch (error: unknown) {
|
|
77
|
+
context.logs.debug('Client metadata document is unreadable', {
|
|
78
|
+
client: id,
|
|
79
|
+
error: error?.toString?.()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return ERR_UNKNOWN_CLIENT
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function validate (id: string, metadata: Metadata & { client_id?: string }): Client | Error {
|
|
87
|
+
// the document names itself, so one client's cannot claim another's identity
|
|
88
|
+
if (metadata.client_id !== id)
|
|
89
|
+
return ERR_UNKNOWN_CLIENT
|
|
90
|
+
|
|
91
|
+
if (!Array.isArray(metadata.redirect_uris) || metadata.redirect_uris.length === 0 ||
|
|
92
|
+
metadata.redirect_uris.some((uri) => typeof uri !== 'string'))
|
|
93
|
+
return ERR_UNKNOWN_CLIENT
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
client_id: id,
|
|
97
|
+
client_name: metadata.client_name,
|
|
98
|
+
client_uri: metadata.client_uri,
|
|
99
|
+
logo_uri: metadata.logo_uri,
|
|
100
|
+
redirect_uris: metadata.redirect_uris
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const CACHE = 'cimd:'
|
|
105
|
+
|
|
106
|
+
/** Held in place of a document that could not be read, so a bad id is not refetched. */
|
|
107
|
+
const MISS = ''
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { permits } from './redirect.js'
|
|
5
|
+
|
|
6
|
+
const hosted = ['https://claude.ai/api/mcp/auth_callback']
|
|
7
|
+
const native = ['http://localhost/callback', 'http://127.0.0.1/callback']
|
|
8
|
+
|
|
9
|
+
it('should permit an exact match', () => {
|
|
10
|
+
assert.equal(permits(hosted, 'https://claude.ai/api/mcp/auth_callback'), true)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should refuse anything else on a registered host', () => {
|
|
14
|
+
assert.equal(permits(hosted, 'https://claude.ai/api/mcp/auth_callback/x'), false)
|
|
15
|
+
assert.equal(permits(hosted, 'https://claude.ai/'), false)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should refuse a host that merely starts the same way', () => {
|
|
19
|
+
assert.equal(permits(hosted, 'https://claude.ai.evil.example/api/mcp/auth_callback'), false)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should ignore the port of a loopback address', () => {
|
|
23
|
+
for (const redirect of [
|
|
24
|
+
'http://localhost:3118/callback',
|
|
25
|
+
'http://localhost:51234/callback',
|
|
26
|
+
'http://127.0.0.1:3118/callback'
|
|
27
|
+
])
|
|
28
|
+
assert.equal(permits(native, redirect), true)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should not ignore the path of a loopback address', () => {
|
|
32
|
+
assert.equal(permits(native, 'http://localhost:3118/stolen'), false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should not treat a remote address as loopback', () => {
|
|
36
|
+
assert.equal(permits(['http://example.com/callback'], 'http://example.com:8080/callback'), false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should not cross schemes', () => {
|
|
40
|
+
assert.equal(permits(native, 'https://localhost:3118/callback'), false)
|
|
41
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a client may receive a code at this address.
|
|
3
|
+
*
|
|
4
|
+
* Exact string comparison, which RFC 6749 §3.1.2.3 requires, except for a loopback
|
|
5
|
+
* address: a native client binds whatever port is free when it starts, so RFC 8252 §7.3
|
|
6
|
+
* has the port ignored there. Claude Code registers `http://localhost/callback` and
|
|
7
|
+
* `http://127.0.0.1/callback` and comes back on an ephemeral port; comparing those
|
|
8
|
+
* literally would refuse every one of them.
|
|
9
|
+
*/
|
|
10
|
+
export function permits (registered: string[], redirect: string): boolean {
|
|
11
|
+
return registered.some((uri) => uri === redirect || loopback(uri, redirect))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function loopback (registered: string, redirect: string): boolean {
|
|
15
|
+
const a = parse(registered)
|
|
16
|
+
const b = parse(redirect)
|
|
17
|
+
|
|
18
|
+
if (a === null || b === null)
|
|
19
|
+
return false
|
|
20
|
+
|
|
21
|
+
return a.protocol === 'http:' && b.protocol === 'http:' &&
|
|
22
|
+
LOOPBACK.has(a.hostname) && a.hostname === b.hostname &&
|
|
23
|
+
a.pathname === b.pathname && a.search === b.search
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parse (value: string): URL | null {
|
|
27
|
+
try {
|
|
28
|
+
return new URL(value)
|
|
29
|
+
} catch {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** `localhost` resolves through the host's own configuration, which RFC 8252 §8.3 warns of. */
|
|
35
|
+
const LOOPBACK = new Set(['127.0.0.1', '[::1]', 'localhost'])
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { identify } from './lib/index.js'
|
|
2
|
+
import type { Maybe } from '@toa.io/core'
|
|
3
|
+
import type { Operation } from '@toa.io/bridges.node'
|
|
4
|
+
import type { Context, Metadata } from './lib/index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Dynamic Client Registration, RFC 7591: the client sends what it is and is given an
|
|
8
|
+
* identifier for it. Unauthenticated, as the specification has it — a registration grants
|
|
9
|
+
* nothing on its own, and stays inert until a user consents to the client on a page that
|
|
10
|
+
* shows them its name and where it would send them.
|
|
11
|
+
*
|
|
12
|
+
* The identifier is a hash of the metadata, so registering the same thing twice is the
|
|
13
|
+
* same client rather than another row. A client cannot remember it registered here.
|
|
14
|
+
*/
|
|
15
|
+
export class Effect implements Operation {
|
|
16
|
+
private transit!: Context['local']['transit']
|
|
17
|
+
private ttl!: number
|
|
18
|
+
|
|
19
|
+
public mount (context: Context): void {
|
|
20
|
+
this.transit = context.local.transit
|
|
21
|
+
this.ttl = context.configuration.ttl
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async execute (input: Input): Promise<Maybe<Output>> {
|
|
25
|
+
const { authority } = input
|
|
26
|
+
|
|
27
|
+
if (input.token_endpoint_auth_method !== undefined &&
|
|
28
|
+
input.token_endpoint_auth_method !== 'none')
|
|
29
|
+
return invalid('invalid_client_metadata',
|
|
30
|
+
'Only public clients are supported: token_endpoint_auth_method must be `none`')
|
|
31
|
+
|
|
32
|
+
const uris = input.redirect_uris.map(normalize)
|
|
33
|
+
|
|
34
|
+
for (const uri of uris)
|
|
35
|
+
if (uri instanceof Error)
|
|
36
|
+
return invalid('invalid_redirect_uri', uri.message)
|
|
37
|
+
|
|
38
|
+
const metadata: Metadata = {
|
|
39
|
+
client_name: input.client_name,
|
|
40
|
+
client_uri: input.client_uri,
|
|
41
|
+
logo_uri: input.logo_uri,
|
|
42
|
+
scope: input.scope,
|
|
43
|
+
redirect_uris: uris as string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const id = identify(authority, metadata)
|
|
47
|
+
|
|
48
|
+
const entity = await this.transit({
|
|
49
|
+
query: { id },
|
|
50
|
+
input: {
|
|
51
|
+
authority,
|
|
52
|
+
name: metadata.client_name,
|
|
53
|
+
uri: metadata.client_uri,
|
|
54
|
+
logo: metadata.logo_uri,
|
|
55
|
+
scope: metadata.scope,
|
|
56
|
+
uris: metadata.redirect_uris,
|
|
57
|
+
expires: Date.now() + this.ttl * 1000
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
client_id: id,
|
|
63
|
+
// of the record, not of this request: registering again answers what it answered
|
|
64
|
+
client_id_issued_at: Math.floor((entity._created ?? Date.now()) / 1000),
|
|
65
|
+
client_name: entity.name,
|
|
66
|
+
client_uri: entity.uri,
|
|
67
|
+
logo_uri: entity.logo,
|
|
68
|
+
redirect_uris: entity.uris,
|
|
69
|
+
grant_types: ['authorization_code'],
|
|
70
|
+
response_types: ['code'],
|
|
71
|
+
token_endpoint_auth_method: 'none'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Absolute, no fragment, and either https or a loopback address (RFC 8252 §7.3). */
|
|
77
|
+
function normalize (value: string): string | Error {
|
|
78
|
+
let url: URL
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
url = new URL(value)
|
|
82
|
+
} catch {
|
|
83
|
+
return new Error(`\`${value}\` is not an absolute URI`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (url.hash !== '')
|
|
87
|
+
return new Error(`\`${value}\` carries a fragment`)
|
|
88
|
+
|
|
89
|
+
if (url.protocol === 'https:')
|
|
90
|
+
return url.href
|
|
91
|
+
|
|
92
|
+
if (url.protocol === 'http:' && LOOPBACK.has(url.hostname))
|
|
93
|
+
return url.href
|
|
94
|
+
|
|
95
|
+
return new Error(`\`${value}\` is neither https nor a loopback address`)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** RFC 7591 §3.2.2: a registration error is a body, and `map:status` makes it the status. */
|
|
99
|
+
function invalid (error: string, description: string): Output {
|
|
100
|
+
return { status: BAD_REQUEST, error, error_description: description }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const BAD_REQUEST = 400
|
|
104
|
+
|
|
105
|
+
const LOOPBACK = new Set(['127.0.0.1', '[::1]', 'localhost'])
|
|
106
|
+
|
|
107
|
+
interface Input extends Metadata {
|
|
108
|
+
authority: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface Output {
|
|
112
|
+
/** Read and removed by `map:status`; absent on a success, which answers 201. */
|
|
113
|
+
status?: number
|
|
114
|
+
client_id?: string
|
|
115
|
+
client_id_issued_at?: number
|
|
116
|
+
client_name?: string
|
|
117
|
+
client_uri?: string
|
|
118
|
+
logo_uri?: string
|
|
119
|
+
redirect_uris?: string[]
|
|
120
|
+
grant_types?: string[]
|
|
121
|
+
response_types?: string[]
|
|
122
|
+
token_endpoint_auth_method?: string
|
|
123
|
+
error?: string
|
|
124
|
+
error_description?: string
|
|
125
|
+
}
|