@prenta/core 8.6.1 → 8.7.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/api/handlers.d.ts.map +1 -1
  3. package/dist/api/handlers.js +4 -0
  4. package/dist/api/handlers.js.map +1 -1
  5. package/dist/api/index.d.ts +2 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api/index.js +2 -0
  8. package/dist/api/index.js.map +1 -1
  9. package/dist/api/route-helpers/auth.d.ts +24 -2
  10. package/dist/api/route-helpers/auth.d.ts.map +1 -1
  11. package/dist/api/route-helpers/auth.js +93 -1
  12. package/dist/api/route-helpers/auth.js.map +1 -1
  13. package/dist/api/route-helpers.d.ts +1 -1
  14. package/dist/api/route-helpers.d.ts.map +1 -1
  15. package/dist/api/route-helpers.js +1 -1
  16. package/dist/api/route-helpers.js.map +1 -1
  17. package/dist/api/routes/ai-authoring.d.ts.map +1 -1
  18. package/dist/api/routes/ai-authoring.js +8 -3
  19. package/dist/api/routes/ai-authoring.js.map +1 -1
  20. package/dist/api/routes/forms.d.ts.map +1 -1
  21. package/dist/api/routes/forms.js +50 -34
  22. package/dist/api/routes/forms.js.map +1 -1
  23. package/dist/api/routes/mcp-oauth.d.ts +3 -0
  24. package/dist/api/routes/mcp-oauth.d.ts.map +1 -0
  25. package/dist/api/routes/mcp-oauth.js +143 -0
  26. package/dist/api/routes/mcp-oauth.js.map +1 -0
  27. package/dist/api/routes/preview.d.ts.map +1 -1
  28. package/dist/api/routes/preview.js +43 -2
  29. package/dist/api/routes/preview.js.map +1 -1
  30. package/dist/api/routes/privacy.d.ts +3 -0
  31. package/dist/api/routes/privacy.d.ts.map +1 -0
  32. package/dist/api/routes/privacy.js +44 -0
  33. package/dist/api/routes/privacy.js.map +1 -0
  34. package/dist/api/routes/scheduling.d.ts.map +1 -1
  35. package/dist/api/routes/scheduling.js +4 -1
  36. package/dist/api/routes/scheduling.js.map +1 -1
  37. package/dist/api/routes/seo/handlers/audits.d.ts.map +1 -1
  38. package/dist/api/routes/seo/handlers/audits.js +3 -3
  39. package/dist/api/routes/seo/handlers/audits.js.map +1 -1
  40. package/dist/api/routes/seo/handlers/content.d.ts.map +1 -1
  41. package/dist/api/routes/seo/handlers/content.js +2 -2
  42. package/dist/api/routes/seo/handlers/content.js.map +1 -1
  43. package/dist/api/routes/seo/handlers/issue-helpers.d.ts.map +1 -1
  44. package/dist/api/routes/seo/handlers/issue-helpers.js +2 -2
  45. package/dist/api/routes/seo/handlers/issue-helpers.js.map +1 -1
  46. package/dist/api/routes/seo/handlers/issues-fixes.js +3 -3
  47. package/dist/api/routes/seo/handlers/issues-fixes.js.map +1 -1
  48. package/dist/api/routes/seo/handlers/links.d.ts.map +1 -1
  49. package/dist/api/routes/seo/handlers/links.js +6 -6
  50. package/dist/api/routes/seo/handlers/links.js.map +1 -1
  51. package/dist/api/routes/seo/handlers/proposals.js +2 -2
  52. package/dist/api/routes/seo/handlers/proposals.js.map +1 -1
  53. package/dist/api/routes/seo-public.d.ts.map +1 -1
  54. package/dist/api/routes/seo-public.js +9 -0
  55. package/dist/api/routes/seo-public.js.map +1 -1
  56. package/dist/db/model-types.d.ts +40 -0
  57. package/dist/db/model-types.d.ts.map +1 -1
  58. package/dist/forms/delivery.d.ts.map +1 -1
  59. package/dist/forms/delivery.js +16 -10
  60. package/dist/forms/delivery.js.map +1 -1
  61. package/dist/forms/encryption.d.ts +4 -2
  62. package/dist/forms/encryption.d.ts.map +1 -1
  63. package/dist/forms/encryption.js +6 -4
  64. package/dist/forms/encryption.js.map +1 -1
  65. package/dist/forms/submission.d.ts.map +1 -1
  66. package/dist/forms/submission.js +17 -10
  67. package/dist/forms/submission.js.map +1 -1
  68. package/dist/generated/browser.d.ts +19 -0
  69. package/dist/generated/client.d.ts +19 -0
  70. package/dist/generated/internal/class.d.ts +33 -0
  71. package/dist/generated/internal/class.js +4 -4
  72. package/dist/generated/internal/prismaNamespace.d.ts +296 -1
  73. package/dist/generated/internal/prismaNamespace.js +65 -1
  74. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +70 -0
  75. package/dist/generated/internal/prismaNamespaceBrowser.js +65 -1
  76. package/dist/generated/models/McpOauthGrant.d.ts +1577 -0
  77. package/dist/generated/models/McpOauthGrant.js +1 -0
  78. package/dist/generated/models/McpOauthRefreshReplay.d.ts +957 -0
  79. package/dist/generated/models/McpOauthRefreshReplay.js +1 -0
  80. package/dist/generated/models/McpOauthTransaction.d.ts +1153 -0
  81. package/dist/generated/models/McpOauthTransaction.js +1 -0
  82. package/dist/generated/models/User.d.ts +279 -0
  83. package/dist/generated/models.d.ts +3 -0
  84. package/dist/mcp/cimd.d.ts +12 -0
  85. package/dist/mcp/cimd.d.ts.map +1 -0
  86. package/dist/mcp/cimd.js +67 -0
  87. package/dist/mcp/cimd.js.map +1 -0
  88. package/dist/mcp/gate.d.ts +11 -0
  89. package/dist/mcp/gate.d.ts.map +1 -0
  90. package/dist/mcp/gate.js +75 -0
  91. package/dist/mcp/gate.js.map +1 -0
  92. package/dist/mcp/memory-grant-store.d.ts +5 -0
  93. package/dist/mcp/memory-grant-store.d.ts.map +1 -0
  94. package/dist/mcp/memory-grant-store.js +44 -0
  95. package/dist/mcp/memory-grant-store.js.map +1 -0
  96. package/dist/mcp/oauth-grants.d.ts +83 -0
  97. package/dist/mcp/oauth-grants.d.ts.map +1 -0
  98. package/dist/mcp/oauth-grants.js +129 -0
  99. package/dist/mcp/oauth-grants.js.map +1 -0
  100. package/dist/mcp/oauth-http.d.ts +43 -0
  101. package/dist/mcp/oauth-http.d.ts.map +1 -0
  102. package/dist/mcp/oauth-http.js +243 -0
  103. package/dist/mcp/oauth-http.js.map +1 -0
  104. package/dist/mcp/oauth-token.d.ts +43 -0
  105. package/dist/mcp/oauth-token.d.ts.map +1 -0
  106. package/dist/mcp/oauth-token.js +78 -0
  107. package/dist/mcp/oauth-token.js.map +1 -0
  108. package/dist/mcp/prisma-store.d.ts +5 -0
  109. package/dist/mcp/prisma-store.d.ts.map +1 -0
  110. package/dist/mcp/prisma-store.js +69 -0
  111. package/dist/mcp/prisma-store.js.map +1 -0
  112. package/dist/mcp/public.d.ts +5 -0
  113. package/dist/mcp/public.d.ts.map +1 -0
  114. package/dist/mcp/public.js +30 -0
  115. package/dist/mcp/public.js.map +1 -0
  116. package/dist/mcp/runtime.d.ts +4 -0
  117. package/dist/mcp/runtime.d.ts.map +1 -0
  118. package/dist/mcp/runtime.js +46 -0
  119. package/dist/mcp/runtime.js.map +1 -0
  120. package/dist/mcp/transactions.d.ts +18 -0
  121. package/dist/mcp/transactions.d.ts.map +1 -0
  122. package/dist/mcp/transactions.js +15 -0
  123. package/dist/mcp/transactions.js.map +1 -0
  124. package/dist/privacy/phi.d.ts +5 -0
  125. package/dist/privacy/phi.d.ts.map +1 -0
  126. package/dist/privacy/phi.js +69 -0
  127. package/dist/privacy/phi.js.map +1 -0
  128. package/dist/seo/document-path.d.ts +13 -0
  129. package/dist/seo/document-path.d.ts.map +1 -1
  130. package/dist/seo/document-path.js +10 -0
  131. package/dist/seo/document-path.js.map +1 -1
  132. package/package.json +3 -3
  133. package/prisma/migrations/0023_mcp_oauth/migration.sql +40 -0
  134. package/prisma/migrations/0024_mcp_oauth_transactions/migration.sql +16 -0
  135. package/prisma/schema.prisma +58 -0
@@ -38,4 +38,7 @@ export type * from './models/DocumentCRDT.js';
38
38
  export type * from './models/DocumentComment.js';
39
39
  export type * from './models/DocumentNotification.js';
40
40
  export type * from './models/Job.js';
41
+ export type * from './models/McpOauthGrant.js';
42
+ export type * from './models/McpOauthRefreshReplay.js';
43
+ export type * from './models/McpOauthTransaction.js';
41
44
  export type * from './commonInputTypes.js';
@@ -0,0 +1,12 @@
1
+ import { safeFetch } from '../security/safe-fetch.js';
2
+ export type ClientMetadata = {
3
+ clientId: string;
4
+ clientName: string;
5
+ redirectUris: string[];
6
+ };
7
+ /** HTTPS client ids only. IP literals that are private or malformed are rejected before DNS. */
8
+ export declare function validateClientIdUrl(clientId: string): string | null;
9
+ export declare function fetchClientMetadata(clientId: string, fetchImpl?: typeof safeFetch, now?: number): Promise<ClientMetadata | {
10
+ error: string;
11
+ }>;
12
+ //# sourceMappingURL=cimd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cimd.d.ts","sourceRoot":"","sources":["../../src/mcp/cimd.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAoB,MAAM,2BAA2B,CAAA;AAEvE,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAMD,gGAAgG;AAChG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWnE;AAED,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,SAAqB,EACvC,GAAG,SAAa,GACf,OAAO,CAAC,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA0C7C"}
@@ -0,0 +1,67 @@
1
+ import { canonicalizeHostname, isPrivateAddress } from '../security/ip-canon.js';
2
+ import { safeFetch, SsrfBlockedError } from '../security/safe-fetch.js';
3
+ const cache = new Map();
4
+ const CACHE_MS = 5 * 60 * 1000;
5
+ const MAX_BYTES = 64 * 1024;
6
+ /** HTTPS client ids only. IP literals that are private or malformed are rejected before DNS. */
7
+ export function validateClientIdUrl(clientId) {
8
+ let url;
9
+ try {
10
+ url = new URL(clientId);
11
+ }
12
+ catch {
13
+ return 'invalid_client';
14
+ }
15
+ if (url.protocol !== 'https:' || url.username || url.password)
16
+ return 'invalid_client';
17
+ const host = canonicalizeHostname(url.hostname);
18
+ if (isPrivateAddress(host))
19
+ return 'invalid_client';
20
+ return null;
21
+ }
22
+ export async function fetchClientMetadata(clientId, fetchImpl = safeFetch, now = Date.now()) {
23
+ if (validateClientIdUrl(clientId))
24
+ return { error: 'invalid_client' };
25
+ const hit = cache.get(clientId);
26
+ if (hit && now - hit.at < CACHE_MS)
27
+ return hit.meta;
28
+ try {
29
+ const res = await fetchImpl(clientId, {
30
+ method: 'GET',
31
+ redirect: 'manual',
32
+ followRedirects: false,
33
+ timeoutMs: 5000,
34
+ requireDnsCheck: true,
35
+ headers: { accept: 'application/json' },
36
+ });
37
+ if (res.status >= 300 && res.status < 400)
38
+ return { error: 'invalid_client' };
39
+ if (!res.ok)
40
+ return { error: 'invalid_client' };
41
+ const text = await res.text();
42
+ if (text.length > MAX_BYTES)
43
+ return { error: 'invalid_client' };
44
+ const parsed = JSON.parse(text);
45
+ if (parsed.client_id !== clientId)
46
+ return { error: 'invalid_client' };
47
+ if (!Array.isArray(parsed.redirect_uris) || parsed.redirect_uris.length === 0) {
48
+ return { error: 'invalid_client' };
49
+ }
50
+ const redirectUris = parsed.redirect_uris.filter((item) => typeof item === 'string');
51
+ if (redirectUris.length !== parsed.redirect_uris.length)
52
+ return { error: 'invalid_client' };
53
+ const meta = {
54
+ clientId,
55
+ clientName: typeof parsed.client_name === 'string' ? parsed.client_name.slice(0, 120) : clientId,
56
+ redirectUris,
57
+ };
58
+ cache.set(clientId, { at: now, meta });
59
+ return meta;
60
+ }
61
+ catch (err) {
62
+ if (err instanceof SsrfBlockedError)
63
+ return { error: 'invalid_client' };
64
+ return { error: 'invalid_client' };
65
+ }
66
+ }
67
+ //# sourceMappingURL=cimd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cimd.js","sourceRoot":"","sources":["../../src/mcp/cimd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type McpCredential = {
2
+ credential: string;
3
+ baseUrl: string;
4
+ };
5
+ /**
6
+ * Accept an OAuth access token or an `act_sk_` key on `/mcp`.
7
+ * OAuth tokens are exchanged for an internal assertion. The client JWT is
8
+ * not forwarded to the CMS API.
9
+ */
10
+ export declare function authorizeMcpRequest(request: Request): Promise<McpCredential | Response>;
11
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/mcp/gate.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,aAAa,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAgBnE;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,QAAQ,CAAC,CAsD7F"}
@@ -0,0 +1,75 @@
1
+ import { looksLikeApiKey } from '../security/api-key-enhanced.js';
2
+ import { canonicalMcpResource } from './oauth-grants.js';
3
+ import { mcpBearerStatus } from './oauth-http.js';
4
+ import { signMcpInternalToken } from './oauth-token.js';
5
+ import { mcpOauthRuntime, mcpSiteUrl } from './runtime.js';
6
+ function unauthorized(site) {
7
+ const metadata = `${site}/.well-known/oauth-protected-resource/mcp`;
8
+ return Response.json({ error: 'unauthorized' }, {
9
+ status: 401,
10
+ headers: {
11
+ 'WWW-Authenticate': `Bearer resource_metadata="${metadata}", scope="content"`,
12
+ 'cache-control': 'no-store',
13
+ },
14
+ });
15
+ }
16
+ /**
17
+ * Accept an OAuth access token or an `act_sk_` key on `/mcp`.
18
+ * OAuth tokens are exchanged for an internal assertion. The client JWT is
19
+ * not forwarded to the CMS API.
20
+ */
21
+ export async function authorizeMcpRequest(request) {
22
+ const url = new URL(request.url);
23
+ if (url.searchParams.has('api_key') ||
24
+ url.searchParams.has('token') ||
25
+ url.searchParams.has('access_token')) {
26
+ return Response.json({ error: 'invalid_request' }, { status: 400 });
27
+ }
28
+ const runtime = mcpOauthRuntime();
29
+ const site = runtime?.siteUrl ?? mcpSiteUrl();
30
+ if (!runtime || !site) {
31
+ return Response.json({ error: 'server_error' }, { status: 500 });
32
+ }
33
+ const origin = request.headers.get('origin');
34
+ if (origin) {
35
+ let presented;
36
+ try {
37
+ presented = new URL(origin).origin;
38
+ }
39
+ catch {
40
+ return Response.json({ error: 'forbidden' }, { status: 403 });
41
+ }
42
+ if (presented !== new URL(site).origin) {
43
+ return Response.json({ error: 'forbidden' }, { status: 403 });
44
+ }
45
+ }
46
+ const bearer = request.headers.get('authorization');
47
+ const token = bearer?.toLowerCase().startsWith('bearer ')
48
+ ? bearer.slice(bearer.indexOf(' ') + 1).trim()
49
+ : '';
50
+ const apiKey = request.headers.get('x-api-key')?.trim() ?? '';
51
+ const presented = token || apiKey;
52
+ if (!presented)
53
+ return unauthorized(site);
54
+ if (looksLikeApiKey(presented))
55
+ return { credential: presented, baseUrl: site };
56
+ const status = await mcpBearerStatus(presented, runtime);
57
+ if (status === 'insufficient_scope') {
58
+ return Response.json({ error: 'insufficient_scope' }, {
59
+ status: 403,
60
+ headers: {
61
+ 'WWW-Authenticate': 'Bearer error="insufficient_scope", scope="content"',
62
+ 'cache-control': 'no-store',
63
+ },
64
+ });
65
+ }
66
+ if (!status)
67
+ return unauthorized(site);
68
+ const credential = await signMcpInternalToken({
69
+ secret: runtime.secret,
70
+ userId: status.userId,
71
+ role: status.role,
72
+ });
73
+ return { credential, baseUrl: canonicalMcpResource(site).replace(/\/mcp$/, '') };
74
+ }
75
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/mcp/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { GrantRecord, OauthGrantStore } from './oauth-grants.js';
2
+ export declare function createMemoryGrantStore(): OauthGrantStore & {
3
+ rows: GrantRecord[];
4
+ };
5
+ //# sourceMappingURL=memory-grant-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-grant-store.d.ts","sourceRoot":"","sources":["../../src/mcp/memory-grant-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAErE,wBAAgB,sBAAsB,IAAI,eAAe,GAAG;IAAE,IAAI,EAAE,WAAW,EAAE,CAAA;CAAE,CAuClF"}
@@ -0,0 +1,44 @@
1
+ export function createMemoryGrantStore() {
2
+ const rows = [];
3
+ const replays = [];
4
+ return {
5
+ rows,
6
+ async insert(row) {
7
+ rows.push({ ...row });
8
+ },
9
+ async findByCodeHash(hash) {
10
+ return rows.find((row) => row.codeHash === hash) ?? null;
11
+ },
12
+ async save(row) {
13
+ const index = rows.findIndex((item) => item.id === row.id);
14
+ if (index >= 0)
15
+ rows[index] = { ...row };
16
+ },
17
+ async findByRefreshHash(hash) {
18
+ return rows.find((row) => row.refreshHash === hash && !row.revokedAt) ?? null;
19
+ },
20
+ async findReplay(hash) {
21
+ return replays.find((item) => item.hash === hash) ?? null;
22
+ },
23
+ async rememberReplay(hash, familyId) {
24
+ replays.push({ hash, familyId });
25
+ },
26
+ async revokeFamily(familyId, now) {
27
+ for (const row of rows) {
28
+ if (row.familyId === familyId)
29
+ row.revokedAt = now;
30
+ }
31
+ },
32
+ async listForUser(userId) {
33
+ return rows.filter((row) => row.userId === userId);
34
+ },
35
+ async revokeById(id, userId, now) {
36
+ const row = rows.find((item) => item.id === id && item.userId === userId);
37
+ if (!row || row.revokedAt)
38
+ return false;
39
+ row.revokedAt = now;
40
+ return true;
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=memory-grant-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-grant-store.js","sourceRoot":"","sources":["../../src/mcp/memory-grant-store.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,sBAAsB;IACpC,MAAM,IAAI,GAAkB,EAAE,CAAA;IAC9B,MAAM,OAAO,GAAyC,EAAE,CAAA;IACxD,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,MAAM,CAAC,GAAG;YACd,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAA;QACvB,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,IAAI;YACvB,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,IAAI,IAAI,CAAA;QAC1D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;YAC1D,IAAI,KAAK,IAAI,CAAC;gBAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAA;QAC1C,CAAC;QACD,KAAK,CAAC,iBAAiB,CAAC,IAAI;YAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAA;QAC/E,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,IAAI;YACnB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAA;QAC3D,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ;YACjC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAClC,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG;YAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;oBAAE,GAAG,CAAC,SAAS,GAAG,GAAG,CAAA;YACpD,CAAC;QACH,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,MAAM;YACtB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAA;QACpD,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG;YAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAA;YACzE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAA;YACvC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAA;YACnB,OAAO,IAAI,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,83 @@
1
+ export type GrantRecord = {
2
+ id: string;
3
+ userId: string;
4
+ clientId: string;
5
+ redirectUri: string;
6
+ resource: string;
7
+ scope: string;
8
+ codeChallenge: string;
9
+ codeHash: string | null;
10
+ codeExpiresAt: Date | null;
11
+ codeConsumedAt: Date | null;
12
+ refreshHash: string | null;
13
+ refreshExpiresAt: Date | null;
14
+ familyId: string;
15
+ revokedAt: Date | null;
16
+ };
17
+ export interface OauthGrantStore {
18
+ insert(row: GrantRecord): Promise<void>;
19
+ findByCodeHash(hash: string): Promise<GrantRecord | null>;
20
+ save(row: GrantRecord): Promise<void>;
21
+ findByRefreshHash(hash: string): Promise<GrantRecord | null>;
22
+ findReplay(hash: string): Promise<{
23
+ familyId: string;
24
+ } | null>;
25
+ rememberReplay(hash: string, familyId: string): Promise<void>;
26
+ revokeFamily(familyId: string, now: Date): Promise<void>;
27
+ listForUser(userId: string): Promise<GrantRecord[]>;
28
+ revokeById(id: string, userId: string, now: Date): Promise<boolean>;
29
+ }
30
+ export declare function s256Challenge(verifier: string): string;
31
+ export declare function pkceMatches(verifier: string, challenge: string): boolean;
32
+ export declare function newSecret(): string;
33
+ export declare function canonicalMcpResource(siteUrl: string): string;
34
+ /** Exact redirect match. Loopback HTTP ignores the port (RFC 8252). */
35
+ export declare function redirectAllowed(requested: string, registered: string[]): boolean;
36
+ export declare function issueAuthorizationCode(store: OauthGrantStore, input: {
37
+ userId: string;
38
+ clientId: string;
39
+ redirectUri: string;
40
+ resource: string;
41
+ scope: string;
42
+ codeChallenge: string;
43
+ now?: Date;
44
+ }): Promise<{
45
+ code: string;
46
+ grant: GrantRecord;
47
+ }>;
48
+ export type CodeExchange = {
49
+ ok: true;
50
+ grant: GrantRecord;
51
+ refreshToken: string;
52
+ } | {
53
+ ok: false;
54
+ error: 'invalid_grant';
55
+ };
56
+ /**
57
+ * Single-use code exchange. A replay revokes the grant family.
58
+ * `plain` PKCE is not accepted: the caller must pass an S256 verifier.
59
+ */
60
+ export declare function exchangeAuthorizationCode(store: OauthGrantStore, input: {
61
+ code: string;
62
+ verifier: string;
63
+ clientId: string;
64
+ redirectUri: string;
65
+ resource: string;
66
+ now?: Date;
67
+ }): Promise<CodeExchange>;
68
+ export type RefreshExchange = {
69
+ ok: true;
70
+ grant: GrantRecord;
71
+ refreshToken: string;
72
+ } | {
73
+ ok: false;
74
+ error: 'invalid_grant';
75
+ };
76
+ /** Rotate a refresh token. Reuse of a replaced token revokes the family. */
77
+ export declare function rotateRefreshToken(store: OauthGrantStore, input: {
78
+ refreshToken: string;
79
+ clientId: string;
80
+ resource: string;
81
+ now?: Date;
82
+ }): Promise<RefreshExchange>;
83
+ //# sourceMappingURL=oauth-grants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-grants.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth-grants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ const CODE_TTL_MS = 60_000;
3
+ const REFRESH_TTL_MS = 30 * 24 * 60 * 60 * 1000;
4
+ function hashSecret(value) {
5
+ return createHash('sha256').update(value).digest('hex');
6
+ }
7
+ export function s256Challenge(verifier) {
8
+ return createHash('sha256').update(verifier).digest('base64url');
9
+ }
10
+ export function pkceMatches(verifier, challenge) {
11
+ const actual = s256Challenge(verifier);
12
+ const a = Buffer.from(actual);
13
+ const b = Buffer.from(challenge);
14
+ if (a.length !== b.length)
15
+ return false;
16
+ return timingSafeEqual(a, b);
17
+ }
18
+ export function newSecret() {
19
+ return randomBytes(32).toString('base64url');
20
+ }
21
+ export function canonicalMcpResource(siteUrl) {
22
+ return `${siteUrl.replace(/\/+$/, '')}/mcp`;
23
+ }
24
+ function isLoopbackRedirect(value) {
25
+ try {
26
+ const url = new URL(value);
27
+ return (url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1'));
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /** Exact redirect match. Loopback HTTP ignores the port (RFC 8252). */
34
+ export function redirectAllowed(requested, registered) {
35
+ let url;
36
+ try {
37
+ url = new URL(requested);
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ if (isLoopbackRedirect(requested)) {
43
+ return registered.some((entry) => {
44
+ if (!isLoopbackRedirect(entry))
45
+ return false;
46
+ const reg = new URL(entry);
47
+ return (reg.hostname === url.hostname && reg.pathname === url.pathname && reg.search === url.search);
48
+ });
49
+ }
50
+ return registered.includes(requested);
51
+ }
52
+ export async function issueAuthorizationCode(store, input) {
53
+ const now = input.now ?? new Date();
54
+ const code = newSecret();
55
+ const grant = {
56
+ id: newSecret(),
57
+ userId: input.userId,
58
+ clientId: input.clientId,
59
+ redirectUri: input.redirectUri,
60
+ resource: input.resource,
61
+ scope: input.scope,
62
+ codeChallenge: input.codeChallenge,
63
+ codeHash: hashSecret(code),
64
+ codeExpiresAt: new Date(now.getTime() + CODE_TTL_MS),
65
+ codeConsumedAt: null,
66
+ refreshHash: null,
67
+ refreshExpiresAt: null,
68
+ familyId: newSecret(),
69
+ revokedAt: null,
70
+ };
71
+ await store.insert(grant);
72
+ return { code, grant };
73
+ }
74
+ /**
75
+ * Single-use code exchange. A replay revokes the grant family.
76
+ * `plain` PKCE is not accepted: the caller must pass an S256 verifier.
77
+ */
78
+ export async function exchangeAuthorizationCode(store, input) {
79
+ const now = input.now ?? new Date();
80
+ const hash = hashSecret(input.code);
81
+ const grant = await store.findByCodeHash(hash);
82
+ if (!grant || grant.revokedAt)
83
+ return { ok: false, error: 'invalid_grant' };
84
+ if (grant.codeConsumedAt) {
85
+ await store.revokeFamily(grant.familyId, now);
86
+ return { ok: false, error: 'invalid_grant' };
87
+ }
88
+ if (!grant.codeExpiresAt || grant.codeExpiresAt.getTime() <= now.getTime()) {
89
+ return { ok: false, error: 'invalid_grant' };
90
+ }
91
+ if (grant.clientId !== input.clientId ||
92
+ grant.redirectUri !== input.redirectUri ||
93
+ grant.resource !== input.resource ||
94
+ !pkceMatches(input.verifier, grant.codeChallenge)) {
95
+ return { ok: false, error: 'invalid_grant' };
96
+ }
97
+ const refreshToken = newSecret();
98
+ grant.codeConsumedAt = now;
99
+ grant.refreshHash = hashSecret(refreshToken);
100
+ grant.refreshExpiresAt = new Date(now.getTime() + REFRESH_TTL_MS);
101
+ await store.save(grant);
102
+ return { ok: true, grant, refreshToken };
103
+ }
104
+ /** Rotate a refresh token. Reuse of a replaced token revokes the family. */
105
+ export async function rotateRefreshToken(store, input) {
106
+ const now = input.now ?? new Date();
107
+ const hash = hashSecret(input.refreshToken);
108
+ const replay = await store.findReplay(hash);
109
+ if (replay) {
110
+ await store.revokeFamily(replay.familyId, now);
111
+ return { ok: false, error: 'invalid_grant' };
112
+ }
113
+ const grant = await store.findByRefreshHash(hash);
114
+ if (!grant || grant.revokedAt)
115
+ return { ok: false, error: 'invalid_grant' };
116
+ if (grant.clientId !== input.clientId || grant.resource !== input.resource) {
117
+ return { ok: false, error: 'invalid_grant' };
118
+ }
119
+ if (!grant.refreshExpiresAt || grant.refreshExpiresAt.getTime() <= now.getTime()) {
120
+ return { ok: false, error: 'invalid_grant' };
121
+ }
122
+ const next = newSecret();
123
+ await store.rememberReplay(hash, grant.familyId);
124
+ grant.refreshHash = hashSecret(next);
125
+ grant.refreshExpiresAt = new Date(now.getTime() + REFRESH_TTL_MS);
126
+ await store.save(grant);
127
+ return { ok: true, grant, refreshToken: next };
128
+ }
129
+ //# sourceMappingURL=oauth-grants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-grants.js","sourceRoot":"","sources":["../../src/mcp/oauth-grants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { type ClientMetadata } from './cimd.js';
2
+ import { type OauthGrantStore } from './oauth-grants.js';
3
+ import type { OauthTransaction, TransactionStore } from './transactions.js';
4
+ export type OauthUser = {
5
+ id: string;
6
+ role: string;
7
+ active: boolean;
8
+ };
9
+ export type McpOauthRuntime = {
10
+ siteUrl: string;
11
+ adminPath: string;
12
+ secret: string;
13
+ grants: OauthGrantStore;
14
+ transactions: TransactionStore;
15
+ loadUser: (userId: string) => Promise<OauthUser | null>;
16
+ fetchClient?: (clientId: string) => Promise<ClientMetadata | {
17
+ error: string;
18
+ }>;
19
+ now?: () => Date;
20
+ allowAttempt?: (key: string) => boolean;
21
+ };
22
+ export declare function mcpResourceMetadata(runtime: McpOauthRuntime): Response;
23
+ export declare function mcpAuthorizationServerMetadata(runtime: McpOauthRuntime): Response;
24
+ export declare function beginAuthorization(request: Request, runtime: McpOauthRuntime): Promise<Response>;
25
+ export declare function readConsentTransaction(txnId: string, runtime: McpOauthRuntime): Promise<{
26
+ transaction: OauthTransaction;
27
+ redirectHost: string;
28
+ } | null>;
29
+ export declare function completeConsent(input: {
30
+ txnId: string;
31
+ userId: string;
32
+ allow: boolean;
33
+ }, runtime: McpOauthRuntime): Promise<{
34
+ redirectUrl: string;
35
+ } | {
36
+ error: string;
37
+ }>;
38
+ export declare function handleTokenRequest(request: Request, runtime: McpOauthRuntime): Promise<Response>;
39
+ export declare function mcpBearerStatus(token: string, runtime: McpOauthRuntime): Promise<{
40
+ userId: string;
41
+ role: string;
42
+ } | 'insufficient_scope' | null>;
43
+ //# sourceMappingURL=oauth-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-http.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth-http.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAK3E,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAA;AAErE,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,eAAe,CAAA;IACvB,YAAY,EAAE,gBAAgB,CAAA;IAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;IACvD,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/E,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CACxC,CAAA;AAgCD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAQtE;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAcjF;AAQD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CA4CnB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,WAAW,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAIzE;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,EACxD,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgCtD;AAoBD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAqDnB;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,oBAAoB,GAAG,IAAI,CAAC,CAiBzE"}