@cyanmycelium/mcp-broker 0.3.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp-broker.example/CONFIGURATION-EN.md +861 -0
- package/.mcp-broker.example/CONFIGURATION-FR.md +871 -0
- package/.mcp-broker.example/README.md +5 -0
- package/.mcp-broker.example/config.json +86 -0
- package/README.md +116 -1
- package/dist/auth/auth.config.d.ts +40 -0
- package/dist/auth/auth.config.js +62 -0
- package/dist/auth/auth.config.js.map +1 -0
- package/dist/auth/auth.types.d.ts +119 -0
- package/dist/auth/auth.types.js +38 -0
- package/dist/auth/auth.types.js.map +1 -0
- package/dist/auth/http.auth.d.ts +51 -0
- package/dist/auth/http.auth.js +121 -0
- package/dist/auth/http.auth.js.map +1 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.js +12 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt.validator.d.ts +35 -0
- package/dist/auth/jwt.validator.js +46 -0
- package/dist/auth/jwt.validator.js.map +1 -0
- package/dist/auth/provider.auth.d.ts +51 -0
- package/dist/auth/provider.auth.js +80 -0
- package/dist/auth/provider.auth.js.map +1 -0
- package/dist/auth/resource.metadata.d.ts +32 -0
- package/dist/auth/resource.metadata.js +26 -0
- package/dist/auth/resource.metadata.js.map +1 -0
- package/dist/authorization/audit.d.ts +14 -0
- package/dist/authorization/audit.js +20 -0
- package/dist/authorization/audit.js.map +1 -0
- package/dist/authorization/capability.classifier.d.ts +30 -0
- package/dist/authorization/capability.classifier.js +85 -0
- package/dist/authorization/capability.classifier.js.map +1 -0
- package/dist/authorization/index.d.ts +13 -0
- package/dist/authorization/index.js +8 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/policy.engine.d.ts +11 -0
- package/dist/authorization/policy.engine.js +192 -0
- package/dist/authorization/policy.engine.js.map +1 -0
- package/dist/authorization/policy.types.d.ts +92 -0
- package/dist/authorization/policy.types.js +2 -0
- package/dist/authorization/policy.types.js.map +1 -0
- package/dist/authorization/resource.path.d.ts +27 -0
- package/dist/authorization/resource.path.js +122 -0
- package/dist/authorization/resource.path.js.map +1 -0
- package/dist/authorization/runtime.d.ts +22 -0
- package/dist/authorization/runtime.js +34 -0
- package/dist/authorization/runtime.js.map +1 -0
- package/dist/authorization/slot.resource.d.ts +12 -0
- package/dist/authorization/slot.resource.js +41 -0
- package/dist/authorization/slot.resource.js.map +1 -0
- package/dist/authorization/subject.mapper.d.ts +15 -0
- package/dist/authorization/subject.mapper.js +67 -0
- package/dist/authorization/subject.mapper.js.map +1 -0
- package/dist/bin.js +49 -0
- package/dist/bin.js.map +1 -1
- package/dist/broker/adapters/broker.adapter.info.d.ts +3 -3
- package/dist/broker/adapters/broker.adapter.info.js +1 -1
- package/dist/broker/adapters/broker.adapter.info.js.map +1 -1
- package/dist/broker/adapters/broker.adapter.providers.d.ts +2 -2
- package/dist/broker/adapters/broker.adapter.providers.js.map +1 -1
- package/dist/broker/aggregate/aggregate.catalog.d.ts +23 -11
- package/dist/broker/aggregate/aggregate.catalog.js +14 -0
- package/dist/broker/aggregate/aggregate.catalog.js.map +1 -1
- package/dist/broker/aggregate/aggregate.server.d.ts +22 -3
- package/dist/broker/aggregate/aggregate.server.js +115 -9
- package/dist/broker/aggregate/aggregate.server.js.map +1 -1
- package/dist/broker/aggregate/provider.client.session.d.ts +9 -9
- package/dist/broker/aggregate/provider.client.session.js +1 -1
- package/dist/broker/aggregate/provider.client.session.js.map +1 -1
- package/dist/broker/behaviors/broker.behavior.info.d.ts +2 -2
- package/dist/broker/behaviors/broker.behavior.info.js.map +1 -1
- package/dist/broker/behaviors/broker.behavior.providers.d.ts +2 -2
- package/dist/broker/behaviors/broker.behavior.providers.js.map +1 -1
- package/dist/broker/broker.context.d.ts +8 -4
- package/dist/broker/broker.grammars.d.ts +52 -86
- package/dist/broker/broker.grammars.js +55 -84
- package/dist/broker/broker.grammars.js.map +1 -1
- package/dist/broker/broker.server.d.ts +28 -24
- package/dist/broker/broker.server.js +21 -70
- package/dist/broker/broker.server.js.map +1 -1
- package/dist/broker/index.d.ts +4 -4
- package/dist/broker/index.js +1 -1
- package/dist/broker/index.js.map +1 -1
- package/dist/config.d.ts +51 -5
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +13 -9
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/mcpb.loader.d.ts +6 -4
- package/dist/mcpb.loader.js +3 -3
- package/dist/mcpb.loader.js.map +1 -1
- package/dist/remote.transports.d.ts +4 -2
- package/dist/remote.transports.js.map +1 -1
- package/dist/remote.upstream.d.ts +6 -4
- package/dist/remote.upstream.js.map +1 -1
- package/dist/stdio.upstream.d.ts +6 -4
- package/dist/stdio.upstream.js.map +1 -1
- package/dist/upstream.d.ts +3 -1
- package/dist/ws.tunnel.builder.d.ts +41 -4
- package/dist/ws.tunnel.builder.js +66 -0
- package/dist/ws.tunnel.builder.js.map +1 -1
- package/dist/ws.tunnel.d.ts +116 -36
- package/dist/ws.tunnel.js +384 -51
- package/dist/ws.tunnel.js.map +1 -1
- package/package.json +7 -3
- package/src/auth/auth.config.ts +98 -0
- package/src/auth/auth.types.ts +145 -0
- package/src/auth/http.auth.ts +132 -0
- package/src/auth/index.ts +34 -0
- package/src/auth/jwt.validator.ts +63 -0
- package/src/auth/provider.auth.ts +114 -0
- package/src/auth/resource.metadata.ts +41 -0
- package/src/authorization/audit.ts +35 -0
- package/src/authorization/capability.classifier.ts +114 -0
- package/src/authorization/index.ts +37 -0
- package/src/authorization/policy.engine.ts +212 -0
- package/src/authorization/policy.types.ts +105 -0
- package/src/authorization/resource.path.ts +126 -0
- package/src/authorization/runtime.ts +56 -0
- package/src/authorization/slot.resource.ts +50 -0
- package/src/authorization/subject.mapper.ts +81 -0
- package/src/bin.ts +53 -0
- package/src/broker/adapters/broker.adapter.info.ts +3 -3
- package/src/broker/adapters/broker.adapter.providers.ts +3 -3
- package/src/broker/aggregate/aggregate.catalog.ts +48 -20
- package/src/broker/aggregate/aggregate.server.ts +146 -16
- package/src/broker/aggregate/provider.client.session.ts +22 -22
- package/src/broker/behaviors/broker.behavior.info.ts +2 -2
- package/src/broker/behaviors/broker.behavior.providers.ts +2 -2
- package/src/broker/broker.context.ts +10 -4
- package/src/broker/broker.grammars.ts +77 -122
- package/src/broker/broker.server.ts +51 -101
- package/src/broker/index.ts +5 -7
- package/src/config.ts +57 -6
- package/src/index.ts +109 -16
- package/src/mcpb.loader.ts +14 -11
- package/src/remote.transports.ts +8 -5
- package/src/remote.upstream.ts +10 -7
- package/src/stdio.upstream.ts +8 -5
- package/src/upstream.ts +4 -1
- package/src/ws.tunnel.builder.ts +89 -9
- package/src/ws.tunnel.ts +521 -102
- package/web/README.md +94 -0
- package/web/assets/logo.png +0 -0
- package/web/broker-self-mcp.html +338 -0
- package/web/css/styles.css +580 -0
- package/web/demos/DemoPlaceholder.html +258 -0
- package/web/demos/broker-explorer/css/app.css +393 -0
- package/web/demos/broker-explorer/index.html +94 -0
- package/web/demos/broker-explorer/js/app.js +271 -0
- package/web/demos/broker-explorer/js/mcp-ws-client.js +132 -0
- package/web/demos/oauth-lab/README.md +68 -0
- package/web/demos/oauth-lab/config.json +116 -0
- package/web/demos/oauth-lab/css/app.css +1097 -0
- package/web/demos/oauth-lab/index.html +323 -0
- package/web/demos/oauth-lab/js/app.js +630 -0
- package/web/demos/oauth-lab/server/auth-server.mjs +426 -0
- package/web/demos/oauth-lab/server/factory-provider.mjs +269 -0
- package/web/demos/oauth-lab/server/smoke-test.mjs +261 -0
- package/web/demos/oauth-lab/server/start.mjs +106 -0
- package/web/demos/provider-tunnel/css/app.css +384 -0
- package/web/demos/provider-tunnel/index.html +99 -0
- package/web/demos/provider-tunnel/js/app.js +226 -0
- package/web/demos/provider-tunnel/js/toolbox-server.js +186 -0
- package/web/index.html +558 -0
- package/web/js/lib/broker-tunnel.js +173 -0
|
@@ -22,6 +22,92 @@
|
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
24
|
|
|
25
|
+
"auth": {
|
|
26
|
+
"enabled": true,
|
|
27
|
+
"publicBaseUrl": "https://mcp.factory.local",
|
|
28
|
+
"authorizationServers": [
|
|
29
|
+
"https://identity.factory.local"
|
|
30
|
+
],
|
|
31
|
+
"jwks": "https://identity.factory.local/.well-known/jwks.json",
|
|
32
|
+
"requiredScopes": ["mcp:call"],
|
|
33
|
+
"perSlotScopes": {
|
|
34
|
+
"_broker": ["broker:admin"]
|
|
35
|
+
},
|
|
36
|
+
"subjectMapping": {
|
|
37
|
+
"userClaim": "sub",
|
|
38
|
+
"groupClaims": ["groups"],
|
|
39
|
+
"clientClaim": "client_id"
|
|
40
|
+
},
|
|
41
|
+
"roles": {
|
|
42
|
+
"viewer": {
|
|
43
|
+
"capabilities": [
|
|
44
|
+
"mcp.resources.read",
|
|
45
|
+
"mcp.tools.list",
|
|
46
|
+
"mcp.prompts.read"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"maintenance": {
|
|
50
|
+
"inherits": ["viewer"],
|
|
51
|
+
"capabilities": [
|
|
52
|
+
"mcp.tools.call",
|
|
53
|
+
"mcp.tools.diagnose",
|
|
54
|
+
"mcp.tools.configure-analysis"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"operator": {
|
|
58
|
+
"inherits": ["viewer"],
|
|
59
|
+
"capabilities": ["mcp.tools.operate"]
|
|
60
|
+
},
|
|
61
|
+
"administrator": {
|
|
62
|
+
"capabilities": ["*"]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"assignments": [
|
|
66
|
+
{
|
|
67
|
+
"id": "maintenance-area-a",
|
|
68
|
+
"subject": "group:maintenance-area-a",
|
|
69
|
+
"role": "maintenance",
|
|
70
|
+
"resource": "/enterprise-a/site-paris/area-a/**"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "energy-team",
|
|
74
|
+
"subject": "group:energy-team",
|
|
75
|
+
"role": "viewer",
|
|
76
|
+
"resource": "/enterprise-a/site-paris/**"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"denies": [
|
|
80
|
+
{
|
|
81
|
+
"id": "protect-critical-furnace",
|
|
82
|
+
"subject": "group:maintenance-area-a",
|
|
83
|
+
"capabilities": [
|
|
84
|
+
"mcp.tools.configure-analysis",
|
|
85
|
+
"mcp.tools.operate"
|
|
86
|
+
],
|
|
87
|
+
"resource": "/enterprise-a/site-paris/area-a/line-2/cell-4/critical-furnace"
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"slotResources": {
|
|
91
|
+
"spoony-00452": "/enterprise-a/site-paris/area-a/line-3/cell-2/motor-7",
|
|
92
|
+
"site-energy": "/enterprise-a/site-paris"
|
|
93
|
+
},
|
|
94
|
+
"toolCapabilities": {
|
|
95
|
+
"get_electrical_state": "mcp.resources.read",
|
|
96
|
+
"diagnose_motor": "mcp.tools.diagnose",
|
|
97
|
+
"reset_baseline": "mcp.tools.configure-analysis",
|
|
98
|
+
"start_motor": "mcp.tools.operate"
|
|
99
|
+
},
|
|
100
|
+
"providerToolCapabilities": {
|
|
101
|
+
"/enterprise-a/site-paris/area-a/**": {
|
|
102
|
+
"start_motor": "mcp.tools.operate"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"audit": {
|
|
106
|
+
"logAllowed": false
|
|
107
|
+
},
|
|
108
|
+
"providerSecret": "change-me"
|
|
109
|
+
},
|
|
110
|
+
|
|
25
111
|
"stdioUpstreams": [
|
|
26
112
|
{
|
|
27
113
|
"name": "fs",
|
package/README.md
CHANGED
|
@@ -96,6 +96,81 @@ Full reference (every field, defaults, recipes, grammar overrides): **[docs/conf
|
|
|
96
96
|
| `MCP_BROKER_TLS_KEY` | (unset) | Path to a PEM private key. Enables HTTPS/WSS |
|
|
97
97
|
| `MCP_BROKER_PROTOCOL` | auto | `http` forces plain, `https` forces TLS, unset auto-detects from cert+key |
|
|
98
98
|
| `MCP_BROKER_STDIO_PROVIDER` | (unset) | When set, bridge stdin/stdout JSON-RPC to this provider (Claude Desktop integration) |
|
|
99
|
+
| `MCP_BROKER_AUTH_ENABLED` | (unset) | `1` to turn on the OAuth 2.1 resource server (requires the three below) |
|
|
100
|
+
| `MCP_BROKER_PUBLIC_BASE_URL` | (unset) | Public origin used to build canonical resource URIs, e.g. `https://mcp.example.com` |
|
|
101
|
+
| `MCP_BROKER_JWKS` | (unset) | Authorization server's JWKS URL, used to verify token signatures |
|
|
102
|
+
| `MCP_BROKER_ISSUER` | (unset) | Expected token issuer (defaults to the sole authorization server) |
|
|
103
|
+
| `MCP_BROKER_PROVIDER_SECRET` | (unset) | Shared secret every provider must present to occupy a slot |
|
|
104
|
+
|
|
105
|
+
## Authorization (OAuth 2.1)
|
|
106
|
+
|
|
107
|
+
By default the broker performs **no** authentication. That is fine behind a
|
|
108
|
+
trusted network boundary, but do not expose it publicly as-is. Turn on the OAuth 2.1
|
|
109
|
+
resource server to require a bearer token on every client request, authenticate
|
|
110
|
+
providers, and filter the `_all` aggregate per caller.
|
|
111
|
+
|
|
112
|
+
Enabled via the `auth` config block (or env vars). Minimal `.mcp-broker/config.json`:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"auth": {
|
|
117
|
+
"enabled": true,
|
|
118
|
+
"publicBaseUrl": "https://mcp.example.com",
|
|
119
|
+
"authorizationServers": ["https://auth.example.com"],
|
|
120
|
+
"jwks": "https://auth.example.com/.well-known/jwks.json",
|
|
121
|
+
"requiredScopes": ["mcp:call"],
|
|
122
|
+
"perSlotScopes": { "_broker": ["broker:admin"] },
|
|
123
|
+
"subjectMapping": {
|
|
124
|
+
"userClaim": "sub",
|
|
125
|
+
"groupClaims": ["groups"],
|
|
126
|
+
"clientClaim": "client_id"
|
|
127
|
+
},
|
|
128
|
+
"roles": {
|
|
129
|
+
"viewer": {
|
|
130
|
+
"capabilities": ["mcp.tools.list", "mcp.resources.read"]
|
|
131
|
+
},
|
|
132
|
+
"maintenance": {
|
|
133
|
+
"inherits": ["viewer"],
|
|
134
|
+
"capabilities": ["mcp.tools.diagnose"]
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"assignments": [
|
|
138
|
+
{
|
|
139
|
+
"subject": "group:maintenance-site-a",
|
|
140
|
+
"role": "maintenance",
|
|
141
|
+
"resource": "/enterprise/site-a/**"
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"slotResources": {
|
|
145
|
+
"motor-7": "/enterprise/site-a/area-a/line-3/cell-2/motor-7"
|
|
146
|
+
},
|
|
147
|
+
"toolCapabilities": {
|
|
148
|
+
"diagnose_motor": "mcp.tools.diagnose"
|
|
149
|
+
},
|
|
150
|
+
"providerSecret": "change-me"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
With this on:
|
|
156
|
+
|
|
157
|
+
- `POST /<slot>/mcp` (and `/sse`, `/messages`, `ws://…/<slot>`) require
|
|
158
|
+
`Authorization: Bearer <token>`; the token's audience must be
|
|
159
|
+
`https://mcp.example.com/<slot>/mcp`.
|
|
160
|
+
- Clients discover the authorization server via
|
|
161
|
+
`GET /.well-known/oauth-protected-resource/<slot>/mcp` (RFC 9728), advertised
|
|
162
|
+
in the `401` challenge.
|
|
163
|
+
- Providers must present `providerSecret` (via `X-Provider-Token` or
|
|
164
|
+
`Authorization: Bearer`) to connect to `/provider/<slot>` or `/providers`.
|
|
165
|
+
- `_all` only shows providers allowed by the caller's hierarchical policy.
|
|
166
|
+
- Explicit denies override inherited role grants.
|
|
167
|
+
- Structured provider principals can publish only inside their allowed resource
|
|
168
|
+
namespace.
|
|
169
|
+
|
|
170
|
+
Full model, flows, scopes, and endpoints: **[../docs/authorization.md](../docs/authorization.md)**.
|
|
171
|
+
Roles, resource paths, denies, and migration:
|
|
172
|
+
**[../docs/hierarchical-authorization.md](../docs/hierarchical-authorization.md)**.
|
|
173
|
+
Config field reference: **[docs/config.md](docs/config.md#auth-oauth-21-authorization)**.
|
|
99
174
|
|
|
100
175
|
## Programmatic API
|
|
101
176
|
|
|
@@ -111,12 +186,52 @@ const broker = new WsTunnelBuilder()
|
|
|
111
186
|
.withStdioUpstream("my-server", "node", ["./my-server.js"])
|
|
112
187
|
// Optional: serve a dev harness at /
|
|
113
188
|
.withStaticMount("/", "/abs/path/to/www")
|
|
189
|
+
// Optional: enable the OAuth 2.1 resource server + provider auth
|
|
190
|
+
.withJwtAuth({
|
|
191
|
+
publicBaseUrl: "https://mcp.example.com",
|
|
192
|
+
authorizationServers: ["https://auth.example.com"],
|
|
193
|
+
jwksUri: "https://auth.example.com/.well-known/jwks.json",
|
|
194
|
+
requiredScopes: ["mcp:call"],
|
|
195
|
+
roles: {
|
|
196
|
+
viewer: { capabilities: ["mcp.tools.list"] },
|
|
197
|
+
},
|
|
198
|
+
assignments: [
|
|
199
|
+
{
|
|
200
|
+
subject: "group:operators",
|
|
201
|
+
role: "viewer",
|
|
202
|
+
resource: "/enterprise/site-a/**",
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
subjectMapping: { groupClaims: ["groups"] },
|
|
206
|
+
})
|
|
207
|
+
.withProviderSecret(process.env.PROVIDER_SECRET!)
|
|
114
208
|
.build();
|
|
115
209
|
|
|
116
210
|
await broker.start();
|
|
117
211
|
```
|
|
118
212
|
|
|
119
|
-
All builder methods are documented inline. The full options interface is
|
|
213
|
+
All builder methods are documented inline. The full options interface is
|
|
214
|
+
`IWsTunnelOptions`, also exported. Use `withAuthorizationPolicy` for a
|
|
215
|
+
standalone policy configuration, `withPolicyEngine` for a custom engine, and
|
|
216
|
+
`withSlotResourceResolver` for a custom namespace mapping. For a custom token
|
|
217
|
+
validator (for example RFC 7662 introspection) or provider authenticator, use
|
|
218
|
+
`withAuth(resolvedAuth)` or `withProviderAuth(authenticator)` with your own
|
|
219
|
+
`ITokenValidator` or `IProviderAuthenticator`.
|
|
220
|
+
|
|
221
|
+
## Interactive OAuth demo
|
|
222
|
+
|
|
223
|
+
The bundled [OAuth Policy Lab](web/demos/oauth-lab/) runs a complete local
|
|
224
|
+
Authorization Code and PKCE flow with signed JWTs, JWKS validation,
|
|
225
|
+
audience-bound tokens, hierarchical policies, explicit denies, and a live
|
|
226
|
+
audit:
|
|
227
|
+
|
|
228
|
+
```sh
|
|
229
|
+
npm run demo:oauth
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
The page opens at `http://127.0.0.1:3001/demos/oauth-lab/`. See the
|
|
233
|
+
[demo README](web/demos/oauth-lab/README.md) for its identities, policy matrix,
|
|
234
|
+
and automated smoke test.
|
|
120
235
|
|
|
121
236
|
## TLS for local development
|
|
122
237
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { IResolvedAuth } from "./auth.types.js";
|
|
2
|
+
import { type IAuthorizationPolicyConfig } from "../authorization/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* High-level options for the default JWT/JWKS resource-server setup. Mirrors the
|
|
5
|
+
* `auth` block of the broker JSON config and is turned into a fully
|
|
6
|
+
* {@link IResolvedAuth} (with a {@link JwtTokenValidator}) by {@link buildJwtAuth}.
|
|
7
|
+
*/
|
|
8
|
+
export interface IJwtAuthOptions extends IAuthorizationPolicyConfig {
|
|
9
|
+
/** Public origin the broker is reached at (e.g. `https://mcp.example.com`). */
|
|
10
|
+
publicBaseUrl: string;
|
|
11
|
+
/** Authorization server issuer URL(s) advertised in the PRM. At least one. */
|
|
12
|
+
authorizationServers: string[];
|
|
13
|
+
/** URL of the authorization server's JWKS document. */
|
|
14
|
+
jwksUri: string;
|
|
15
|
+
/** Expected token issuer(s). Defaults to the sole authorization server. */
|
|
16
|
+
issuer?: string | string[];
|
|
17
|
+
/** Scopes advertised in the PRM `scopes_supported`. */
|
|
18
|
+
scopesSupported?: string[];
|
|
19
|
+
/** Baseline scope(s) required to reach any slot. */
|
|
20
|
+
requiredScopes?: string[];
|
|
21
|
+
/** Per-slot required-scope overrides (e.g. an admin scope for `_broker`). */
|
|
22
|
+
perSlotScopes?: Record<string, string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Per-provider scope requirements for the `_all` aggregate. A caller sees a
|
|
25
|
+
* provider in `_all` only if it holds at least one of the listed scopes.
|
|
26
|
+
* Providers not listed here stay visible to every authenticated caller.
|
|
27
|
+
* Turned into an {@link AggregateScopeFilter} automatically.
|
|
28
|
+
*/
|
|
29
|
+
providerScopes?: Record<string, string[]>;
|
|
30
|
+
/** Leeway in seconds for token `exp`/`nbf` checks. */
|
|
31
|
+
clockToleranceSec?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Builds an {@link IResolvedAuth} backed by a {@link JwtTokenValidator}. Validates
|
|
35
|
+
* the required inputs up front and throws a descriptive error on misconfig, so
|
|
36
|
+
* an operator sees the problem at boot rather than as opaque `401`s later.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildJwtAuth(options: IJwtAuthOptions): IResolvedAuth;
|
|
39
|
+
/** @deprecated Use {@link IJwtAuthOptions}. */
|
|
40
|
+
export type JwtAuthOptions = IJwtAuthOptions;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { JwtTokenValidator } from "./jwt.validator.js";
|
|
2
|
+
import { compileAuthorizationPolicy, DefaultSlotResourceResolver, hasAuthorizationPolicies } from "../authorization/index.js";
|
|
3
|
+
/** Builds the default aggregate filter from a per-provider scope map. */
|
|
4
|
+
function makeProviderScopeFilter(providerScopes) {
|
|
5
|
+
return (principal, providerName) => {
|
|
6
|
+
const required = providerScopes[providerName];
|
|
7
|
+
if (!required || required.length === 0)
|
|
8
|
+
return true; // unlisted ⇒ visible to all
|
|
9
|
+
return required.some((scope) => principal.scopes.has(scope));
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Removes a single trailing slash so resource URIs concatenate cleanly. */
|
|
13
|
+
function stripTrailingSlash(url) {
|
|
14
|
+
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds an {@link IResolvedAuth} backed by a {@link JwtTokenValidator}. Validates
|
|
18
|
+
* the required inputs up front and throws a descriptive error on misconfig, so
|
|
19
|
+
* an operator sees the problem at boot rather than as opaque `401`s later.
|
|
20
|
+
*/
|
|
21
|
+
export function buildJwtAuth(options) {
|
|
22
|
+
if (!options.publicBaseUrl) {
|
|
23
|
+
throw new Error("auth: publicBaseUrl is required.");
|
|
24
|
+
}
|
|
25
|
+
if (!options.authorizationServers || options.authorizationServers.length === 0) {
|
|
26
|
+
throw new Error("auth: at least one authorizationServers entry is required.");
|
|
27
|
+
}
|
|
28
|
+
if (!options.jwksUri) {
|
|
29
|
+
throw new Error("auth: jwksUri is required.");
|
|
30
|
+
}
|
|
31
|
+
const publicBaseUrl = stripTrailingSlash(options.publicBaseUrl);
|
|
32
|
+
const issuer = options.issuer ?? (options.authorizationServers.length === 1 ? options.authorizationServers[0] : undefined);
|
|
33
|
+
const validator = new JwtTokenValidator({
|
|
34
|
+
jwksUri: options.jwksUri,
|
|
35
|
+
issuer,
|
|
36
|
+
clockToleranceSec: options.clockToleranceSec,
|
|
37
|
+
});
|
|
38
|
+
const resolved = {
|
|
39
|
+
publicBaseUrl,
|
|
40
|
+
authorizationServers: options.authorizationServers,
|
|
41
|
+
validator,
|
|
42
|
+
};
|
|
43
|
+
if (options.scopesSupported)
|
|
44
|
+
resolved.scopesSupported = options.scopesSupported;
|
|
45
|
+
if (options.requiredScopes)
|
|
46
|
+
resolved.requiredScopes = options.requiredScopes;
|
|
47
|
+
if (options.perSlotScopes)
|
|
48
|
+
resolved.perSlotScopes = options.perSlotScopes;
|
|
49
|
+
if (options.providerScopes && Object.keys(options.providerScopes).length > 0) {
|
|
50
|
+
console.warn("[mcp-broker] auth.providerScopes is deprecated; migrate to roles and hierarchical assignments.");
|
|
51
|
+
resolved.aggregateScopeFilter = makeProviderScopeFilter(options.providerScopes);
|
|
52
|
+
}
|
|
53
|
+
if (options.slotResources && Object.keys(options.slotResources).length > 0) {
|
|
54
|
+
resolved.slotResourceResolver = new DefaultSlotResourceResolver(options.slotResources);
|
|
55
|
+
}
|
|
56
|
+
if (hasAuthorizationPolicies(options)) {
|
|
57
|
+
resolved.authorization = compileAuthorizationPolicy(options);
|
|
58
|
+
resolved.slotResourceResolver = resolved.authorization.slotResourceResolver;
|
|
59
|
+
}
|
|
60
|
+
return resolved;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=auth.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.config.js","sourceRoot":"","sources":["../../src/auth/auth.config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core contracts for the broker's OAuth 2.1 **resource server** layer.
|
|
3
|
+
*
|
|
4
|
+
* The broker never acts as an authorization server: it only validates the
|
|
5
|
+
* access tokens minted by an external AS and advertises that AS through
|
|
6
|
+
* Protected Resource Metadata (RFC 9728). These types are the seam every other
|
|
7
|
+
* auth module builds on, so a host can drop in a custom {@link ITokenValidator}
|
|
8
|
+
* (e.g. RFC 7662 introspection) without touching the enforcement code.
|
|
9
|
+
*/
|
|
10
|
+
import type { IAuthorizationSubject, IPolicyAuthorization, ISlotResourceResolver } from "../authorization/index.js";
|
|
11
|
+
/**
|
|
12
|
+
* The subset of OAuth 2.1 / RFC 9068 access-token claims a resource server
|
|
13
|
+
* cares about. `aud` carries the audience binding (RFC 8707), `scope` the
|
|
14
|
+
* space-delimited granted scopes; some authorization servers emit `scopes` as
|
|
15
|
+
* an array instead. Unknown claims are preserved via the index signature.
|
|
16
|
+
*/
|
|
17
|
+
export interface IAccessTokenClaims {
|
|
18
|
+
/** Subject — the principal the token was issued for. */
|
|
19
|
+
sub?: string;
|
|
20
|
+
/** Intended audience(s). MUST include the slot's canonical resource URI. */
|
|
21
|
+
aud?: string | string[];
|
|
22
|
+
/** Issuer (the authorization server). */
|
|
23
|
+
iss?: string;
|
|
24
|
+
/** Expiry, seconds since the epoch. */
|
|
25
|
+
exp?: number;
|
|
26
|
+
/** Space-delimited granted scopes (RFC 8693). */
|
|
27
|
+
scope?: string;
|
|
28
|
+
/** Array-form granted scopes, emitted by some authorization servers. */
|
|
29
|
+
scopes?: string[];
|
|
30
|
+
[claim: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validates a bearer access token for a specific resource. Implementations MUST
|
|
34
|
+
* verify the token's signature/validity **and** that it was issued for
|
|
35
|
+
* `resource` (audience binding, RFC 8707). On any failure they MUST throw an
|
|
36
|
+
* {@link AuthError} with status `401` and code `invalid_token`; unexpected
|
|
37
|
+
* infrastructure failures (e.g. JWKS unreachable) should throw a plain error so
|
|
38
|
+
* the caller can surface a `500` rather than a misleading `401`.
|
|
39
|
+
*/
|
|
40
|
+
export interface ITokenValidator {
|
|
41
|
+
validate(token: string, resource: string): Promise<IAccessTokenClaims>;
|
|
42
|
+
}
|
|
43
|
+
/** OAuth 2.1 error codes the broker emits in `WWW-Authenticate` challenges. */
|
|
44
|
+
export type AuthErrorCode = "invalid_token" | "insufficient_scope" | "invalid_request";
|
|
45
|
+
/**
|
|
46
|
+
* A thrown authorization failure carrying the HTTP status and OAuth error code
|
|
47
|
+
* the enforcement point should surface. `scope` is set for `insufficient_scope`
|
|
48
|
+
* challenges to advertise the scope(s) the resource requires.
|
|
49
|
+
*/
|
|
50
|
+
export declare class AuthError extends Error {
|
|
51
|
+
readonly status: 401 | 403 | 400;
|
|
52
|
+
readonly code: AuthErrorCode;
|
|
53
|
+
readonly description?: string;
|
|
54
|
+
readonly scope?: string;
|
|
55
|
+
constructor(status: 401 | 403 | 400, code: AuthErrorCode, description?: string, scope?: string);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The authenticated caller, produced once a token passes validation and the
|
|
59
|
+
* required scopes are satisfied. Threaded through the request so downstream
|
|
60
|
+
* components (e.g. the `_all` aggregate) can make scope-aware decisions.
|
|
61
|
+
*/
|
|
62
|
+
export interface IPrincipal {
|
|
63
|
+
readonly claims: IAccessTokenClaims;
|
|
64
|
+
readonly scopes: ReadonlySet<string>;
|
|
65
|
+
/** Subjects derived exclusively from validated token claims. */
|
|
66
|
+
readonly subject?: IAuthorizationSubject;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Decides, per authenticated caller, whether a given provider is visible in the
|
|
70
|
+
* `_all` aggregate. This is the content-confidentiality enforcement point: a
|
|
71
|
+
* client only sees (and can call) tools/prompts from providers it is authorized
|
|
72
|
+
* for. Return `true` to include the provider for this principal.
|
|
73
|
+
*/
|
|
74
|
+
export type AggregateScopeFilter = (principal: IPrincipal, providerName: string) => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* A fully resolved authorization configuration, ready for the enforcement
|
|
77
|
+
* layer. Built either from JSON config (via `buildJwtAuth`) or supplied
|
|
78
|
+
* directly by an embedder with a custom {@link ITokenValidator}.
|
|
79
|
+
*/
|
|
80
|
+
export interface IResolvedAuth {
|
|
81
|
+
/**
|
|
82
|
+
* Public origin the broker is reached at, used to build canonical resource
|
|
83
|
+
* URIs and metadata URLs. No trailing slash (e.g. `https://mcp.example.com`).
|
|
84
|
+
*/
|
|
85
|
+
publicBaseUrl: string;
|
|
86
|
+
/** One or more authorization server issuer URLs, advertised in the PRM. */
|
|
87
|
+
authorizationServers: string[];
|
|
88
|
+
/** Optional list of scopes advertised in the PRM `scopes_supported`. */
|
|
89
|
+
scopesSupported?: string[];
|
|
90
|
+
/** The token validator (JWKS-backed by default). */
|
|
91
|
+
validator: ITokenValidator;
|
|
92
|
+
/** Baseline scope(s) required to reach any slot. Empty ⇒ any valid token. */
|
|
93
|
+
requiredScopes?: string[];
|
|
94
|
+
/** Per-slot required-scope overrides (e.g. an admin scope for `_broker`). */
|
|
95
|
+
perSlotScopes?: Record<string, string[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Per-caller filter for the `_all` aggregate. When set, a client's view of
|
|
98
|
+
* `_all` is narrowed to the providers this returns `true` for. When absent,
|
|
99
|
+
* every authenticated caller sees the full aggregate.
|
|
100
|
+
*/
|
|
101
|
+
aggregateScopeFilter?: AggregateScopeFilter;
|
|
102
|
+
/** Compiled hierarchical policy runtime, absent for legacy OAuth behavior. */
|
|
103
|
+
authorization?: IPolicyAuthorization;
|
|
104
|
+
/** Optional resolver usable for provider namespace checks without policies. */
|
|
105
|
+
slotResourceResolver?: ISlotResourceResolver;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Extracts the effective set of granted scopes from token claims, unioning the
|
|
109
|
+
* space-delimited `scope` string and the array-form `scopes` claim.
|
|
110
|
+
*/
|
|
111
|
+
export declare function scopesOf(claims: IAccessTokenClaims): Set<string>;
|
|
112
|
+
/** @deprecated Use {@link IAccessTokenClaims}. */
|
|
113
|
+
export type AccessTokenClaims = IAccessTokenClaims;
|
|
114
|
+
/** @deprecated Use {@link ITokenValidator}. */
|
|
115
|
+
export type TokenValidator = ITokenValidator;
|
|
116
|
+
/** @deprecated Use {@link IPrincipal}. */
|
|
117
|
+
export type Principal = IPrincipal;
|
|
118
|
+
/** @deprecated Use {@link IResolvedAuth}. */
|
|
119
|
+
export type ResolvedAuth = IResolvedAuth;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A thrown authorization failure carrying the HTTP status and OAuth error code
|
|
3
|
+
* the enforcement point should surface. `scope` is set for `insufficient_scope`
|
|
4
|
+
* challenges to advertise the scope(s) the resource requires.
|
|
5
|
+
*/
|
|
6
|
+
export class AuthError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
code;
|
|
9
|
+
description;
|
|
10
|
+
scope;
|
|
11
|
+
constructor(status, code, description, scope) {
|
|
12
|
+
super(description ?? code);
|
|
13
|
+
this.name = "AuthError";
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.description = description;
|
|
17
|
+
this.scope = scope;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extracts the effective set of granted scopes from token claims, unioning the
|
|
22
|
+
* space-delimited `scope` string and the array-form `scopes` claim.
|
|
23
|
+
*/
|
|
24
|
+
export function scopesOf(claims) {
|
|
25
|
+
const set = new Set();
|
|
26
|
+
if (typeof claims.scope === "string") {
|
|
27
|
+
for (const s of claims.scope.split(/\s+/))
|
|
28
|
+
if (s)
|
|
29
|
+
set.add(s);
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(claims.scopes)) {
|
|
32
|
+
for (const s of claims.scopes)
|
|
33
|
+
if (typeof s === "string" && s)
|
|
34
|
+
set.add(s);
|
|
35
|
+
}
|
|
36
|
+
return set;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=auth.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.types.js","sourceRoot":"","sources":["../../src/auth/auth.types.ts"],"names":[],"mappings":"AAgDA;;;;GAIG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IACvB,MAAM,CAAkB;IACxB,IAAI,CAAgB;IACpB,WAAW,CAAU;IACrB,KAAK,CAAU;IAExB,YAAY,MAAuB,EAAE,IAAmB,EAAE,WAAoB,EAAE,KAAc;QAC1F,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ;AAuDD;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,MAA0B;IAC/C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM;YAAE,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
|
+
import { AuthError, type IPrincipal, type IResolvedAuth } from "./auth.types.js";
|
|
3
|
+
import { type IProtectedResourceMetadata } from "./resource.metadata.js";
|
|
4
|
+
/**
|
|
5
|
+
* The HTTP enforcement point for the resource-server layer. Wraps a
|
|
6
|
+
* {@link IResolvedAuth} plus the configured `/mcp` path suffix and turns it into
|
|
7
|
+
* the three operations the transport needs: serve Protected Resource Metadata,
|
|
8
|
+
* authorize a request, and write an RFC 9728 `401`/`403` challenge.
|
|
9
|
+
*
|
|
10
|
+
* A slot's **canonical resource URI** is `<publicBaseUrl>/<slot>/<mcp>` and is
|
|
11
|
+
* used uniformly across all of that slot's HTTP endpoints (`/mcp`, `/sse`,
|
|
12
|
+
* `/messages`) so a single token audience covers the whole slot.
|
|
13
|
+
*/
|
|
14
|
+
export declare class HttpAuthGuard {
|
|
15
|
+
private readonly _auth;
|
|
16
|
+
/** The `/mcp` suffix without a leading slash, e.g. `"mcp"`. */
|
|
17
|
+
private readonly _mcpSuffix;
|
|
18
|
+
constructor(auth: IResolvedAuth, mcpSuffix: string);
|
|
19
|
+
/** Canonical resource identifier for a slot (RFC 8707 §2). */
|
|
20
|
+
resourceFor(slot: string): string;
|
|
21
|
+
/** RFC 9728 metadata URL for a slot, advertised in the `401` challenge. */
|
|
22
|
+
metadataUrlFor(slot: string): string;
|
|
23
|
+
/** Required scope(s) for a slot: per-slot override, else the baseline. */
|
|
24
|
+
requiredScopesFor(slot: string): string[];
|
|
25
|
+
/** The RFC 9728 metadata document for a slot. */
|
|
26
|
+
metadataFor(slot: string): IProtectedResourceMetadata;
|
|
27
|
+
/**
|
|
28
|
+
* If `rawUrl` is a Protected Resource Metadata request
|
|
29
|
+
* (`/.well-known/oauth-protected-resource/<slot>/<mcp>`), returns the slot;
|
|
30
|
+
* otherwise `null`.
|
|
31
|
+
*/
|
|
32
|
+
matchMetadataRequest(rawUrl: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Validates the request's bearer token against the slot's canonical resource
|
|
35
|
+
* and enforces the slot's required scopes. Resolves to the {@link IPrincipal}
|
|
36
|
+
* on success; rejects with an {@link AuthError} (`401` missing/invalid token,
|
|
37
|
+
* `403` insufficient scope) otherwise.
|
|
38
|
+
*/
|
|
39
|
+
authorize(req: IncomingMessage, slot: string): Promise<IPrincipal>;
|
|
40
|
+
/**
|
|
41
|
+
* Builds the RFC 9728 §5.1 `WWW-Authenticate` header value for a challenge.
|
|
42
|
+
* Always points the client at the slot's metadata URL so it can discover the
|
|
43
|
+
* authorization server and retry. Shared by the HTTP and WebSocket paths.
|
|
44
|
+
*/
|
|
45
|
+
challengeHeader(slot: string, err: AuthError): string;
|
|
46
|
+
/**
|
|
47
|
+
* Writes an RFC 9728 §5.1 `WWW-Authenticate` challenge and the matching
|
|
48
|
+
* status to an HTTP response.
|
|
49
|
+
*/
|
|
50
|
+
writeChallenge(res: ServerResponse, slot: string, err: AuthError): void;
|
|
51
|
+
}
|