@decocms/runtime 1.2.6 → 1.2.8
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/package.json +6 -1
- package/scripts/generate-json-schema.ts +8 -3
- package/src/bindings.ts +6 -0
- package/src/oauth.ts +29 -9
- package/src/tools.ts +34 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/runtime",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"check": "tsc --noEmit",
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=24.0.0"
|
|
33
33
|
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/decocms/mesh.git",
|
|
37
|
+
"directory": "packages/runtime"
|
|
38
|
+
},
|
|
34
39
|
"publishConfig": {
|
|
35
40
|
"access": "public"
|
|
36
41
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// heavily inspired by https://github.com/cloudflare/workers-sdk/blob/main/packages/wrangler/scripts/generate-json-schema.ts
|
|
2
2
|
import { writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { createGenerator } from "ts-json-schema-generator";
|
|
5
6
|
import type { Config, Schema } from "ts-json-schema-generator";
|
|
6
7
|
|
|
8
|
+
// Use standard ESM __dirname pattern for cross-runtime compatibility
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
7
11
|
const config: Config = {
|
|
8
|
-
path: join(
|
|
12
|
+
path: join(__dirname, "../src/wrangler.ts"),
|
|
13
|
+
tsconfig: join(__dirname, "../tsconfig.json"),
|
|
9
14
|
type: "WranglerConfig",
|
|
10
15
|
skipTypeCheck: true,
|
|
11
16
|
};
|
|
@@ -19,6 +24,6 @@ const schema = applyFormattingRules(
|
|
|
19
24
|
);
|
|
20
25
|
|
|
21
26
|
writeFileSync(
|
|
22
|
-
join(
|
|
27
|
+
join(__dirname, "../config-schema.json"),
|
|
23
28
|
JSON.stringify(schema, null, 2),
|
|
24
29
|
);
|
package/src/bindings.ts
CHANGED
|
@@ -108,6 +108,12 @@ export const proxyConnectionForId = (
|
|
|
108
108
|
headers ??= {};
|
|
109
109
|
headers.cookie = ctx.cookie;
|
|
110
110
|
}
|
|
111
|
+
|
|
112
|
+
if (ctx.token) {
|
|
113
|
+
headers ??= {};
|
|
114
|
+
headers["x-mesh-token"] = ctx.token;
|
|
115
|
+
}
|
|
116
|
+
|
|
111
117
|
return {
|
|
112
118
|
type: "HTTP",
|
|
113
119
|
url: new URL(`/mcp/${connectionId}`, ctx.meshUrl).href,
|
package/src/oauth.ts
CHANGED
|
@@ -285,24 +285,41 @@ export function createOAuthHandlers(oauth: OAuthConfig) {
|
|
|
285
285
|
const handleToken = async (req: Request): Promise<Response> => {
|
|
286
286
|
try {
|
|
287
287
|
const contentType = req.headers.get("content-type") ?? "";
|
|
288
|
-
let body: Record<string,
|
|
288
|
+
let body: Record<string, unknown>;
|
|
289
289
|
|
|
290
290
|
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
291
291
|
const formData = await req.formData();
|
|
292
|
-
body = Object.fromEntries(formData.entries())
|
|
292
|
+
body = Object.fromEntries(formData.entries());
|
|
293
293
|
} else {
|
|
294
|
-
|
|
294
|
+
const jsonBody = await req.json();
|
|
295
|
+
if (
|
|
296
|
+
typeof jsonBody !== "object" ||
|
|
297
|
+
jsonBody === null ||
|
|
298
|
+
Array.isArray(jsonBody)
|
|
299
|
+
) {
|
|
300
|
+
return Response.json(
|
|
301
|
+
{
|
|
302
|
+
error: "invalid_request",
|
|
303
|
+
error_description: "Request body must be a JSON object",
|
|
304
|
+
},
|
|
305
|
+
{ status: 400 },
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
body = jsonBody as Record<string, unknown>;
|
|
295
309
|
}
|
|
296
310
|
|
|
311
|
+
// Extract and validate OAuth parameters
|
|
312
|
+
// Per RFC 6749, all parameters should be strings, but we validate at runtime
|
|
297
313
|
const { code, code_verifier, grant_type, refresh_token } = body;
|
|
298
314
|
|
|
299
315
|
// Handle refresh_token grant type
|
|
300
316
|
if (grant_type === "refresh_token") {
|
|
301
|
-
if (!refresh_token) {
|
|
317
|
+
if (typeof refresh_token !== "string" || !refresh_token) {
|
|
302
318
|
return Response.json(
|
|
303
319
|
{
|
|
304
320
|
error: "invalid_request",
|
|
305
|
-
error_description:
|
|
321
|
+
error_description:
|
|
322
|
+
"refresh_token is required and must be a string",
|
|
306
323
|
},
|
|
307
324
|
{ status: 400 },
|
|
308
325
|
);
|
|
@@ -356,9 +373,12 @@ export function createOAuthHandlers(oauth: OAuthConfig) {
|
|
|
356
373
|
);
|
|
357
374
|
}
|
|
358
375
|
|
|
359
|
-
if (!code) {
|
|
376
|
+
if (typeof code !== "string" || !code) {
|
|
360
377
|
return Response.json(
|
|
361
|
-
{
|
|
378
|
+
{
|
|
379
|
+
error: "invalid_request",
|
|
380
|
+
error_description: "code is required and must be a string",
|
|
381
|
+
},
|
|
362
382
|
{ status: 400 },
|
|
363
383
|
);
|
|
364
384
|
}
|
|
@@ -377,11 +397,11 @@ export function createOAuthHandlers(oauth: OAuthConfig) {
|
|
|
377
397
|
|
|
378
398
|
// Verify PKCE if code challenge was provided
|
|
379
399
|
if (payload.codeChallenge) {
|
|
380
|
-
if (!code_verifier) {
|
|
400
|
+
if (typeof code_verifier !== "string" || !code_verifier) {
|
|
381
401
|
return Response.json(
|
|
382
402
|
{
|
|
383
403
|
error: "invalid_grant",
|
|
384
|
-
error_description: "code_verifier required",
|
|
404
|
+
error_description: "code_verifier required and must be a string",
|
|
385
405
|
},
|
|
386
406
|
{ status: 400 },
|
|
387
407
|
);
|
package/src/tools.ts
CHANGED
|
@@ -345,39 +345,70 @@ export interface OnChangeCallback<TState> {
|
|
|
345
345
|
scopes: string[];
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
/**
|
|
349
|
+
* OAuth 2.0 Token Exchange Parameters
|
|
350
|
+
* Parameters passed to exchangeCode() for token retrieval
|
|
351
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
|
|
352
|
+
*/
|
|
348
353
|
export interface OAuthParams {
|
|
354
|
+
/** REQUIRED - The authorization code received from the authorization server */
|
|
349
355
|
code: string;
|
|
356
|
+
/** OPTIONAL - PKCE code verifier (RFC 7636) */
|
|
350
357
|
code_verifier?: string;
|
|
358
|
+
/** OPTIONAL - Code challenge method: S256 (SHA-256) or plain */
|
|
351
359
|
code_challenge_method?: "S256" | "plain";
|
|
352
360
|
/**
|
|
353
|
-
* The redirect_uri used in the authorization request
|
|
354
|
-
*
|
|
361
|
+
* OPTIONAL - The redirect_uri used in the authorization request
|
|
362
|
+
* MUST be identical if included in the authorization request
|
|
355
363
|
*/
|
|
356
364
|
redirect_uri?: string;
|
|
357
365
|
}
|
|
358
366
|
|
|
367
|
+
/**
|
|
368
|
+
* OAuth 2.0 Token Response
|
|
369
|
+
* Response from the authorization server's token endpoint
|
|
370
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
|
|
371
|
+
*/
|
|
359
372
|
export interface OAuthTokenResponse {
|
|
373
|
+
/** REQUIRED - The access token issued by the authorization server */
|
|
360
374
|
access_token: string;
|
|
375
|
+
/** REQUIRED - Type of token (usually "Bearer" per RFC 6750) */
|
|
361
376
|
token_type: string;
|
|
377
|
+
/** RECOMMENDED - Lifetime in seconds of the access token */
|
|
362
378
|
expires_in?: number;
|
|
379
|
+
/** OPTIONAL - Used to obtain new access tokens (if applicable) */
|
|
363
380
|
refresh_token?: string;
|
|
381
|
+
/** OPTIONAL - Scope of the access token (if different from requested) */
|
|
364
382
|
scope?: string;
|
|
383
|
+
/** Additional provider-specific fields */
|
|
365
384
|
[key: string]: unknown;
|
|
366
385
|
}
|
|
367
386
|
|
|
368
387
|
/**
|
|
369
|
-
* OAuth
|
|
388
|
+
* OAuth 2.0 Client Metadata (Dynamic Client Registration)
|
|
389
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7591#section-2
|
|
390
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1
|
|
370
391
|
*/
|
|
371
392
|
export interface OAuthClient {
|
|
393
|
+
/** REQUIRED - OAuth 2.0 client identifier string */
|
|
372
394
|
client_id: string;
|
|
395
|
+
/** OPTIONAL - OAuth 2.0 client secret string (confidential clients) */
|
|
373
396
|
client_secret?: string;
|
|
397
|
+
/** OPTIONAL - Human-readable name of the client */
|
|
374
398
|
client_name?: string;
|
|
399
|
+
/** REQUIRED - Array of redirect URIs for use in redirect-based flows */
|
|
375
400
|
redirect_uris: string[];
|
|
401
|
+
/** OPTIONAL - Array of OAuth 2.0 grant types (e.g., "authorization_code", "refresh_token") */
|
|
376
402
|
grant_types?: string[];
|
|
403
|
+
/** OPTIONAL - Array of response types (e.g., "code", "token") */
|
|
377
404
|
response_types?: string[];
|
|
405
|
+
/** OPTIONAL - Authentication method for the token endpoint (e.g., "client_secret_basic", "none") */
|
|
378
406
|
token_endpoint_auth_method?: string;
|
|
407
|
+
/** OPTIONAL - Space-separated list of scope values */
|
|
379
408
|
scope?: string;
|
|
409
|
+
/** OPTIONAL - Time at which the client identifier was issued (Unix timestamp) */
|
|
380
410
|
client_id_issued_at?: number;
|
|
411
|
+
/** OPTIONAL - Time at which the client secret expires (Unix timestamp, 0 = never) */
|
|
381
412
|
client_secret_expires_at?: number;
|
|
382
413
|
}
|
|
383
414
|
|