@noodleseed/one 0.96.3 → 0.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/commands/author-loop.d.ts.map +1 -1
- package/dist/commands/author-loop.js +7 -1
- package/dist/commands/author-loop.js.map +1 -1
- package/dist/commands/catalog-data-local-authoring.d.ts.map +1 -1
- package/dist/commands/catalog-data-local-authoring.js +2 -2
- package/dist/commands/catalog-data-local-authoring.js.map +1 -1
- package/dist/deploy.d.ts +5 -0
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +1 -1
- package/dist/deploy.js.map +1 -1
- package/dist/dev.d.ts +13 -0
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +116 -7
- package/dist/dev.js.map +1 -1
- package/dist/devtools-auth-routes.d.ts +4 -0
- package/dist/devtools-auth-routes.d.ts.map +1 -0
- package/dist/devtools-auth-routes.js +205 -0
- package/dist/devtools-auth-routes.js.map +1 -0
- package/dist/devtools-auth-session-helpers.d.ts +6 -0
- package/dist/devtools-auth-session-helpers.d.ts.map +1 -0
- package/dist/devtools-auth-session-helpers.js +19 -0
- package/dist/devtools-auth-session-helpers.js.map +1 -0
- package/dist/devtools-auth-session.d.ts +35 -0
- package/dist/devtools-auth-session.d.ts.map +1 -0
- package/dist/devtools-auth-session.js +433 -0
- package/dist/devtools-auth-session.js.map +1 -0
- package/dist/devtools-auth-types.d.ts +51 -0
- package/dist/devtools-auth-types.d.ts.map +1 -0
- package/dist/devtools-auth-types.js +16 -0
- package/dist/devtools-auth-types.js.map +1 -0
- package/dist/devtools-auth-ui.d.ts +6 -0
- package/dist/devtools-auth-ui.d.ts.map +1 -0
- package/dist/devtools-auth-ui.js +127 -0
- package/dist/devtools-auth-ui.js.map +1 -0
- package/dist/devtools-delegated-credential.d.ts +10 -0
- package/dist/devtools-delegated-credential.d.ts.map +1 -0
- package/dist/devtools-delegated-credential.js +32 -0
- package/dist/devtools-delegated-credential.js.map +1 -0
- package/dist/devtools-firebase-auth.d.ts +55 -0
- package/dist/devtools-firebase-auth.d.ts.map +1 -0
- package/dist/devtools-firebase-auth.js +237 -0
- package/dist/devtools-firebase-auth.js.map +1 -0
- package/dist/devtools-harness.d.ts +5 -0
- package/dist/devtools-harness.d.ts.map +1 -1
- package/dist/devtools-harness.js +60 -21
- package/dist/devtools-harness.js.map +1 -1
- package/dist/devtools-microsoft-auth.d.ts +42 -0
- package/dist/devtools-microsoft-auth.d.ts.map +1 -0
- package/dist/devtools-microsoft-auth.js +362 -0
- package/dist/devtools-microsoft-auth.js.map +1 -0
- package/dist/devtools-microsoft-errors.d.ts +7 -0
- package/dist/devtools-microsoft-errors.d.ts.map +1 -0
- package/dist/devtools-microsoft-errors.js +60 -0
- package/dist/devtools-microsoft-errors.js.map +1 -0
- package/dist/devtools-preview.d.ts +15 -13
- package/dist/devtools-preview.d.ts.map +1 -1
- package/dist/devtools-preview.js +101 -179
- package/dist/devtools-preview.js.map +1 -1
- package/dist/devtools-rpc-forwarder.d.ts +37 -0
- package/dist/devtools-rpc-forwarder.d.ts.map +1 -0
- package/dist/devtools-rpc-forwarder.js +251 -0
- package/dist/devtools-rpc-forwarder.js.map +1 -0
- package/dist/devtools-tool-safety.d.ts +20 -0
- package/dist/devtools-tool-safety.d.ts.map +1 -0
- package/dist/devtools-tool-safety.js +43 -0
- package/dist/devtools-tool-safety.js.map +1 -0
- package/dist/preview-session.d.ts.map +1 -1
- package/dist/preview-session.js +5 -0
- package/dist/preview-session.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/auth/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/auth/dist/index.d.ts.map +1 -1
- package/node_modules/@noodle-borg/auth/dist/index.js +1 -0
- package/node_modules/@noodle-borg/auth/dist/index.js.map +1 -1
- package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.d.ts +56 -0
- package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.d.ts.map +1 -0
- package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.js +367 -0
- package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.d.ts +13 -0
- package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.js +71 -0
- package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/customer-verifier.d.ts +1 -1
- package/node_modules/@noodle-borg/service/dist/customer-verifier.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/customer-verifier.js +43 -31
- package/node_modules/@noodle-borg/service/dist/customer-verifier.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/index.d.ts +3 -1
- package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/index.js +1 -0
- package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.d.ts +20 -0
- package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.js +107 -0
- package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/oauth/microsoft.d.ts +5 -0
- package/node_modules/@noodle-borg/service/dist/oauth/microsoft.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/oauth/microsoft.js +48 -28
- package/node_modules/@noodle-borg/service/dist/oauth/microsoft.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/serve-options.d.ts +13 -0
- package/node_modules/@noodle-borg/service/dist/serve-options.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/serve.d.ts +3 -0
- package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/serve.js +49 -18
- package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { issuerMetadataCandidates } from './discovery.js';
|
|
3
|
+
import { protectedResourceMetadataUrl } from './metadata.js';
|
|
4
|
+
const MAX_OAUTH_JSON_BYTES = 256 * 1024;
|
|
5
|
+
const OAUTH_REQUEST_TIMEOUT_MS = 10_000;
|
|
6
|
+
/**
|
|
7
|
+
* Express-free MCP OAuth client primitives. The caller owns session storage; this class keeps only immutable
|
|
8
|
+
* resource/client configuration and never persists credentials.
|
|
9
|
+
*/
|
|
10
|
+
export class McpOAuthClient {
|
|
11
|
+
#resource;
|
|
12
|
+
#issuer;
|
|
13
|
+
#redirectUri;
|
|
14
|
+
#fetch;
|
|
15
|
+
#clientName;
|
|
16
|
+
#allowInsecureLocalhost;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.#allowInsecureLocalhost = options.allowInsecureLocalhost === true;
|
|
19
|
+
this.#resource = this.#validateResource(options.resource);
|
|
20
|
+
this.#issuer = this.#validateIssuer(options.issuer);
|
|
21
|
+
this.#redirectUri = this.#validateRedirect(options.redirectUri);
|
|
22
|
+
this.#fetch = options.fetchFn ?? fetch;
|
|
23
|
+
this.#clientName = options.clientName ?? 'Noodle Seed Devtools';
|
|
24
|
+
}
|
|
25
|
+
async discover() {
|
|
26
|
+
const protectedResource = await this.#getJson(protectedResourceMetadataUrl(this.#resource));
|
|
27
|
+
if (protectedResource.resource !== this.#resource) {
|
|
28
|
+
throw new Error('protected resource metadata does not match the MCP resource');
|
|
29
|
+
}
|
|
30
|
+
const authorizationServers = stringArray(protectedResource.authorization_servers);
|
|
31
|
+
if (!authorizationServers.includes(this.#issuer)) {
|
|
32
|
+
throw new Error('protected resource metadata does not advertise the configured issuer');
|
|
33
|
+
}
|
|
34
|
+
let firstError;
|
|
35
|
+
for (const candidate of issuerMetadataCandidates(this.#issuer)) {
|
|
36
|
+
try {
|
|
37
|
+
const metadata = await this.#getJson(candidate.url);
|
|
38
|
+
if (metadata.issuer !== this.#issuer) {
|
|
39
|
+
throw new Error('issuer metadata does not exactly match the configured issuer');
|
|
40
|
+
}
|
|
41
|
+
const authorizationEndpoint = this.#requiredEndpoint(metadata.authorization_endpoint, 'authorization endpoint');
|
|
42
|
+
const tokenEndpoint = this.#requiredEndpoint(metadata.token_endpoint, 'token endpoint');
|
|
43
|
+
const registrationEndpoint = this.#requiredEndpoint(metadata.registration_endpoint, 'registration endpoint');
|
|
44
|
+
const responseTypes = stringArray(metadata.response_types_supported);
|
|
45
|
+
if (!responseTypes.includes('code')) {
|
|
46
|
+
throw new Error('authorization server does not support the authorization-code flow');
|
|
47
|
+
}
|
|
48
|
+
const grantTypes = stringArray(metadata.grant_types_supported);
|
|
49
|
+
if (!grantTypes.includes('authorization_code') || !grantTypes.includes('refresh_token')) {
|
|
50
|
+
throw new Error('authorization server does not advertise code and refresh grants');
|
|
51
|
+
}
|
|
52
|
+
const challengeMethods = stringArray(metadata.code_challenge_methods_supported);
|
|
53
|
+
if (!challengeMethods.includes('S256')) {
|
|
54
|
+
throw new Error('authorization server does not support PKCE S256');
|
|
55
|
+
}
|
|
56
|
+
const tokenAuthMethods = stringArray(metadata.token_endpoint_auth_methods_supported);
|
|
57
|
+
if (!tokenAuthMethods.includes('none')) {
|
|
58
|
+
throw new Error('authorization server does not support public OAuth clients');
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
resource: this.#resource,
|
|
62
|
+
issuer: this.#issuer,
|
|
63
|
+
authorizationEndpoint,
|
|
64
|
+
tokenEndpoint,
|
|
65
|
+
registrationEndpoint,
|
|
66
|
+
scopes: canonicalScopes(stringArray(protectedResource.scopes_supported)),
|
|
67
|
+
authorizationResponseIssuerRequired: metadata.authorization_response_iss_parameter_supported === true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
firstError ??= safeError(error, 'authorization server discovery failed');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw firstError ?? new Error('authorization server discovery failed');
|
|
75
|
+
}
|
|
76
|
+
async register(discovery, scopes = discovery.scopes) {
|
|
77
|
+
this.#assertDiscovery(discovery);
|
|
78
|
+
const selectedScopes = canonicalScopes(scopes);
|
|
79
|
+
const body = {
|
|
80
|
+
application_type: 'native',
|
|
81
|
+
client_name: this.#clientName,
|
|
82
|
+
redirect_uris: [this.#redirectUri],
|
|
83
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
84
|
+
response_types: ['code'],
|
|
85
|
+
token_endpoint_auth_method: 'none',
|
|
86
|
+
...(selectedScopes.length > 0 ? { scope: selectedScopes.join(' ') } : {}),
|
|
87
|
+
};
|
|
88
|
+
const response = await this.#getJson(discovery.registrationEndpoint, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
91
|
+
body: JSON.stringify(body),
|
|
92
|
+
});
|
|
93
|
+
if (typeof response.client_id !== 'string' || response.client_id.length === 0) {
|
|
94
|
+
throw new Error('authorization server returned an invalid client registration');
|
|
95
|
+
}
|
|
96
|
+
if (response.token_endpoint_auth_method !== 'none') {
|
|
97
|
+
throw new Error('authorization server did not register a public client');
|
|
98
|
+
}
|
|
99
|
+
const registeredRedirects = stringArray(response.redirect_uris);
|
|
100
|
+
if (registeredRedirects.length > 0 && !registeredRedirects.includes(this.#redirectUri)) {
|
|
101
|
+
throw new Error('authorization server registered a different callback URL');
|
|
102
|
+
}
|
|
103
|
+
return { clientId: response.client_id, tokenEndpointAuthMethod: 'none' };
|
|
104
|
+
}
|
|
105
|
+
beginAuthorization(discovery, registration, scopes = discovery.scopes) {
|
|
106
|
+
this.#assertDiscovery(discovery);
|
|
107
|
+
assertPublicRegistration(registration);
|
|
108
|
+
const state = randomBytes(32).toString('base64url');
|
|
109
|
+
const codeVerifier = randomBytes(32).toString('base64url');
|
|
110
|
+
const selectedScopes = canonicalScopes(scopes);
|
|
111
|
+
const authorizationUrl = new URL(discovery.authorizationEndpoint);
|
|
112
|
+
authorizationUrl.searchParams.set('response_type', 'code');
|
|
113
|
+
authorizationUrl.searchParams.set('client_id', registration.clientId);
|
|
114
|
+
authorizationUrl.searchParams.set('redirect_uri', this.#redirectUri);
|
|
115
|
+
authorizationUrl.searchParams.set('state', state);
|
|
116
|
+
authorizationUrl.searchParams.set('code_challenge_method', 'S256');
|
|
117
|
+
authorizationUrl.searchParams.set('code_challenge', createHash('sha256').update(codeVerifier).digest('base64url'));
|
|
118
|
+
authorizationUrl.searchParams.set('resource', this.#resource);
|
|
119
|
+
if (selectedScopes.length > 0) {
|
|
120
|
+
authorizationUrl.searchParams.set('scope', selectedScopes.join(' '));
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
authorizationUrl: authorizationUrl.href,
|
|
124
|
+
state,
|
|
125
|
+
codeVerifier,
|
|
126
|
+
scopes: selectedScopes,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async exchangeCallback(discovery, registration, pending, callbackUrl) {
|
|
130
|
+
this.#assertDiscovery(discovery);
|
|
131
|
+
assertPublicRegistration(registration);
|
|
132
|
+
const callback = new URL(callbackUrl);
|
|
133
|
+
const expectedCallback = new URL(this.#redirectUri);
|
|
134
|
+
if (callback.origin !== expectedCallback.origin ||
|
|
135
|
+
callback.pathname !== expectedCallback.pathname) {
|
|
136
|
+
throw new Error('OAuth callback URL does not match the registered redirect');
|
|
137
|
+
}
|
|
138
|
+
if (!constantTimeStringEqual(callback.searchParams.get('state') ?? '', pending.state)) {
|
|
139
|
+
throw new Error('OAuth callback state is invalid');
|
|
140
|
+
}
|
|
141
|
+
const responseIssuer = callback.searchParams.get('iss');
|
|
142
|
+
if ((discovery.authorizationResponseIssuerRequired && responseIssuer === null) ||
|
|
143
|
+
(responseIssuer !== null && responseIssuer !== discovery.issuer)) {
|
|
144
|
+
throw new Error('OAuth callback issuer is invalid');
|
|
145
|
+
}
|
|
146
|
+
if (callback.searchParams.has('error')) {
|
|
147
|
+
throw new Error('authorization server denied the sign-in request');
|
|
148
|
+
}
|
|
149
|
+
const code = callback.searchParams.get('code');
|
|
150
|
+
if (!code)
|
|
151
|
+
throw new Error('OAuth callback did not include an authorization code');
|
|
152
|
+
return this.#requestTokens(discovery, new URLSearchParams({
|
|
153
|
+
grant_type: 'authorization_code',
|
|
154
|
+
code,
|
|
155
|
+
code_verifier: pending.codeVerifier,
|
|
156
|
+
redirect_uri: this.#redirectUri,
|
|
157
|
+
client_id: registration.clientId,
|
|
158
|
+
resource: this.#resource,
|
|
159
|
+
}), pending.scopes);
|
|
160
|
+
}
|
|
161
|
+
async refresh(discovery, registration, current) {
|
|
162
|
+
this.#assertDiscovery(discovery);
|
|
163
|
+
assertPublicRegistration(registration);
|
|
164
|
+
if (!current.refreshToken)
|
|
165
|
+
throw new Error('OAuth session does not have a refresh token');
|
|
166
|
+
const refreshed = await this.#requestTokens(discovery, new URLSearchParams({
|
|
167
|
+
grant_type: 'refresh_token',
|
|
168
|
+
refresh_token: current.refreshToken,
|
|
169
|
+
client_id: registration.clientId,
|
|
170
|
+
resource: this.#resource,
|
|
171
|
+
}), current.scope);
|
|
172
|
+
return {
|
|
173
|
+
...refreshed,
|
|
174
|
+
refreshToken: refreshed.refreshToken ?? current.refreshToken,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async #requestTokens(discovery, body, fallbackScope) {
|
|
178
|
+
const response = await this.#getJson(discovery.tokenEndpoint, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: {
|
|
181
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
182
|
+
accept: 'application/json',
|
|
183
|
+
},
|
|
184
|
+
body,
|
|
185
|
+
});
|
|
186
|
+
if (typeof response.access_token !== 'string' || response.access_token.length === 0) {
|
|
187
|
+
throw new Error('authorization server returned an invalid token response');
|
|
188
|
+
}
|
|
189
|
+
if (typeof response.token_type !== 'string' || response.token_type.toLowerCase() !== 'bearer') {
|
|
190
|
+
throw new Error('authorization server returned an unsupported token type');
|
|
191
|
+
}
|
|
192
|
+
const expiresIn = typeof response.expires_in === 'number' && Number.isFinite(response.expires_in)
|
|
193
|
+
? Math.max(0, response.expires_in)
|
|
194
|
+
: undefined;
|
|
195
|
+
const responseScopes = typeof response.scope === 'string' ? response.scope.split(/\s+/u) : fallbackScope;
|
|
196
|
+
return {
|
|
197
|
+
accessToken: response.access_token,
|
|
198
|
+
...(typeof response.refresh_token === 'string' && response.refresh_token.length > 0
|
|
199
|
+
? { refreshToken: response.refresh_token }
|
|
200
|
+
: {}),
|
|
201
|
+
tokenType: 'Bearer',
|
|
202
|
+
...(expiresIn !== undefined ? { expiresAt: Date.now() + expiresIn * 1000 } : {}),
|
|
203
|
+
scope: canonicalScopes(responseScopes),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async #getJson(url, init) {
|
|
207
|
+
let response;
|
|
208
|
+
const headers = new Headers(init?.headers);
|
|
209
|
+
if (!headers.has('accept'))
|
|
210
|
+
headers.set('accept', 'application/json');
|
|
211
|
+
try {
|
|
212
|
+
response = await this.#fetch(url, {
|
|
213
|
+
...init,
|
|
214
|
+
headers,
|
|
215
|
+
redirect: 'manual',
|
|
216
|
+
signal: init?.signal ?? AbortSignal.timeout(OAUTH_REQUEST_TIMEOUT_MS),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
throw new Error(`OAuth endpoint could not be reached: ${new URL(url).origin}`);
|
|
221
|
+
}
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
await response.body?.cancel();
|
|
224
|
+
throw new Error(`OAuth endpoint returned HTTP ${response.status}: ${new URL(url).origin}`);
|
|
225
|
+
}
|
|
226
|
+
const text = await readBoundedText(response);
|
|
227
|
+
try {
|
|
228
|
+
const parsed = JSON.parse(text);
|
|
229
|
+
if (!isJsonObject(parsed))
|
|
230
|
+
throw new Error('not an object');
|
|
231
|
+
return parsed;
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
throw new Error('OAuth endpoint returned invalid JSON');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
#requiredEndpoint(value, label) {
|
|
238
|
+
if (typeof value !== 'string')
|
|
239
|
+
throw new Error(`issuer metadata is missing ${label}`);
|
|
240
|
+
return this.#validateEndpoint(value, label);
|
|
241
|
+
}
|
|
242
|
+
#validateResource(value) {
|
|
243
|
+
const url = new URL(value);
|
|
244
|
+
if (url.username || url.password || url.hash) {
|
|
245
|
+
throw new Error('MCP OAuth resource must not contain credentials or a fragment');
|
|
246
|
+
}
|
|
247
|
+
if (url.protocol === 'https:' || (url.protocol === 'http:' && isLoopback(url.hostname))) {
|
|
248
|
+
return url.href;
|
|
249
|
+
}
|
|
250
|
+
throw new Error('MCP OAuth resource must use HTTPS or an HTTP loopback URL');
|
|
251
|
+
}
|
|
252
|
+
#validateRedirect(value) {
|
|
253
|
+
const url = new URL(value);
|
|
254
|
+
if (url.protocol !== 'http:' ||
|
|
255
|
+
!isLoopback(url.hostname) ||
|
|
256
|
+
url.username ||
|
|
257
|
+
url.password ||
|
|
258
|
+
url.hash) {
|
|
259
|
+
throw new Error('MCP OAuth redirect must use an HTTP loopback URL');
|
|
260
|
+
}
|
|
261
|
+
return url.href;
|
|
262
|
+
}
|
|
263
|
+
#validateEndpoint(value, label) {
|
|
264
|
+
const url = new URL(value);
|
|
265
|
+
if (url.username || url.password || url.hash) {
|
|
266
|
+
throw new Error(`${label} must not contain credentials or a fragment`);
|
|
267
|
+
}
|
|
268
|
+
if (url.protocol !== 'https:' &&
|
|
269
|
+
!(this.#allowInsecureLocalhost && url.protocol === 'http:' && isLoopback(url.hostname))) {
|
|
270
|
+
throw new Error(`${label} must use HTTPS`);
|
|
271
|
+
}
|
|
272
|
+
return url.href;
|
|
273
|
+
}
|
|
274
|
+
#validateIssuer(value) {
|
|
275
|
+
const url = new URL(value);
|
|
276
|
+
this.#validateEndpoint(value, 'authorization server issuer');
|
|
277
|
+
if (url.search || url.hash) {
|
|
278
|
+
throw new Error('authorization server issuer must not contain a query or fragment');
|
|
279
|
+
}
|
|
280
|
+
// The issuer identifier is compared byte-for-byte with metadata and the callback `iss`.
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
#assertDiscovery(discovery) {
|
|
284
|
+
if (discovery.resource !== this.#resource || discovery.issuer !== this.#issuer) {
|
|
285
|
+
throw new Error('OAuth discovery state belongs to a different resource or issuer');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** Parse only the challenge fields needed for reauthorization; ignore realms and metadata URLs. */
|
|
290
|
+
export function parseBearerChallenge(header) {
|
|
291
|
+
if (!header || !/^Bearer(?:\s|$)/iu.test(header))
|
|
292
|
+
return undefined;
|
|
293
|
+
const fields = new Map();
|
|
294
|
+
const raw = header.replace(/^Bearer\s*/iu, '');
|
|
295
|
+
const pattern = /([A-Za-z][A-Za-z0-9_-]*)=(?:"((?:\\.|[^"])*)"|([^,\s]+))/gu;
|
|
296
|
+
for (const match of raw.matchAll(pattern)) {
|
|
297
|
+
const key = match[1]?.toLowerCase();
|
|
298
|
+
const value = match[2]?.replace(/\\(["\\])/gu, '$1') ?? match[3];
|
|
299
|
+
if (key && value !== undefined)
|
|
300
|
+
fields.set(key, value);
|
|
301
|
+
}
|
|
302
|
+
const error = fields.get('error');
|
|
303
|
+
return {
|
|
304
|
+
...(error !== undefined ? { error } : {}),
|
|
305
|
+
scopes: canonicalScopes((fields.get('scope') ?? '').split(/\s+/u)),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
export function canonicalScopes(scopes) {
|
|
309
|
+
return [...new Set(scopes.map((scope) => scope.trim()).filter(Boolean))].sort();
|
|
310
|
+
}
|
|
311
|
+
function assertPublicRegistration(registration) {
|
|
312
|
+
if (registration.clientId.length === 0 || registration.tokenEndpointAuthMethod !== 'none') {
|
|
313
|
+
throw new Error('OAuth client registration is not a public client');
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function stringArray(value) {
|
|
317
|
+
return Array.isArray(value)
|
|
318
|
+
? value.filter((item) => typeof item === 'string')
|
|
319
|
+
: [];
|
|
320
|
+
}
|
|
321
|
+
function isJsonObject(value) {
|
|
322
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
323
|
+
}
|
|
324
|
+
async function readBoundedText(response) {
|
|
325
|
+
const declared = Number(response.headers.get('content-length'));
|
|
326
|
+
if (Number.isFinite(declared) && declared > MAX_OAUTH_JSON_BYTES) {
|
|
327
|
+
await response.body?.cancel().catch(() => undefined);
|
|
328
|
+
throw new Error('OAuth JSON response is too large');
|
|
329
|
+
}
|
|
330
|
+
if (response.body === null)
|
|
331
|
+
return '';
|
|
332
|
+
const reader = response.body.getReader();
|
|
333
|
+
const decoder = new TextDecoder();
|
|
334
|
+
const chunks = [];
|
|
335
|
+
let bytes = 0;
|
|
336
|
+
try {
|
|
337
|
+
while (true) {
|
|
338
|
+
const chunk = await reader.read();
|
|
339
|
+
if (chunk.done)
|
|
340
|
+
break;
|
|
341
|
+
bytes += chunk.value.byteLength;
|
|
342
|
+
if (bytes > MAX_OAUTH_JSON_BYTES) {
|
|
343
|
+
await reader.cancel().catch(() => undefined);
|
|
344
|
+
throw new Error('OAuth JSON response is too large');
|
|
345
|
+
}
|
|
346
|
+
chunks.push(decoder.decode(chunk.value, { stream: true }));
|
|
347
|
+
}
|
|
348
|
+
chunks.push(decoder.decode());
|
|
349
|
+
return chunks.join('');
|
|
350
|
+
}
|
|
351
|
+
finally {
|
|
352
|
+
reader.releaseLock();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function safeError(value, fallback) {
|
|
356
|
+
return value instanceof Error ? value : new Error(fallback);
|
|
357
|
+
}
|
|
358
|
+
function constantTimeStringEqual(left, right) {
|
|
359
|
+
const leftBytes = Buffer.from(left);
|
|
360
|
+
const rightBytes = Buffer.from(right);
|
|
361
|
+
return leftBytes.length === rightBytes.length && timingSafeEqual(leftBytes, rightBytes);
|
|
362
|
+
}
|
|
363
|
+
function isLoopback(hostname) {
|
|
364
|
+
const normalized = hostname.replace(/^\[|\]$/gu, '').toLowerCase();
|
|
365
|
+
return normalized === 'localhost' || normalized === '127.0.0.1' || normalized === '::1';
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=mcp-oauth-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-oauth-client.js","sourceRoot":"","sources":["../src/mcp-oauth-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OwnerTokenVerifier } from '@noodle-borg/transport-http';
|
|
2
|
+
import type { TenantAuthConfig, TenantBridgeAuthConfig } from './store.js';
|
|
3
|
+
export declare function createHostedCustomerVerifierFactory(rawFactory: (auth: TenantAuthConfig) => OwnerTokenVerifier, platformVerifier: OwnerTokenVerifier | undefined): (auth: TenantAuthConfig) => OwnerTokenVerifier;
|
|
4
|
+
/**
|
|
5
|
+
* Local Devtools accepts Firebase and Microsoft ID tokens directly because no hosted Noodle issuer exists
|
|
6
|
+
* in the author loop. The configured provider still verifies signature, issuer, and audience; this wrapper
|
|
7
|
+
* then binds the customer identity to the exact loopback MCP resource. Other bridge providers remain denied.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createLocalDevtoolsCustomerVerifierFactory(rawFactory: (auth: TenantAuthConfig) => OwnerTokenVerifier, options?: {
|
|
10
|
+
readonly allowedProviders?: readonly ('firebase' | 'microsoft')[];
|
|
11
|
+
readonly resolveBridgeAuth?: (auth: TenantBridgeAuthConfig, resource: string) => Promise<TenantBridgeAuthConfig>;
|
|
12
|
+
}): (auth: TenantAuthConfig) => OwnerTokenVerifier;
|
|
13
|
+
//# sourceMappingURL=customer-verifier-boundaries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customer-verifier-boundaries.d.ts","sourceRoot":"","sources":["../src/customer-verifier-boundaries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE3E,wBAAgB,mCAAmC,CACjD,UAAU,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,kBAAkB,EAC1D,gBAAgB,EAAE,kBAAkB,GAAG,SAAS,GAC/C,CAAC,IAAI,EAAE,gBAAgB,KAAK,kBAAkB,CAehD;AAED;;;;GAIG;AACH,wBAAgB,0CAA0C,CACxD,UAAU,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,kBAAkB,EAC1D,OAAO,GAAE;IACP,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC,UAAU,GAAG,WAAW,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAC3B,IAAI,EAAE,sBAAsB,EAC5B,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACjC,GACL,CAAC,IAAI,EAAE,gBAAgB,KAAK,kBAAkB,CA8BhD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export function createHostedCustomerVerifierFactory(rawFactory, platformVerifier) {
|
|
2
|
+
return (auth) => {
|
|
3
|
+
if (auth.kind !== 'bridge')
|
|
4
|
+
return rawFactory(auth);
|
|
5
|
+
if (platformVerifier === undefined)
|
|
6
|
+
return async () => null;
|
|
7
|
+
return async (token, resource) => {
|
|
8
|
+
const verification = await platformVerifier(token, resource);
|
|
9
|
+
if (verification?.caller.identityKind !== 'customer' ||
|
|
10
|
+
verification.caller.identityProvider !== auth.provider) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return { caller: verification.caller };
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Local Devtools accepts Firebase and Microsoft ID tokens directly because no hosted Noodle issuer exists
|
|
19
|
+
* in the author loop. The configured provider still verifies signature, issuer, and audience; this wrapper
|
|
20
|
+
* then binds the customer identity to the exact loopback MCP resource. Other bridge providers remain denied.
|
|
21
|
+
*/
|
|
22
|
+
export function createLocalDevtoolsCustomerVerifierFactory(rawFactory, options = {}) {
|
|
23
|
+
return (auth) => {
|
|
24
|
+
if (auth.kind !== 'bridge')
|
|
25
|
+
return rawFactory(auth);
|
|
26
|
+
if (auth.provider !== 'firebase' && auth.provider !== 'microsoft')
|
|
27
|
+
return async () => null;
|
|
28
|
+
if (options.allowedProviders !== undefined &&
|
|
29
|
+
!options.allowedProviders.includes(auth.provider)) {
|
|
30
|
+
return async () => null;
|
|
31
|
+
}
|
|
32
|
+
const resolveBridgeAuth = options.resolveBridgeAuth;
|
|
33
|
+
if (resolveBridgeAuth !== undefined) {
|
|
34
|
+
return async (token, resource) => {
|
|
35
|
+
if (resource === undefined || resource.length === 0)
|
|
36
|
+
return null;
|
|
37
|
+
let resolved;
|
|
38
|
+
try {
|
|
39
|
+
resolved = await resolveBridgeAuth(auth, resource);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (resolved.provider !== auth.provider)
|
|
45
|
+
return null;
|
|
46
|
+
return bindLocalBridgeIdentity(rawFactory(resolved), resolved, token, resource);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const verifyBridge = rawFactory(auth);
|
|
50
|
+
return async (token, resource) => {
|
|
51
|
+
if (resource === undefined || resource.length === 0)
|
|
52
|
+
return null;
|
|
53
|
+
return bindLocalBridgeIdentity(verifyBridge, auth, token, resource);
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function bindLocalBridgeIdentity(verifier, auth, token, resource) {
|
|
58
|
+
const providerAudience = auth.provider === 'firebase' ? (auth.projectId ?? '') : (auth.clientId ?? '');
|
|
59
|
+
const verification = await verifier(token, providerAudience);
|
|
60
|
+
if (verification === null)
|
|
61
|
+
return null;
|
|
62
|
+
return {
|
|
63
|
+
caller: {
|
|
64
|
+
...verification.caller,
|
|
65
|
+
audience: resource,
|
|
66
|
+
identityKind: 'customer',
|
|
67
|
+
identityProvider: auth.provider,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=customer-verifier-boundaries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customer-verifier-boundaries.js","sourceRoot":"","sources":["../src/customer-verifier-boundaries.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { type DnsLookup } from '@noodle-borg/connector-http';
|
|
|
2
2
|
import type { OwnerTokenVerifier } from '@noodle-borg/transport-http';
|
|
3
3
|
import { type JSONWebKeySet } from 'jose';
|
|
4
4
|
import type { TenantAuthClaimMap, TenantAuthConfig } from './store.js';
|
|
5
|
+
export { createHostedCustomerVerifierFactory, createLocalDevtoolsCustomerVerifierFactory, } from './customer-verifier-boundaries.js';
|
|
5
6
|
export interface CustomerVerifierFactoryOptions {
|
|
6
7
|
readonly fetchImpl?: typeof fetch;
|
|
7
8
|
readonly firebaseJwks?: JSONWebKeySet;
|
|
@@ -16,6 +17,5 @@ export interface CustomerVerifierFactoryOptions {
|
|
|
16
17
|
readonly allowInsecureLocalhost?: boolean;
|
|
17
18
|
}
|
|
18
19
|
export declare function createCustomerVerifierFactory(options?: CustomerVerifierFactoryOptions): (auth: TenantAuthConfig) => OwnerTokenVerifier;
|
|
19
|
-
export declare function createHostedCustomerVerifierFactory(rawFactory: (auth: TenantAuthConfig) => OwnerTokenVerifier, platformVerifier: OwnerTokenVerifier | undefined): (auth: TenantAuthConfig) => OwnerTokenVerifier;
|
|
20
20
|
export declare function createFirebaseIdTokenVerifier(projectId: string | undefined, options: CustomerVerifierFactoryOptions, claims?: TenantAuthClaimMap): Promise<OwnerTokenVerifier>;
|
|
21
21
|
//# sourceMappingURL=customer-verifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customer-verifier.d.ts","sourceRoot":"","sources":["../src/customer-verifier.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAIL,KAAK,aAAa,EAGnB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"customer-verifier.d.ts","sourceRoot":"","sources":["../src/customer-verifier.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAIL,KAAK,aAAa,EAGnB,MAAM,MAAM,CAAC;AAGd,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAwB,MAAM,YAAY,CAAC;AAE7F,OAAO,EACL,mCAAmC,EACnC,0CAA0C,GAC3C,MAAM,mCAAmC,CAAC;AAE3C,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAOD,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,8BAAmC,GAC3C,CAAC,IAAI,EAAE,gBAAgB,KAAK,kBAAkB,CA0FhD;AAoBD,wBAAsB,6BAA6B,CACjD,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,8BAA8B,EACvC,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CA0C7B"}
|
|
@@ -2,6 +2,8 @@ import { createJwtVerifier, discoverIssuerMetadata, projectMappedAuthorizationCl
|
|
|
2
2
|
import { guardedFetch, isPublicUnicast, needsGuard, } from '@noodle-borg/connector-http';
|
|
3
3
|
import { createLocalJWKSet, decodeJwt, importX509, jwtVerify, } from 'jose';
|
|
4
4
|
import { canonicalOAuthIdentityPreferences } from './oauth/identity-preferences.js';
|
|
5
|
+
import { createMicrosoftIdTokenVerifier, isConfiguredMicrosoftBridge } from './oauth/microsoft.js';
|
|
6
|
+
export { createHostedCustomerVerifierFactory, createLocalDevtoolsCustomerVerifierFactory, } from './customer-verifier-boundaries.js';
|
|
5
7
|
export function createCustomerVerifierFactory(options = {}) {
|
|
6
8
|
const cache = new Map();
|
|
7
9
|
const cacheTtlMs = options.cacheTtlMs ?? 300_000;
|
|
@@ -9,7 +11,6 @@ export function createCustomerVerifierFactory(options = {}) {
|
|
|
9
11
|
const verifyOidc = async (oidc, token, resource) => {
|
|
10
12
|
if (resource === undefined || resource.length === 0)
|
|
11
13
|
return null;
|
|
12
|
-
const now = Date.now();
|
|
13
14
|
const key = JSON.stringify([
|
|
14
15
|
oidc.issuer,
|
|
15
16
|
oidc.audience,
|
|
@@ -17,13 +18,7 @@ export function createCustomerVerifierFactory(options = {}) {
|
|
|
17
18
|
oidc.claims?.scopes ?? '',
|
|
18
19
|
routingClaimEntries(oidc),
|
|
19
20
|
]);
|
|
20
|
-
const
|
|
21
|
-
const verifier = cached !== undefined && cached.expiresAt > now
|
|
22
|
-
? cached.verifier
|
|
23
|
-
: discoverCustomerVerifier(oidc, options);
|
|
24
|
-
if (cached === undefined || cached.expiresAt <= now) {
|
|
25
|
-
cache.set(key, { expiresAt: now + cacheTtlMs, verifier });
|
|
26
|
-
}
|
|
21
|
+
const verifier = cachedCustomerVerifier(cache, key, cacheTtlMs, () => discoverCustomerVerifier(oidc, options));
|
|
27
22
|
try {
|
|
28
23
|
return await (await verifier)(token, resource);
|
|
29
24
|
}
|
|
@@ -48,23 +43,43 @@ export function createCustomerVerifierFactory(options = {}) {
|
|
|
48
43
|
};
|
|
49
44
|
}
|
|
50
45
|
if (auth.kind === 'bridge') {
|
|
46
|
+
if (auth.provider === 'microsoft') {
|
|
47
|
+
if (!isConfiguredMicrosoftBridge(auth))
|
|
48
|
+
return async () => null;
|
|
49
|
+
return async (token) => {
|
|
50
|
+
const key = [
|
|
51
|
+
'microsoft',
|
|
52
|
+
auth.tenantId,
|
|
53
|
+
auth.clientId,
|
|
54
|
+
JSON.stringify(auth.user ?? {}),
|
|
55
|
+
].join('\u0000');
|
|
56
|
+
const verifier = cachedCustomerVerifier(cache, key, cacheTtlMs, () => createMicrosoftIdTokenVerifier({
|
|
57
|
+
auth,
|
|
58
|
+
fetchImpl: options.fetchImpl ?? fetch,
|
|
59
|
+
}).then((verify) => async (candidate) => {
|
|
60
|
+
const identity = await verify(candidate);
|
|
61
|
+
return identity === null
|
|
62
|
+
? null
|
|
63
|
+
: { caller: { ...identity, audience: auth.clientId } };
|
|
64
|
+
}));
|
|
65
|
+
try {
|
|
66
|
+
return await (await verifier)(token, auth.clientId);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
51
73
|
if (auth.provider !== 'firebase')
|
|
52
74
|
return async () => null;
|
|
53
75
|
return async (token) => {
|
|
54
|
-
const now = Date.now();
|
|
55
76
|
const key = [
|
|
56
77
|
'firebase',
|
|
57
78
|
auth.projectId ?? '',
|
|
58
79
|
auth.user?.roles ?? '',
|
|
59
80
|
auth.user?.scopes ?? '',
|
|
60
81
|
].join('\u0000');
|
|
61
|
-
const
|
|
62
|
-
const verifier = cached !== undefined && cached.expiresAt > now
|
|
63
|
-
? cached.verifier
|
|
64
|
-
: createFirebaseIdTokenVerifier(auth.projectId, options, auth.user);
|
|
65
|
-
if (cached === undefined || cached.expiresAt <= now) {
|
|
66
|
-
cache.set(key, { expiresAt: now + cacheTtlMs, verifier });
|
|
67
|
-
}
|
|
82
|
+
const verifier = cachedCustomerVerifier(cache, key, cacheTtlMs, () => createFirebaseIdTokenVerifier(auth.projectId, options, auth.user));
|
|
68
83
|
try {
|
|
69
84
|
return await (await verifier)(token, auth.projectId ?? '');
|
|
70
85
|
}
|
|
@@ -76,21 +91,18 @@ export function createCustomerVerifierFactory(options = {}) {
|
|
|
76
91
|
return async (token, resource) => verifyOidc(auth, token, resource);
|
|
77
92
|
};
|
|
78
93
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return { caller: verification.caller };
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
+
function cachedCustomerVerifier(cache, key, cacheTtlMs, create) {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
const cached = cache.get(key);
|
|
97
|
+
if (cached !== undefined && cached.expiresAt > now)
|
|
98
|
+
return cached.verifier;
|
|
99
|
+
const verifier = create();
|
|
100
|
+
cache.set(key, { expiresAt: now + cacheTtlMs, verifier });
|
|
101
|
+
void verifier.catch(() => {
|
|
102
|
+
if (cache.get(key)?.verifier === verifier)
|
|
103
|
+
cache.delete(key);
|
|
104
|
+
});
|
|
105
|
+
return verifier;
|
|
94
106
|
}
|
|
95
107
|
export async function createFirebaseIdTokenVerifier(projectId, options, claims) {
|
|
96
108
|
if (projectId === undefined || projectId.length === 0)
|