@se-studio/contentful-cms 3.15.2 → 3.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/HOSTED.md +20 -0
- package/dist/commands/host-scaffold.d.ts.map +1 -1
- package/dist/commands/host-scaffold.js +5 -3
- package/dist/commands/host-scaffold.js.map +1 -1
- package/dist/contentful/cma-user-agent.d.ts +1 -1
- package/dist/editor-pack/capabilities.js +1 -1
- package/dist/editor-pack/capabilities.js.map +1 -1
- package/dist/editor-pack/generate.d.ts.map +1 -1
- package/dist/editor-pack/generate.js +1 -1
- package/dist/editor-pack/generate.js.map +1 -1
- package/dist/editor-pack/load.d.ts.map +1 -1
- package/dist/editor-pack/load.js +14 -2
- package/dist/editor-pack/load.js.map +1 -1
- package/dist/generated/help-topics.d.ts.map +1 -1
- package/dist/generated/help-topics.js +1 -1
- package/dist/generated/help-topics.js.map +1 -1
- package/dist/generated/hosted-guide.d.ts +1 -1
- package/dist/generated/hosted-guide.d.ts.map +1 -1
- package/dist/generated/hosted-guide.js +1 -1
- package/dist/generated/hosted-guide.js.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/hosted/index.d.ts +1 -1
- package/dist/hosted/index.d.ts.map +1 -1
- package/dist/hosted/index.js +1 -1
- package/dist/hosted/index.js.map +1 -1
- package/dist/hosted/next/pages/CmsEditPage.d.ts.map +1 -1
- package/dist/hosted/next/pages/CmsEditPage.js +1 -1
- package/dist/hosted/next/pages/CmsEditPage.js.map +1 -1
- package/dist/hosted/next/pages/HomePage.d.ts.map +1 -1
- package/dist/hosted/next/pages/HomePage.js +1 -1
- package/dist/hosted/next/pages/HomePage.js.map +1 -1
- package/dist/hosted/next/routes/mcp.d.ts +1 -0
- package/dist/hosted/next/routes/mcp.d.ts.map +1 -1
- package/dist/hosted/next/routes/mcp.js +6 -1
- package/dist/hosted/next/routes/mcp.js.map +1 -1
- package/dist/hosted/next/routes/oauth-authorization-server.d.ts +1 -0
- package/dist/hosted/next/routes/oauth-authorization-server.d.ts.map +1 -1
- package/dist/hosted/next/routes/oauth-authorization-server.js +5 -1
- package/dist/hosted/next/routes/oauth-authorization-server.js.map +1 -1
- package/dist/hosted/next/routes/oauth-protected-resource-path.d.ts +13 -0
- package/dist/hosted/next/routes/oauth-protected-resource-path.d.ts.map +1 -0
- package/dist/hosted/next/routes/oauth-protected-resource-path.js +17 -0
- package/dist/hosted/next/routes/oauth-protected-resource-path.js.map +1 -0
- package/dist/hosted/next/routes/oauth-protected-resource.d.ts +1 -0
- package/dist/hosted/next/routes/oauth-protected-resource.d.ts.map +1 -1
- package/dist/hosted/next/routes/oauth-protected-resource.js +5 -1
- package/dist/hosted/next/routes/oauth-protected-resource.js.map +1 -1
- package/dist/hosted/oauth/cimd.d.ts +17 -0
- package/dist/hosted/oauth/cimd.d.ts.map +1 -0
- package/dist/hosted/oauth/cimd.js +300 -0
- package/dist/hosted/oauth/cimd.js.map +1 -0
- package/dist/hosted/oauth/complete.d.ts +1 -0
- package/dist/hosted/oauth/complete.d.ts.map +1 -1
- package/dist/hosted/oauth/complete.js +4 -0
- package/dist/hosted/oauth/complete.js.map +1 -1
- package/dist/hosted/oauth/config.d.ts +2 -0
- package/dist/hosted/oauth/config.d.ts.map +1 -1
- package/dist/hosted/oauth/config.js +4 -0
- package/dist/hosted/oauth/config.js.map +1 -1
- package/dist/hosted/oauth/cors.d.ts +4 -0
- package/dist/hosted/oauth/cors.d.ts.map +1 -0
- package/dist/hosted/oauth/cors.js +23 -0
- package/dist/hosted/oauth/cors.js.map +1 -0
- package/dist/hosted/oauth/handlers.d.ts.map +1 -1
- package/dist/hosted/oauth/handlers.js +57 -7
- package/dist/hosted/oauth/handlers.js.map +1 -1
- package/dist/hosted/oauth/log.d.ts +2 -0
- package/dist/hosted/oauth/log.d.ts.map +1 -1
- package/dist/hosted/oauth/log.js +4 -0
- package/dist/hosted/oauth/log.js.map +1 -1
- package/dist/hosted/oauth/redirect-hosts.d.ts +4 -0
- package/dist/hosted/oauth/redirect-hosts.d.ts.map +1 -0
- package/dist/hosted/oauth/redirect-hosts.js +47 -0
- package/dist/hosted/oauth/redirect-hosts.js.map +1 -0
- package/dist/hosted/oauth/refresh.d.ts +1 -0
- package/dist/hosted/oauth/refresh.d.ts.map +1 -1
- package/dist/hosted/oauth/refresh.js +2 -0
- package/dist/hosted/oauth/refresh.js.map +1 -1
- package/dist/hosted/oauth/scopes.d.ts +15 -0
- package/dist/hosted/oauth/scopes.d.ts.map +1 -0
- package/dist/hosted/oauth/scopes.js +21 -0
- package/dist/hosted/oauth/scopes.js.map +1 -0
- package/dist/hosted/oauth/store.d.ts +4 -0
- package/dist/hosted/oauth/store.d.ts.map +1 -1
- package/dist/hosted/oauth/store.js +3 -0
- package/dist/hosted/oauth/store.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/client-tools.d.ts +11 -0
- package/dist/mcp/tools/client-tools.d.ts.map +1 -0
- package/dist/mcp/tools/client-tools.js +215 -0
- package/dist/mcp/tools/client-tools.js.map +1 -0
- package/dist/mcp/tools/cms-edit.d.ts +1 -1
- package/dist/mcp/tools/cms-edit.d.ts.map +1 -1
- package/dist/mcp/tools/cms-edit.js +3 -0
- package/dist/mcp/tools/cms-edit.js.map +1 -1
- package/dist/staged-upload/store.d.ts +18 -0
- package/dist/staged-upload/store.d.ts.map +1 -1
- package/dist/staged-upload/store.js +73 -0
- package/dist/staged-upload/store.js.map +1 -1
- package/dist/staged-upload/types.d.ts +2 -0
- package/dist/staged-upload/types.d.ts.map +1 -1
- package/docs/editor-tasks/publish-handoff.md +1 -0
- package/docs/help/publish.md +1 -1
- package/docs/mcp/hosted-guide.md +8 -0
- package/package.json +1 -1
- package/skills/core/SKILL.md +10 -3
- package/skills/editor-tasks/SKILL.md +1 -1
- package/skills/manifest.json +1 -1
- package/skills/setup/SKILL.md +23 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const CIMD_CLIENT_TTL_SECONDS: number;
|
|
2
|
+
export interface ClientIdMetadata {
|
|
3
|
+
clientId: string;
|
|
4
|
+
redirectUris: string[];
|
|
5
|
+
clientName?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class CimdError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
export declare function isCimdClientId(clientId: string): boolean;
|
|
11
|
+
export type CimdHostResolver = (hostname: string) => Promise<readonly string[]>;
|
|
12
|
+
export declare function isBlockedCimdAddress(address: string): boolean;
|
|
13
|
+
export declare function parseClientIdMetadata(clientId: string, body: unknown): ClientIdMetadata;
|
|
14
|
+
export declare function metadataAllowsRedirect(metadata: ClientIdMetadata, redirectUri: string): boolean;
|
|
15
|
+
/** Fetch a Client ID Metadata Document. Redirects are rejected. */
|
|
16
|
+
export declare function fetchClientIdMetadata(clientId: string, fetchImpl?: typeof fetch, resolveHost?: CimdHostResolver): Promise<ClientIdMetadata>;
|
|
17
|
+
//# sourceMappingURL=cimd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/cimd.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,uBAAuB,QAAU,CAAC;AAK/C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;AAmChF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAoC7D;AAkED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAoCvF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAE/F;AAiHD,mEAAmE;AACnE,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,KAAa,EAC/B,WAAW,GAAE,gBAAqC,GACjD,OAAO,CAAC,gBAAgB,CAAC,CAyB3B"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { lookup } from 'node:dns/promises';
|
|
2
|
+
import * as https from 'node:https';
|
|
3
|
+
import { isIP } from 'node:net';
|
|
4
|
+
import { redirectUriMatchesRegistered } from './redirect-uri.js';
|
|
5
|
+
export const CIMD_CLIENT_TTL_SECONDS = 60 * 60;
|
|
6
|
+
const CIMD_MAX_BYTES = 65_536;
|
|
7
|
+
const CIMD_TIMEOUT_MS = 5_000;
|
|
8
|
+
export class CimdError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'CimdError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function isCimdClientId(clientId) {
|
|
15
|
+
return clientId.startsWith('https://');
|
|
16
|
+
}
|
|
17
|
+
/** Node leaves a trailing dot on the hostname (`localhost.` still resolves to loopback). */
|
|
18
|
+
function canonicalHostname(hostname) {
|
|
19
|
+
return hostname.toLowerCase().replace(/\.+$/, '');
|
|
20
|
+
}
|
|
21
|
+
function ipv4ToInt(ip) {
|
|
22
|
+
const parts = ip.split('.');
|
|
23
|
+
if (parts.length !== 4) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
let value = 0;
|
|
27
|
+
for (const part of parts) {
|
|
28
|
+
if (!/^\d{1,3}$/.test(part)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const octet = Number(part);
|
|
32
|
+
if (octet > 255) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
value = (value << 8) | octet;
|
|
36
|
+
}
|
|
37
|
+
return value >>> 0;
|
|
38
|
+
}
|
|
39
|
+
function inIpv4Cidr(ip, base, bits) {
|
|
40
|
+
const baseInt = ipv4ToInt(base);
|
|
41
|
+
if (baseInt === null) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;
|
|
45
|
+
return (ip & mask) === (baseInt & mask);
|
|
46
|
+
}
|
|
47
|
+
export function isBlockedCimdAddress(address) {
|
|
48
|
+
const normalized = address.toLowerCase().replace(/^\[|\]$/g, '');
|
|
49
|
+
if (normalized.startsWith('::ffff:')) {
|
|
50
|
+
const mapped = normalized.slice('::ffff:'.length);
|
|
51
|
+
if (mapped.includes('.')) {
|
|
52
|
+
return isBlockedCimdAddress(mapped);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (normalized.includes(':')) {
|
|
56
|
+
if (normalized === '::' || normalized === '::1') {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
const head = Number.parseInt(normalized.split(':')[0] || '0', 16);
|
|
60
|
+
if (Number.isNaN(head)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if ((head & 0xfe00) === 0xfc00) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if ((head & 0xffc0) === 0xfe80) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const ip = ipv4ToInt(normalized);
|
|
72
|
+
if (ip === null) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return (inIpv4Cidr(ip, '0.0.0.0', 8) ||
|
|
76
|
+
inIpv4Cidr(ip, '10.0.0.0', 8) ||
|
|
77
|
+
inIpv4Cidr(ip, '127.0.0.0', 8) ||
|
|
78
|
+
inIpv4Cidr(ip, '169.254.0.0', 16) ||
|
|
79
|
+
inIpv4Cidr(ip, '172.16.0.0', 12) ||
|
|
80
|
+
inIpv4Cidr(ip, '192.168.0.0', 16));
|
|
81
|
+
}
|
|
82
|
+
function assertPublicHostname(host) {
|
|
83
|
+
if (host.length === 0 ||
|
|
84
|
+
host === 'localhost' ||
|
|
85
|
+
host.endsWith('.localhost') ||
|
|
86
|
+
host.endsWith('.local') ||
|
|
87
|
+
host.endsWith('.internal') ||
|
|
88
|
+
host.includes(':') ||
|
|
89
|
+
host.includes('%')) {
|
|
90
|
+
throw new CimdError('Client ID metadata host is not public.');
|
|
91
|
+
}
|
|
92
|
+
if (isIP(host) !== 0 || /^\d+\.\d+\.\d+\.\d+$/.test(host)) {
|
|
93
|
+
throw new CimdError('Client ID metadata host is not public.');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function assertPublicHttps(clientId) {
|
|
97
|
+
let url;
|
|
98
|
+
try {
|
|
99
|
+
url = new URL(clientId);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new CimdError('client_id is not a URL.');
|
|
103
|
+
}
|
|
104
|
+
if (url.protocol !== 'https:') {
|
|
105
|
+
throw new CimdError('Client ID metadata must be HTTPS.');
|
|
106
|
+
}
|
|
107
|
+
if (url.username || url.password) {
|
|
108
|
+
throw new CimdError('Client ID metadata URL must not include credentials.');
|
|
109
|
+
}
|
|
110
|
+
assertPublicHostname(canonicalHostname(url.hostname));
|
|
111
|
+
return url;
|
|
112
|
+
}
|
|
113
|
+
async function defaultResolveHost(hostname) {
|
|
114
|
+
const records = await lookup(hostname, { all: true, verbatim: true });
|
|
115
|
+
return records.map((record) => record.address);
|
|
116
|
+
}
|
|
117
|
+
async function assertStablePublicDns(hostname, resolveHost) {
|
|
118
|
+
let first;
|
|
119
|
+
let second;
|
|
120
|
+
try {
|
|
121
|
+
first = await resolveHost(hostname);
|
|
122
|
+
second = await resolveHost(hostname);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error instanceof CimdError) {
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
throw new CimdError('Client ID metadata host could not be resolved.');
|
|
129
|
+
}
|
|
130
|
+
const blocked = [...first, ...second].filter((address) => isBlockedCimdAddress(address));
|
|
131
|
+
const same = first.length > 0 &&
|
|
132
|
+
second.length === first.length &&
|
|
133
|
+
second.every((address, index) => address === first[index]);
|
|
134
|
+
if (blocked.length > 0 || !same) {
|
|
135
|
+
throw new CimdError('Client ID metadata host is not public.');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export function parseClientIdMetadata(clientId, body) {
|
|
139
|
+
if (!body || typeof body !== 'object') {
|
|
140
|
+
throw new CimdError('Client ID metadata is not a JSON object.');
|
|
141
|
+
}
|
|
142
|
+
const record = body;
|
|
143
|
+
if (record.client_id !== clientId) {
|
|
144
|
+
throw new CimdError('Client ID metadata client_id does not match the requested client.');
|
|
145
|
+
}
|
|
146
|
+
const supported = Array.isArray(record.token_endpoint_auth_methods_supported)
|
|
147
|
+
? record.token_endpoint_auth_methods_supported
|
|
148
|
+
: [];
|
|
149
|
+
const supportsNone = record.token_endpoint_auth_method === undefined ||
|
|
150
|
+
record.token_endpoint_auth_method === 'none' ||
|
|
151
|
+
supported.includes('none');
|
|
152
|
+
if (!supportsNone) {
|
|
153
|
+
throw new CimdError('Only public clients (token_endpoint_auth_method none) are supported.');
|
|
154
|
+
}
|
|
155
|
+
if (!Array.isArray(record.redirect_uris) || record.redirect_uris.length === 0) {
|
|
156
|
+
throw new CimdError('Client ID metadata is missing redirect_uris.');
|
|
157
|
+
}
|
|
158
|
+
const redirectUris = record.redirect_uris.filter((uri) => typeof uri === 'string');
|
|
159
|
+
if (redirectUris.length !== record.redirect_uris.length) {
|
|
160
|
+
throw new CimdError('Client ID metadata redirect_uris must be strings.');
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
clientId,
|
|
164
|
+
redirectUris,
|
|
165
|
+
clientName: typeof record.client_name === 'string' ? record.client_name : undefined,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export function metadataAllowsRedirect(metadata, redirectUri) {
|
|
169
|
+
return redirectUriMatchesRegistered(metadata.redirectUris, redirectUri);
|
|
170
|
+
}
|
|
171
|
+
async function readCappedBody(response) {
|
|
172
|
+
const declared = response.headers.get('content-length');
|
|
173
|
+
if (declared !== null && Number(declared) > CIMD_MAX_BYTES) {
|
|
174
|
+
throw new CimdError('Client ID metadata document is too large.');
|
|
175
|
+
}
|
|
176
|
+
const reader = response.body?.getReader();
|
|
177
|
+
if (!reader) {
|
|
178
|
+
const text = await response.text();
|
|
179
|
+
if (Buffer.byteLength(text) > CIMD_MAX_BYTES) {
|
|
180
|
+
throw new CimdError('Client ID metadata document is too large.');
|
|
181
|
+
}
|
|
182
|
+
return text;
|
|
183
|
+
}
|
|
184
|
+
const chunks = [];
|
|
185
|
+
let total = 0;
|
|
186
|
+
while (true) {
|
|
187
|
+
const { done, value } = await reader.read();
|
|
188
|
+
if (done) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
if (!value) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
total += value.byteLength;
|
|
195
|
+
if (total > CIMD_MAX_BYTES) {
|
|
196
|
+
await reader.cancel();
|
|
197
|
+
throw new CimdError('Client ID metadata document is too large.');
|
|
198
|
+
}
|
|
199
|
+
chunks.push(value);
|
|
200
|
+
}
|
|
201
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
202
|
+
}
|
|
203
|
+
function httpsGetPublic(url, signal) {
|
|
204
|
+
return new Promise((resolve, reject) => {
|
|
205
|
+
const req = https.get(url, {
|
|
206
|
+
headers: { accept: 'application/json' },
|
|
207
|
+
signal,
|
|
208
|
+
lookup: (hostname, options, callback) => {
|
|
209
|
+
lookup(hostname, { all: true, verbatim: true })
|
|
210
|
+
.then((records) => {
|
|
211
|
+
if (records.length === 0 ||
|
|
212
|
+
records.some((record) => isBlockedCimdAddress(record.address))) {
|
|
213
|
+
callback(new Error('Client ID metadata host is not public.'), '', 4);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const first = records[0];
|
|
217
|
+
if (!first) {
|
|
218
|
+
callback(new Error('Client ID metadata host could not be resolved.'), '', 4);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (typeof options === 'object' && options.all) {
|
|
222
|
+
callback(null, records);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
callback(null, first.address, first.family);
|
|
226
|
+
})
|
|
227
|
+
.catch((error) => {
|
|
228
|
+
callback(error instanceof Error ? error : new Error(String(error)), '', 4);
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
}, (res) => {
|
|
232
|
+
const chunks = [];
|
|
233
|
+
let total = 0;
|
|
234
|
+
const declared = res.headers['content-length'];
|
|
235
|
+
if (declared !== undefined && Number(declared) > CIMD_MAX_BYTES) {
|
|
236
|
+
req.destroy();
|
|
237
|
+
reject(new CimdError('Client ID metadata document is too large.'));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
res.on('data', (chunk) => {
|
|
241
|
+
total += chunk.length;
|
|
242
|
+
if (total > CIMD_MAX_BYTES) {
|
|
243
|
+
req.destroy();
|
|
244
|
+
reject(new CimdError('Client ID metadata document is too large.'));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
chunks.push(chunk);
|
|
248
|
+
});
|
|
249
|
+
res.on('end', () => {
|
|
250
|
+
resolve({ status: res.statusCode ?? 0, body: Buffer.concat(chunks).toString('utf8') });
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
req.on('error', (error) => {
|
|
254
|
+
reject(error);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function parseFetchedMetadata(clientId, status, text) {
|
|
259
|
+
if (status < 200 || status >= 300) {
|
|
260
|
+
throw new CimdError(`Client ID metadata request failed (${String(status)}).`);
|
|
261
|
+
}
|
|
262
|
+
let parsed;
|
|
263
|
+
try {
|
|
264
|
+
parsed = JSON.parse(text);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
throw new CimdError('Client ID metadata is not JSON.');
|
|
268
|
+
}
|
|
269
|
+
return parseClientIdMetadata(clientId, parsed);
|
|
270
|
+
}
|
|
271
|
+
/** Fetch a Client ID Metadata Document. Redirects are rejected. */
|
|
272
|
+
export async function fetchClientIdMetadata(clientId, fetchImpl = fetch, resolveHost = defaultResolveHost) {
|
|
273
|
+
const url = assertPublicHttps(clientId);
|
|
274
|
+
await assertStablePublicDns(canonicalHostname(url.hostname), resolveHost);
|
|
275
|
+
const controller = new AbortController();
|
|
276
|
+
const timer = setTimeout(() => controller.abort(), CIMD_TIMEOUT_MS);
|
|
277
|
+
try {
|
|
278
|
+
if (fetchImpl !== fetch) {
|
|
279
|
+
const response = await fetchImpl(clientId, {
|
|
280
|
+
method: 'GET',
|
|
281
|
+
redirect: 'error',
|
|
282
|
+
signal: controller.signal,
|
|
283
|
+
headers: { accept: 'application/json' },
|
|
284
|
+
});
|
|
285
|
+
return parseFetchedMetadata(clientId, response.status, await readCappedBody(response));
|
|
286
|
+
}
|
|
287
|
+
const result = await httpsGetPublic(clientId, controller.signal);
|
|
288
|
+
return parseFetchedMetadata(clientId, result.status, result.body);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (error instanceof CimdError) {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
throw new CimdError('Client ID metadata could not be fetched.');
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
clearTimeout(timer);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=cimd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.js","sourceRoot":"","sources":["../../../src/hosted/oauth/cimd.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/complete.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/complete.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEjE,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D,GACA,OAAO,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAsDD"}
|
|
@@ -3,6 +3,7 @@ import { loadAndValidateContentfulToken } from './cached-cma.js';
|
|
|
3
3
|
import { signMcpAccessToken } from './jwt.js';
|
|
4
4
|
import { generateAuthCode } from './pkce.js';
|
|
5
5
|
import { redirectUrisEquivalent } from './redirect-uri.js';
|
|
6
|
+
import { CMS_EDIT_OAUTH_SCOPE } from './scopes.js';
|
|
6
7
|
export async function issueAuthorizationRedirect(store, authRequestId, sub, request) {
|
|
7
8
|
const code = generateAuthCode();
|
|
8
9
|
await store.saveAuthCode(code, { authRequestId, sub });
|
|
@@ -40,16 +41,19 @@ export async function completeAuthorizationCodeGrant(store, input) {
|
|
|
40
41
|
}
|
|
41
42
|
const { accessToken, expiresIn } = await signMcpAccessToken(codeRecord.sub, authRequest.resource);
|
|
42
43
|
const refreshToken = randomUUID();
|
|
44
|
+
const scope = authRequest.scope ?? CMS_EDIT_OAUTH_SCOPE;
|
|
43
45
|
await store.saveRefreshToken(refreshToken, {
|
|
44
46
|
sub: codeRecord.sub,
|
|
45
47
|
clientId: authRequest.clientId,
|
|
46
48
|
resource: authRequest.resource,
|
|
49
|
+
scope,
|
|
47
50
|
});
|
|
48
51
|
return {
|
|
49
52
|
accessToken,
|
|
50
53
|
tokenType: 'Bearer',
|
|
51
54
|
expiresIn,
|
|
52
55
|
refreshToken,
|
|
56
|
+
scope,
|
|
53
57
|
};
|
|
54
58
|
}
|
|
55
59
|
//# sourceMappingURL=complete.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../src/hosted/oauth/complete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../src/hosted/oauth/complete.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare function getPublicOrigin(): string;
|
|
2
2
|
export declare function getMcpResourceUrl(): string;
|
|
3
|
+
/** RFC 9728 path suffix for the MCP resource (`/.well-known/oauth-protected-resource/api/mcp`). */
|
|
4
|
+
export declare function isMcpProtectedResourcePath(segments: readonly string[]): boolean;
|
|
3
5
|
export declare function getContentfulOAuthClientId(): string;
|
|
4
6
|
export declare function getContentfulOAuthRedirectUri(): string;
|
|
5
7
|
export declare function getContentfulAuthorizeUrl(authRequestId: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/config.ts"],"names":[],"mappings":"AAQA,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD;AAED,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CASvE;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAExD"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/config.ts"],"names":[],"mappings":"AAQA,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,mGAAmG;AACnG,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE/E;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD;AAED,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CASvE;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAExD"}
|
|
@@ -11,6 +11,10 @@ export function getPublicOrigin() {
|
|
|
11
11
|
export function getMcpResourceUrl() {
|
|
12
12
|
return `${getPublicOrigin()}/api/mcp`;
|
|
13
13
|
}
|
|
14
|
+
/** RFC 9728 path suffix for the MCP resource (`/.well-known/oauth-protected-resource/api/mcp`). */
|
|
15
|
+
export function isMcpProtectedResourcePath(segments) {
|
|
16
|
+
return segments.join('/') === 'api/mcp';
|
|
17
|
+
}
|
|
14
18
|
export function getContentfulOAuthClientId() {
|
|
15
19
|
return requireEnv('CONTENTFUL_OAUTH_CLIENT_ID');
|
|
16
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/hosted/oauth/config.ts"],"names":[],"mappings":"AAAA,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,GAAG,eAAe,EAAE,UAAU,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,OAAO,UAAU,CAAC,4BAA4B,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO,GAAG,eAAe,EAAE,4BAA4B,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,aAAqB;IAC7D,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,aAAa,EAAE,OAAO;QACtB,SAAS,EAAE,0BAA0B,EAAE;QACvC,YAAY,EAAE,6BAA6B,EAAE;QAC7C,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,aAAa;KACrB,CAAC,CAAC;IACH,OAAO,6CAA6C,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,+BAA+B;IAC7C,OAAO,GAAG,eAAe,EAAE,uCAAuC,CAAC;AACrE,CAAC"}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/hosted/oauth/config.ts"],"names":[],"mappings":"AAAA,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,GAAG,eAAe,EAAE,UAAU,CAAC;AACxC,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,0BAA0B,CAAC,QAA2B;IACpE,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,OAAO,UAAU,CAAC,4BAA4B,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO,GAAG,eAAe,EAAE,4BAA4B,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,aAAqB;IAC7D,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,aAAa,EAAE,OAAO;QACtB,SAAS,EAAE,0BAA0B,EAAE;QACvC,YAAY,EAAE,6BAA6B,EAAE;QAC7C,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,aAAa;KACrB,CAAC,CAAC;IACH,OAAO,6CAA6C,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,+BAA+B;IAC7C,OAAO,GAAG,eAAe,EAAE,uCAAuC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/cors.ts"],"names":[],"mappings":"AASA,wBAAgB,aAAa,IAAI,QAAQ,CAExC;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAUrD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const CORS_HEADERS = {
|
|
2
|
+
'Access-Control-Allow-Origin': '*',
|
|
3
|
+
'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',
|
|
4
|
+
'Access-Control-Allow-Headers': 'Authorization, Content-Type, MCP-Protocol-Version, Mcp-Protocol-Version, Mcp-Session-Id, Last-Event-ID',
|
|
5
|
+
'Access-Control-Expose-Headers': 'WWW-Authenticate, MCP-Protocol-Version, Mcp-Session-Id',
|
|
6
|
+
'Access-Control-Max-Age': '86400',
|
|
7
|
+
};
|
|
8
|
+
export function corsPreflight() {
|
|
9
|
+
return new Response(null, { status: 204, headers: CORS_HEADERS });
|
|
10
|
+
}
|
|
11
|
+
/** Copy a response and add discovery CORS headers. Body is a single read. */
|
|
12
|
+
export function withCors(response) {
|
|
13
|
+
const headers = new Headers(response.headers);
|
|
14
|
+
for (const [name, value] of Object.entries(CORS_HEADERS)) {
|
|
15
|
+
headers.set(name, value);
|
|
16
|
+
}
|
|
17
|
+
return new Response(response.body, {
|
|
18
|
+
status: response.status,
|
|
19
|
+
statusText: response.statusText,
|
|
20
|
+
headers,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=cors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../../src/hosted/oauth/cors.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAA2B;IAC3C,6BAA6B,EAAE,GAAG;IAClC,8BAA8B,EAAE,4BAA4B;IAC5D,8BAA8B,EAC5B,wGAAwG;IAC1G,+BAA+B,EAAE,wDAAwD;IACzF,wBAAwB,EAAE,OAAO;CAClC,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,QAAQ,CAAC,QAAkB;IACzC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;QACjC,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,OAAO;KACR,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAA4B,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/hosted/oauth/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAA4B,MAAM,wBAAwB,CAAC;AAmB/F,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,sBAAsB,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmDD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,QAAQ,CAAC,CA0EnB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,QAAQ,CAAC,CA4BnB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,QAAQ,CAAC,CAgGnB;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,QAAQ,CAAC,CAiDnB;AAED,wBAAgB,yCAAyC,IAAI,QAAQ,CAcpE;AAED,wBAAgB,uCAAuC,IAAI,QAAQ,CAQlE;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBvF"}
|
|
@@ -1,18 +1,48 @@
|
|
|
1
1
|
import { validatePatForDeployment } from '../../config/hosted.js';
|
|
2
2
|
import { flushTelemetry, recordOAuthTelemetryFailure, recordOAuthTelemetrySuccess, } from '../../telemetry/index.js';
|
|
3
|
+
import { CIMD_CLIENT_TTL_SECONDS, fetchClientIdMetadata, isCimdClientId, metadataAllowsRedirect, } from './cimd.js';
|
|
3
4
|
import { completeAuthorizationCodeGrant, issueAuthorizationRedirect } from './complete.js';
|
|
4
5
|
import { getContentfulAuthorizeUrl, getMcpResourceUrl, getPublicOrigin } from './config.js';
|
|
5
6
|
import { oauthLogAuthFailure, oauthLogVerbose, patValidationFailureReason } from './log.js';
|
|
7
|
+
import { noteOauthRedirect } from './redirect-hosts.js';
|
|
6
8
|
import { normalizeLoopbackRedirectUri, redirectUriMatchesRegistered } from './redirect-uri.js';
|
|
7
9
|
import { completeRefreshTokenGrant } from './refresh.js';
|
|
8
|
-
import {
|
|
10
|
+
import { CMS_EDIT_OAUTH_SCOPES, resolveGrantedScope } from './scopes.js';
|
|
11
|
+
import { CONTENTFUL_TOKEN_TTL_SECONDS, } from './store.js';
|
|
9
12
|
import { oauthTokenGrantFailureResponse } from './token-grant-response.js';
|
|
10
|
-
function
|
|
13
|
+
async function resolveAuthorizeClient(ctx, clientId, redirectUri) {
|
|
14
|
+
const existing = await ctx.store.getOAuthClient(clientId);
|
|
15
|
+
if (existing) {
|
|
16
|
+
return existing;
|
|
17
|
+
}
|
|
18
|
+
if (!isCimdClientId(clientId)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const metadata = await fetchClientIdMetadata(clientId);
|
|
23
|
+
if (!metadataAllowsRedirect(metadata, redirectUri)) {
|
|
24
|
+
return 'redirect_mismatch';
|
|
25
|
+
}
|
|
26
|
+
const record = {
|
|
27
|
+
clientId: metadata.clientId,
|
|
28
|
+
redirectUris: metadata.redirectUris,
|
|
29
|
+
clientName: metadata.clientName,
|
|
30
|
+
};
|
|
31
|
+
await ctx.store.saveOAuthClient(record, CIMD_CLIENT_TTL_SECONDS);
|
|
32
|
+
return record;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const message = error instanceof Error ? error.message : 'Invalid client metadata';
|
|
36
|
+
oauthLogAuthFailure('authorize', 'cimd', { message });
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function errorRedirect(redirectUri, message, state, error = 'access_denied') {
|
|
11
41
|
if (!redirectUri) {
|
|
12
42
|
return new Response(message, { status: 400 });
|
|
13
43
|
}
|
|
14
44
|
const url = new URL(redirectUri);
|
|
15
|
-
url.searchParams.set('error',
|
|
45
|
+
url.searchParams.set('error', error);
|
|
16
46
|
url.searchParams.set('error_description', message);
|
|
17
47
|
if (state) {
|
|
18
48
|
url.searchParams.set('state', state);
|
|
@@ -40,18 +70,28 @@ export async function handleOAuthAuthorizeGet(request, ctx) {
|
|
|
40
70
|
const redirect = await issueAuthorizationRedirect(ctx.store, resumeAuthRequestId, linkedSub, pending);
|
|
41
71
|
return Response.redirect(redirect, 302);
|
|
42
72
|
}
|
|
73
|
+
if (!clientId || !redirectUri) {
|
|
74
|
+
return new Response('Missing required OAuth parameters', { status: 400 });
|
|
75
|
+
}
|
|
76
|
+
const client = await resolveAuthorizeClient(ctx, clientId, redirectUri);
|
|
77
|
+
if (client === 'redirect_mismatch' ||
|
|
78
|
+
!client ||
|
|
79
|
+
!redirectUriMatchesRegistered(client.redirectUris, redirectUri)) {
|
|
80
|
+
return new Response('redirect_uri is not registered for this client', { status: 400 });
|
|
81
|
+
}
|
|
82
|
+
noteOauthRedirect(clientId, redirectUri);
|
|
43
83
|
if (responseType !== 'code') {
|
|
44
84
|
return errorRedirect(redirectUri, 'Unsupported response_type', state);
|
|
45
85
|
}
|
|
46
|
-
if (!
|
|
86
|
+
if (!codeChallenge || !resource) {
|
|
47
87
|
return errorRedirect(redirectUri, 'Missing required OAuth parameters', state);
|
|
48
88
|
}
|
|
49
89
|
if (codeChallengeMethod && codeChallengeMethod !== 'S256') {
|
|
50
90
|
return errorRedirect(redirectUri, 'Only S256 code challenge is supported', state);
|
|
51
91
|
}
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
54
|
-
return errorRedirect(redirectUri,
|
|
92
|
+
const granted = resolveGrantedScope(url.searchParams.get('scope'));
|
|
93
|
+
if (!granted.ok) {
|
|
94
|
+
return errorRedirect(redirectUri, `Unknown scope: ${granted.unknown.join(' ')}`, state, 'invalid_scope');
|
|
55
95
|
}
|
|
56
96
|
const normalizedRedirectUri = normalizeLoopbackRedirectUri(redirectUri);
|
|
57
97
|
const { authRequestId } = await ctx.store.savePendingAuthRequest({
|
|
@@ -60,6 +100,7 @@ export async function handleOAuthAuthorizeGet(request, ctx) {
|
|
|
60
100
|
codeChallenge,
|
|
61
101
|
state,
|
|
62
102
|
resource,
|
|
103
|
+
scope: granted.scope,
|
|
63
104
|
});
|
|
64
105
|
const contentfulAuthorizeUrl = getContentfulAuthorizeUrl(authRequestId);
|
|
65
106
|
return Response.redirect(contentfulAuthorizeUrl, 302);
|
|
@@ -80,6 +121,9 @@ export async function handleOAuthRegisterPost(request, ctx) {
|
|
|
80
121
|
redirectUris,
|
|
81
122
|
clientName: body.client_name,
|
|
82
123
|
});
|
|
124
|
+
for (const uri of client.redirectUris) {
|
|
125
|
+
noteOauthRedirect(client.clientId, uri);
|
|
126
|
+
}
|
|
83
127
|
return Response.json({
|
|
84
128
|
client_id: client.clientId,
|
|
85
129
|
client_id_issued_at: Math.floor(Date.now() / 1000),
|
|
@@ -129,6 +173,7 @@ export async function handleOAuthTokenPost(request, ctx) {
|
|
|
129
173
|
expires_in: token.expiresIn,
|
|
130
174
|
refresh_token: token.refreshToken,
|
|
131
175
|
refresh_token_expires_in: CONTENTFUL_TOKEN_TTL_SECONDS,
|
|
176
|
+
scope: token.scope,
|
|
132
177
|
});
|
|
133
178
|
}
|
|
134
179
|
catch (error) {
|
|
@@ -165,6 +210,7 @@ export async function handleOAuthTokenPost(request, ctx) {
|
|
|
165
210
|
expires_in: token.expiresIn,
|
|
166
211
|
refresh_token: token.refreshToken,
|
|
167
212
|
refresh_token_expires_in: CONTENTFUL_TOKEN_TTL_SECONDS,
|
|
213
|
+
scope: token.scope,
|
|
168
214
|
});
|
|
169
215
|
}
|
|
170
216
|
catch (error) {
|
|
@@ -232,6 +278,8 @@ export function handleOAuthAuthorizationServerMetadataGet() {
|
|
|
232
278
|
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
233
279
|
code_challenge_methods_supported: ['S256'],
|
|
234
280
|
token_endpoint_auth_methods_supported: ['none'],
|
|
281
|
+
scopes_supported: [...CMS_EDIT_OAUTH_SCOPES],
|
|
282
|
+
client_id_metadata_document_supported: true,
|
|
235
283
|
});
|
|
236
284
|
}
|
|
237
285
|
export function handleOAuthProtectedResourceMetadataGet() {
|
|
@@ -239,6 +287,8 @@ export function handleOAuthProtectedResourceMetadataGet() {
|
|
|
239
287
|
return Response.json({
|
|
240
288
|
resource: getMcpResourceUrl(),
|
|
241
289
|
authorization_servers: [origin],
|
|
290
|
+
scopes_supported: [...CMS_EDIT_OAUTH_SCOPES],
|
|
291
|
+
bearer_methods_supported: ['header'],
|
|
242
292
|
});
|
|
243
293
|
}
|
|
244
294
|
export async function handleOAuthHealthCheck() {
|