tiny-http-mcp-server 0.1.32 → 0.1.34
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/dist/composition.json +1 -1
- package/node_modules/mcp-oauth/README.md +17 -0
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +1 -6
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +6 -18
- package/node_modules/mcp-oauth/dist/client/client-registration.d.ts +4 -0
- package/node_modules/mcp-oauth/dist/client/client-registration.js +103 -0
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +56 -44
- package/node_modules/mcp-oauth/dist/client/token-auth-method.d.ts +2 -0
- package/node_modules/mcp-oauth/dist/client/token-auth-method.js +7 -0
- package/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +3 -1
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +19 -9
- package/node_modules/mcp-oauth/dist/client/types.d.ts +25 -4
- package/node_modules/mcp-oauth/dist/index.d.ts +2 -1
- package/node_modules/mcp-oauth/dist/index.js +1 -0
- package/node_modules/tiny-mcp-client/dist/index.d.ts +25 -4
- package/node_modules/tiny-mcp-client/dist/index.js +205 -77
- package/package.json +1 -1
package/dist/composition.json
CHANGED
|
@@ -112,6 +112,23 @@ Discovery binds an expired or explicitly rejected grant before silent refresh,
|
|
|
112
112
|
using the original configured client. Persisted sessions take precedence,
|
|
113
113
|
including sessions whose tokens have been cleared; an import cannot revive them.
|
|
114
114
|
Input tokens are copied and invalid expiry values fail before authorization.
|
|
115
|
+
Pass a complete DCR response as `client.registration` (or validate untrusted JSON
|
|
116
|
+
with `parseOAuthClientRegistration`). Dynamic clients infer their original ID
|
|
117
|
+
and secret from that response and reuse it without registering another app.
|
|
118
|
+
Explicit ID/secret values must agree with the imported response. Sessions and
|
|
119
|
+
native registration stores retain arrays, issuance/expiry timestamps and JSON
|
|
120
|
+
provider metadata. Registration input is copied, bounded to 64 KiB and 64
|
|
121
|
+
levels, and rejects invalid standard field types and non-JSON extensions.
|
|
122
|
+
Set `client.tokenEndpointAuthMethod` to `none`, `client_secret_post` or
|
|
123
|
+
`client_secret_basic`; a full registration can supply the same field as
|
|
124
|
+
`token_endpoint_auth_method`. Public clients never transmit a stored secret.
|
|
125
|
+
Basic credentials are individually form-encoded before Base64 encoding and
|
|
126
|
+
are omitted from the form body. Cached grants retain their registered method;
|
|
127
|
+
an explicitly different configured method requires separate persistence or a
|
|
128
|
+
reset. Native DCR chooses a supported method, preferring public PKCE when
|
|
129
|
+
advertised. Unsupported methods and missing confidential secrets fail before
|
|
130
|
+
token requests. Existing clients without a method keep the previous default:
|
|
131
|
+
body authentication when a secret is present, public authentication otherwise.
|
|
115
132
|
Static clients and dynamic initial-grant imports require cached grants to match
|
|
116
133
|
the original normalized client ID and secret. A different client configuration
|
|
117
134
|
fails before attaching or refreshing credentials and retains the stored record;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { type CreateSecretStoreInput } from "auth-store";
|
|
2
|
-
import type { OAuthSessionStore } from "./types.js";
|
|
3
|
-
interface StoredOAuthClient {
|
|
4
|
-
clientId: string;
|
|
5
|
-
clientSecret?: string;
|
|
6
|
-
}
|
|
2
|
+
import type { OAuthSessionStore, StoredOAuthClient } from "./types.js";
|
|
7
3
|
export interface OAuthClientStore {
|
|
8
4
|
load(issuer: string): Promise<StoredOAuthClient | null>;
|
|
9
5
|
save(issuer: string, client: StoredOAuthClient): Promise<void>;
|
|
@@ -12,4 +8,3 @@ export interface OAuthClientStore {
|
|
|
12
8
|
export declare function createAuthStoreSessionStore(options?: CreateSecretStoreInput, namespace?: string): OAuthSessionStore;
|
|
13
9
|
export declare function createAuthStoreClientStore(options: CreateSecretStoreInput, namespace?: string): OAuthClientStore;
|
|
14
10
|
export declare function assertPersistenceNamespace(namespace: string | undefined): void;
|
|
15
|
-
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeStoredOAuthClient } from "./client-registration.js";
|
|
1
2
|
import crypto from "node:crypto";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { createSecretStore } from "auth-store";
|
|
@@ -62,15 +63,10 @@ export function createAuthStoreClientStore(options, namespace) {
|
|
|
62
63
|
catch {
|
|
63
64
|
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Object.prototype.hasOwnProperty.call(parsed, "clientSecret")) {
|
|
70
|
-
client.clientSecret = getOwnEntry(parsed, "clientSecret");
|
|
71
|
-
}
|
|
72
|
-
return client;
|
|
73
|
-
}
|
|
66
|
+
// Preserve all registration metadata; provider normalization validates its
|
|
67
|
+
// identity and schema before it can authorize a request.
|
|
68
|
+
if (isObjectRecord(parsed) && typeof getOwnEntry(parsed, "clientId") === "string")
|
|
69
|
+
return parsed;
|
|
74
70
|
throw new Error("Stored OAuth client must be a JSON object with clientId");
|
|
75
71
|
},
|
|
76
72
|
async save(issuer, client) {
|
|
@@ -142,21 +138,13 @@ function isStoredOAuthSession(value) {
|
|
|
142
138
|
}
|
|
143
139
|
return (isNonBlankOwnString(value, "resource") &&
|
|
144
140
|
isNonBlankOwnString(value, "authorizationServer") &&
|
|
145
|
-
|
|
141
|
+
normalizeStoredOAuthClient(getOwnEntry(value, "client")) !== null &&
|
|
146
142
|
isStoredOAuthDiscovery(getOwnEntry(value, "discovery")) &&
|
|
147
143
|
(getOwnEntry(value, "requestedScope") === undefined || isNonBlankOwnString(value, "requestedScope")) &&
|
|
148
144
|
(getOwnEntry(value, "refreshState") === undefined ||
|
|
149
145
|
(getOwnEntry(value, "refreshState") === "pending" && getOwnEntry(value, "tokens") === undefined)) &&
|
|
150
146
|
isStoredOAuthTokensOrMissing(getOwnEntry(value, "tokens")));
|
|
151
147
|
}
|
|
152
|
-
function isStoredOAuthClient(value) {
|
|
153
|
-
if (!isObjectRecord(value) || !isNonBlankOwnString(value, "clientId")) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
const clientSecret = getOwnEntry(value, "clientSecret");
|
|
157
|
-
return (clientSecret === undefined ||
|
|
158
|
-
(typeof clientSecret === "string" && clientSecret.trim().length > 0));
|
|
159
|
-
}
|
|
160
148
|
function isStoredOAuthDiscovery(value) {
|
|
161
149
|
if (!isObjectRecord(value)) {
|
|
162
150
|
return false;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { OAuthClientRegistration, StoredOAuthClient } from "./types.js";
|
|
2
|
+
/** Validate and copy a bounded JSON DCR response without quoting credential input. */
|
|
3
|
+
export declare function parseOAuthClientRegistration(value: unknown): OAuthClientRegistration;
|
|
4
|
+
export declare function normalizeStoredOAuthClient(value: unknown): StoredOAuthClient | null;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { normalizeOAuthScope } from "./scope.js";
|
|
2
|
+
import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
|
|
3
|
+
/** Validate and copy a bounded JSON DCR response without quoting credential input. */
|
|
4
|
+
export function parseOAuthClientRegistration(value) {
|
|
5
|
+
const invalid = () => new Error("Invalid OAuth client registration metadata");
|
|
6
|
+
let nodes = 0;
|
|
7
|
+
function copy(input, depth) {
|
|
8
|
+
if (++nodes > 20_000 || depth > 64)
|
|
9
|
+
throw invalid();
|
|
10
|
+
if (input === null || typeof input === "boolean" || typeof input === "string")
|
|
11
|
+
return input;
|
|
12
|
+
if (typeof input === "number" && Number.isFinite(input))
|
|
13
|
+
return input;
|
|
14
|
+
if (typeof input !== "object" || input === null)
|
|
15
|
+
throw invalid();
|
|
16
|
+
const descriptors = Object.getOwnPropertyDescriptors(input);
|
|
17
|
+
if (Array.isArray(input)) {
|
|
18
|
+
const length = descriptors.length?.value;
|
|
19
|
+
if (length > 20_000)
|
|
20
|
+
throw invalid();
|
|
21
|
+
const result = [];
|
|
22
|
+
for (let index = 0; index < length; index++) {
|
|
23
|
+
const descriptor = descriptors[String(index)];
|
|
24
|
+
if (descriptor === undefined || !Object.hasOwn(descriptor, "value"))
|
|
25
|
+
throw invalid();
|
|
26
|
+
result.push(copy(descriptor.value, depth + 1));
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
if (Object.getPrototypeOf(input) !== Object.prototype && Object.getPrototypeOf(input) !== null)
|
|
31
|
+
throw invalid();
|
|
32
|
+
return Object.fromEntries(Object.entries(descriptors).filter(([, descriptor]) => descriptor.enumerable).map(([key, descriptor]) => {
|
|
33
|
+
if (!Object.hasOwn(descriptor, "value"))
|
|
34
|
+
throw invalid();
|
|
35
|
+
return [key, copy(descriptor.value, depth + 1)];
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
let result;
|
|
39
|
+
try {
|
|
40
|
+
result = copy(value, 0);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw invalid();
|
|
44
|
+
}
|
|
45
|
+
if (typeof result !== "object" || result === null || Array.isArray(result))
|
|
46
|
+
throw invalid();
|
|
47
|
+
const record = result;
|
|
48
|
+
if (!Object.hasOwn(record, "client_id") || typeof record.client_id !== "string" || record.client_id.trim() === "")
|
|
49
|
+
throw new Error("OAuth client registration response missing client_id");
|
|
50
|
+
for (const key of ["client_id", "client_secret", "token_endpoint_auth_method", "application_type", "client_name", "client_uri", "logo_uri", "scope",
|
|
51
|
+
"tos_uri", "policy_uri", "jwks_uri", "software_id", "software_version", "software_statement", "registration_access_token", "registration_client_uri", "issuer"]) {
|
|
52
|
+
if (Object.hasOwn(record, key) && record[key] !== null && typeof record[key] !== "string")
|
|
53
|
+
throw invalid();
|
|
54
|
+
}
|
|
55
|
+
if (typeof record.client_secret === "string" && record.client_secret.trim() === "")
|
|
56
|
+
throw invalid();
|
|
57
|
+
for (const key of ["redirect_uris", "grant_types", "response_types", "contacts"]) {
|
|
58
|
+
const entry = record[key];
|
|
59
|
+
if (Object.hasOwn(record, key) && entry !== null && (!Array.isArray(entry) || entry.some(item => typeof item !== "string")))
|
|
60
|
+
throw invalid();
|
|
61
|
+
}
|
|
62
|
+
for (const key of ["client_id_issued_at", "client_secret_expires_at"]) {
|
|
63
|
+
const entry = record[key];
|
|
64
|
+
if (Object.hasOwn(record, key) && entry !== null && (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 0))
|
|
65
|
+
throw invalid();
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
normalizeOAuthScope(Object.hasOwn(record, "scope") && record.scope !== null ? record.scope : undefined);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw invalid();
|
|
72
|
+
}
|
|
73
|
+
if (Buffer.byteLength(JSON.stringify(record), "utf8") > 64 * 1024)
|
|
74
|
+
throw invalid();
|
|
75
|
+
return record;
|
|
76
|
+
}
|
|
77
|
+
export function normalizeStoredOAuthClient(value) {
|
|
78
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
79
|
+
return null;
|
|
80
|
+
const record = value;
|
|
81
|
+
const clientId = Object.hasOwn(record, "clientId") ? record.clientId : undefined;
|
|
82
|
+
const clientSecret = Object.hasOwn(record, "clientSecret") ? record.clientSecret : undefined;
|
|
83
|
+
if (typeof clientId !== "string" || clientId.trim() === "" ||
|
|
84
|
+
(clientSecret !== undefined && (typeof clientSecret !== "string" || clientSecret.trim() === "")))
|
|
85
|
+
return null;
|
|
86
|
+
const client = { clientId: clientId.trim(), ...(clientSecret === undefined ? {} : { clientSecret: clientSecret.trim() }) };
|
|
87
|
+
const method = normalizeOAuthTokenEndpointAuthMethod(Object.hasOwn(record, "tokenEndpointAuthMethod") ? record.tokenEndpointAuthMethod : undefined);
|
|
88
|
+
if (Object.hasOwn(record, "registration") && record.registration !== undefined) {
|
|
89
|
+
const registration = parseOAuthClientRegistration(record.registration);
|
|
90
|
+
const registeredSecret = Object.hasOwn(registration, "client_secret") ? registration.client_secret?.trim() : undefined;
|
|
91
|
+
if (registration.client_id.trim() !== client.clientId || registeredSecret !== client.clientSecret)
|
|
92
|
+
throw new Error("OAuth client registration does not match the client identity");
|
|
93
|
+
client.registration = registration;
|
|
94
|
+
const registrationMethod = normalizeOAuthTokenEndpointAuthMethod(Object.hasOwn(registration, "token_endpoint_auth_method") ? registration.token_endpoint_auth_method : undefined);
|
|
95
|
+
if (method !== undefined && registrationMethod !== undefined && method !== registrationMethod)
|
|
96
|
+
throw new Error("OAuth token endpoint authentication conflicts with the client registration");
|
|
97
|
+
if (registrationMethod !== undefined)
|
|
98
|
+
client.tokenEndpointAuthMethod = registrationMethod;
|
|
99
|
+
}
|
|
100
|
+
if (method !== undefined)
|
|
101
|
+
client.tokenEndpointAuthMethod = method;
|
|
102
|
+
return client;
|
|
103
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { normalizeStoredOAuthClient, parseOAuthClientRegistration } from "./client-registration.js";
|
|
1
2
|
import { normalizeOAuthScope } from "./scope.js";
|
|
3
|
+
import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
|
|
2
4
|
import { isIP } from "node:net";
|
|
3
5
|
import { fetchMcpResponse } from "../http-fetch.js";
|
|
4
6
|
import { URL } from "node:url";
|
|
@@ -21,6 +23,9 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
21
23
|
assertPersistenceNamespace(options.persistenceNamespace);
|
|
22
24
|
const clientMetadata = getClientMetadata(options.client);
|
|
23
25
|
const requestedScope = clientMetadata?.scope;
|
|
26
|
+
const configuredClient = normalizeConfiguredClient(options.client);
|
|
27
|
+
const requestedTokenMethod = normalizeOAuthTokenEndpointAuthMethod(options.client.tokenEndpointAuthMethod);
|
|
28
|
+
const configuredTokenMethod = requestedTokenMethod ?? configuredClient?.tokenEndpointAuthMethod;
|
|
24
29
|
const sessionStore = options.sessionStore ?? createAuthStoreSessionStore(options.authStore, options.persistenceNamespace);
|
|
25
30
|
const clientStore = options.authStore === undefined ? null : createAuthStoreClientStore(options.authStore, options.persistenceNamespace);
|
|
26
31
|
const now = options.now ?? Date.now;
|
|
@@ -39,7 +44,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
39
44
|
const initialGrant = options.initialGrant === undefined ? undefined : {
|
|
40
45
|
resource: canonicalizeResourceIndicator(options.initialGrant.resource),
|
|
41
46
|
tokens: normalizeStoredTokens(options.initialGrant.tokens),
|
|
42
|
-
client:
|
|
47
|
+
client: configuredClient
|
|
43
48
|
};
|
|
44
49
|
if (initialGrant !== undefined && (initialGrant.tokens === undefined || initialGrant.client === null))
|
|
45
50
|
throw new Error("OAuth initial grant requires valid tokens and the original client ID");
|
|
@@ -147,13 +152,16 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
147
152
|
if (forceRefresh && rejectedTokens !== undefined && (rejectedTokens === null || session?.tokens === undefined || !sameTokenGrant(session.tokens, rejectedTokens)))
|
|
148
153
|
forceRefresh = false;
|
|
149
154
|
const sessionDiscovery = resolveDiscovery(discovery, session);
|
|
150
|
-
if ((options.client.mode === "static" || initialGrant !== undefined) && session !== null && (session.tokens !== undefined || session.refreshState === "pending")) {
|
|
151
|
-
const configured =
|
|
155
|
+
if ((options.client.mode === "static" || configuredClient?.registration !== undefined || initialGrant !== undefined) && session !== null && (session.tokens !== undefined || session.refreshState === "pending")) {
|
|
156
|
+
const configured = configuredClient;
|
|
152
157
|
if (configured === null || configured.clientId !== session.client.clientId || configured.clientSecret !== session.client.clientSecret)
|
|
153
158
|
throw new Error("Stored session belongs to a different OAuth client; use separate persistence or explicitly reset it");
|
|
154
159
|
}
|
|
155
160
|
if (requestedScope !== undefined && session?.tokens !== undefined && normalizeOAuthScope(session.tokens.scope ?? session.requestedScope) !== requestedScope)
|
|
156
161
|
throw new Error("Stored session does not match the requested OAuth scope; authorize again or select separate persistence");
|
|
162
|
+
if (configuredTokenMethod !== undefined && session !== null && (session.tokens !== undefined || session.refreshState === "pending") &&
|
|
163
|
+
(session.client.tokenEndpointAuthMethod ?? (session.client.clientSecret === undefined ? "none" : "client_secret_post")) !== configuredTokenMethod)
|
|
164
|
+
throw new Error("Stored session does not match the requested OAuth token endpoint authentication; select separate persistence or reset it");
|
|
157
165
|
if (session?.refreshState === "pending") {
|
|
158
166
|
if (!allowInteractive || options.allowInteractive === false || sessionDiscovery === undefined)
|
|
159
167
|
throw new Error("OAuth refresh outcome is unknown; authorize again before using this resource");
|
|
@@ -188,6 +196,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
188
196
|
if (session.tokens?.refreshToken === undefined) {
|
|
189
197
|
return session;
|
|
190
198
|
}
|
|
199
|
+
assertTokenEndpointAuthentication(session.client, discovery.authorizationServerMetadata);
|
|
191
200
|
const pendingSession = { ...clearSessionTokens(session), refreshState: "pending" };
|
|
192
201
|
await saveSession(resource, pendingSession);
|
|
193
202
|
signal?.throwIfAborted();
|
|
@@ -199,6 +208,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
199
208
|
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
200
209
|
clientId: session.client.clientId,
|
|
201
210
|
clientSecret: session.client.clientSecret,
|
|
211
|
+
tokenEndpointAuthMethod: session.client.tokenEndpointAuthMethod,
|
|
202
212
|
refreshToken: session.tokens.refreshToken,
|
|
203
213
|
resource,
|
|
204
214
|
fetch, signal,
|
|
@@ -264,6 +274,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
264
274
|
let resolvedClient = null;
|
|
265
275
|
try {
|
|
266
276
|
resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch, signal);
|
|
277
|
+
assertTokenEndpointAuthentication(resolvedClient.client, discovery.authorizationServerMetadata);
|
|
267
278
|
const sessionWithoutTokens = {
|
|
268
279
|
resource,
|
|
269
280
|
authorizationServer: discovery.authorizationServer,
|
|
@@ -287,6 +298,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
287
298
|
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
288
299
|
clientId: resolvedClient.client.clientId,
|
|
289
300
|
clientSecret: resolvedClient.client.clientSecret,
|
|
301
|
+
tokenEndpointAuthMethod: resolvedClient.client.tokenEndpointAuthMethod,
|
|
290
302
|
code,
|
|
291
303
|
codeVerifier: verifier,
|
|
292
304
|
redirectUri: loopback.redirectUri,
|
|
@@ -327,8 +339,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
327
339
|
}
|
|
328
340
|
async function resolveClient(existingSession, discovery, redirectUri, fetch, parentSignal) {
|
|
329
341
|
parentSignal?.throwIfAborted();
|
|
330
|
-
|
|
331
|
-
if (options.client.mode === "static") {
|
|
342
|
+
if (options.client.mode === "static" || configuredClient?.registration !== undefined) {
|
|
332
343
|
if (configuredClient === null) {
|
|
333
344
|
throw new Error("OAuth client_id must not be blank");
|
|
334
345
|
}
|
|
@@ -377,7 +388,12 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
377
388
|
};
|
|
378
389
|
}
|
|
379
390
|
}
|
|
380
|
-
const
|
|
391
|
+
const supported = getSupportedTokenAuthMethods(discovery.authorizationServerMetadata);
|
|
392
|
+
const registrationMethod = requestedTokenMethod ?? (supported === undefined ? "none" :
|
|
393
|
+
["none", "client_secret_basic", "client_secret_post"].find(method => supported.includes(method)));
|
|
394
|
+
if (registrationMethod === undefined || (supported !== undefined && !supported.includes(registrationMethod)))
|
|
395
|
+
throw new Error("Authorization server does not support the requested OAuth token endpoint authentication");
|
|
396
|
+
const registrationBody = buildClientRegistrationBody(clientMetadata, redirectUri, registrationMethod);
|
|
381
397
|
const deadline = AbortSignal.timeout(30_000);
|
|
382
398
|
const signal = parentSignal === undefined ? deadline : AbortSignal.any([parentSignal, deadline]);
|
|
383
399
|
const response = await fetchMcpResponse(fetch, registrationEndpoint, {
|
|
@@ -389,16 +405,15 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
389
405
|
signal
|
|
390
406
|
});
|
|
391
407
|
const payload = await readOAuthJsonObjectResponse(response, signal);
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const clientSecret = getOwnString(payload, "client_secret");
|
|
408
|
+
const registration = parseOAuthClientRegistration(payload);
|
|
409
|
+
const registeredSecret = getOwnString(registration, "client_secret");
|
|
410
|
+
const responseMethod = normalizeOAuthTokenEndpointAuthMethod(getOwnEntry(registration, "token_endpoint_auth_method")) ??
|
|
411
|
+
requestedTokenMethod ?? (supported === undefined ? undefined : normalizeOAuthTokenEndpointAuthMethod(registrationMethod));
|
|
397
412
|
const registeredClient = {
|
|
398
|
-
clientId:
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
413
|
+
clientId: registration.client_id.trim(),
|
|
414
|
+
...(registeredSecret === undefined ? {} : { clientSecret: registeredSecret.trim() }),
|
|
415
|
+
...(responseMethod === undefined ? {} : { tokenEndpointAuthMethod: responseMethod }),
|
|
416
|
+
registration
|
|
402
417
|
};
|
|
403
418
|
await saveRegisteredClient(discovery.authorizationServer, registeredClient);
|
|
404
419
|
return {
|
|
@@ -411,7 +426,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
411
426
|
return normalizeLoadedSession(await sessionStore.load(resource));
|
|
412
427
|
}
|
|
413
428
|
async function saveSession(resource, session) {
|
|
414
|
-
await sessionStore.save(resource, session);
|
|
429
|
+
await sessionStore.save(resource, structuredClone(session));
|
|
415
430
|
}
|
|
416
431
|
async function clearSession(resource) {
|
|
417
432
|
await sessionStore.clear(resource);
|
|
@@ -424,7 +439,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
424
439
|
return null;
|
|
425
440
|
}
|
|
426
441
|
const client = await clientStore.load(issuer);
|
|
427
|
-
const normalizedClient = client === null ? null :
|
|
442
|
+
const normalizedClient = client === null ? null : normalizeStoredOAuthClient(client);
|
|
428
443
|
if (client !== null && normalizedClient === null) {
|
|
429
444
|
await clientStore.clear(issuer);
|
|
430
445
|
return null;
|
|
@@ -503,7 +518,7 @@ function normalizeLoadedSession(session) {
|
|
|
503
518
|
const refreshState = getOwnEntry(session, "refreshState");
|
|
504
519
|
if (refreshState !== undefined && (refreshState !== "pending" || getOwnEntry(session, "tokens") !== undefined))
|
|
505
520
|
throw new Error("Stored OAuth refresh state is invalid");
|
|
506
|
-
const client =
|
|
521
|
+
const client = normalizeStoredOAuthClient(getOwnEntry(session, "client"));
|
|
507
522
|
if (client === null) {
|
|
508
523
|
return { ...session, client: { clientId: "" }, tokens: undefined };
|
|
509
524
|
}
|
|
@@ -513,25 +528,6 @@ function normalizeLoadedSession(session) {
|
|
|
513
528
|
tokens: normalizeStoredTokens(getOwnEntry(session, "tokens"))
|
|
514
529
|
};
|
|
515
530
|
}
|
|
516
|
-
function normalizeStoredClient(value) {
|
|
517
|
-
if (!isObjectRecord(value)) {
|
|
518
|
-
return null;
|
|
519
|
-
}
|
|
520
|
-
const clientId = getOwnString(value, "clientId");
|
|
521
|
-
if (clientId === undefined || clientId.trim().length === 0) {
|
|
522
|
-
return null;
|
|
523
|
-
}
|
|
524
|
-
const normalizedClientId = clientId.trim();
|
|
525
|
-
const clientSecret = getOwnEntry(value, "clientSecret");
|
|
526
|
-
if (clientSecret === undefined) {
|
|
527
|
-
return { clientId: normalizedClientId };
|
|
528
|
-
}
|
|
529
|
-
if (typeof clientSecret !== "string" || clientSecret.trim().length === 0) {
|
|
530
|
-
return null;
|
|
531
|
-
}
|
|
532
|
-
const normalizedClientSecret = clientSecret.trim();
|
|
533
|
-
return { clientId: normalizedClientId, clientSecret: normalizedClientSecret };
|
|
534
|
-
}
|
|
535
531
|
function normalizeStoredTokens(value) {
|
|
536
532
|
if (value === undefined || !isObjectRecord(value)) {
|
|
537
533
|
return undefined;
|
|
@@ -581,12 +577,12 @@ function getClientMetadata(client) {
|
|
|
581
577
|
};
|
|
582
578
|
}
|
|
583
579
|
function normalizeConfiguredClient(client) {
|
|
584
|
-
const
|
|
585
|
-
|
|
580
|
+
const registration = client.registration === undefined ? undefined : parseOAuthClientRegistration(client.registration);
|
|
581
|
+
const clientId = normalizeOptionalOAuthString(client.clientId) ?? registration?.client_id.trim();
|
|
582
|
+
if (clientId === undefined)
|
|
586
583
|
return null;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return clientSecret === undefined ? { clientId } : { clientId, clientSecret };
|
|
584
|
+
const clientSecret = normalizeOptionalOAuthString(client.clientSecret) ?? (registration === undefined ? undefined : getOwnString(registration, "client_secret")?.trim());
|
|
585
|
+
return normalizeStoredOAuthClient({ clientId, clientSecret, registration, tokenEndpointAuthMethod: client.tokenEndpointAuthMethod });
|
|
590
586
|
}
|
|
591
587
|
function normalizeOptionalOAuthString(value) {
|
|
592
588
|
if (value === undefined) {
|
|
@@ -693,12 +689,28 @@ function assertRequestMatchesResource(requestUrl, resource) {
|
|
|
693
689
|
throw new Error(`OAuth request URL ${requestUrl} does not match discovered resource ${resource}`);
|
|
694
690
|
}
|
|
695
691
|
}
|
|
696
|
-
function
|
|
692
|
+
function getSupportedTokenAuthMethods(metadata) {
|
|
693
|
+
const value = getOwnEntry(metadata, "token_endpoint_auth_methods_supported");
|
|
694
|
+
if (value === undefined)
|
|
695
|
+
return undefined;
|
|
696
|
+
if (!Array.isArray(value) || value.length > 128 || value.some(method => typeof method !== "string"))
|
|
697
|
+
throw new Error("Invalid OAuth token endpoint authentication metadata");
|
|
698
|
+
return value;
|
|
699
|
+
}
|
|
700
|
+
function assertTokenEndpointAuthentication(client, metadata) {
|
|
701
|
+
const method = client.tokenEndpointAuthMethod ?? (client.clientSecret === undefined ? "none" : "client_secret_post");
|
|
702
|
+
if (method !== "none" && client.clientSecret === undefined)
|
|
703
|
+
throw new Error("OAuth token endpoint authentication requires a client secret");
|
|
704
|
+
const supported = getSupportedTokenAuthMethods(metadata);
|
|
705
|
+
if (supported !== undefined && !supported.includes(method))
|
|
706
|
+
throw new Error("Authorization server does not support the requested OAuth token endpoint authentication");
|
|
707
|
+
}
|
|
708
|
+
function buildClientRegistrationBody(metadata, redirectUri, tokenEndpointAuthMethod) {
|
|
697
709
|
const body = {
|
|
698
710
|
redirect_uris: [redirectUri],
|
|
699
711
|
grant_types: ["authorization_code", "refresh_token"],
|
|
700
712
|
response_types: ["code"],
|
|
701
|
-
token_endpoint_auth_method:
|
|
713
|
+
token_endpoint_auth_method: tokenEndpointAuthMethod
|
|
702
714
|
};
|
|
703
715
|
const clientName = metadata === undefined ? undefined : getOwnString(metadata, "clientName");
|
|
704
716
|
const scope = metadata === undefined ? undefined : getOwnString(metadata, "scope");
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function normalizeOAuthTokenEndpointAuthMethod(value) {
|
|
2
|
+
if (value === undefined || value === null)
|
|
3
|
+
return undefined;
|
|
4
|
+
if (value !== "none" && value !== "client_secret_post" && value !== "client_secret_basic")
|
|
5
|
+
throw new Error("Unsupported OAuth token endpoint authentication method");
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OAuthMetadataFetch, StoredOAuthTokens } from "./types.js";
|
|
1
|
+
import type { OAuthMetadataFetch, StoredOAuthTokens, OAuthTokenEndpointAuthMethod } from "./types.js";
|
|
2
2
|
interface OAuthErrorShape {
|
|
3
3
|
error: string;
|
|
4
4
|
error_description?: string;
|
|
@@ -22,6 +22,7 @@ export declare function exchangeAuthorizationCode(input: {
|
|
|
22
22
|
tokenEndpoint: string;
|
|
23
23
|
clientId: string;
|
|
24
24
|
clientSecret?: string;
|
|
25
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
25
26
|
code: string;
|
|
26
27
|
codeVerifier: string;
|
|
27
28
|
redirectUri: string;
|
|
@@ -34,6 +35,7 @@ export declare function refreshAccessToken(input: {
|
|
|
34
35
|
tokenEndpoint: string;
|
|
35
36
|
clientId: string;
|
|
36
37
|
clientSecret?: string;
|
|
38
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
37
39
|
refreshToken: string;
|
|
38
40
|
resource: string;
|
|
39
41
|
fetch: OAuthMetadataFetch;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
|
|
1
2
|
import { canonicalizeResourceIndicator } from "../resource-indicator.js";
|
|
2
3
|
import { readBoundedResponseText } from "../http-response.js";
|
|
3
4
|
import { fetchMcpResponse } from "../http-fetch.js";
|
|
@@ -40,6 +41,7 @@ export async function exchangeAuthorizationCode(input) {
|
|
|
40
41
|
tokenEndpoint: input.tokenEndpoint,
|
|
41
42
|
clientId: input.clientId,
|
|
42
43
|
clientSecret: input.clientSecret,
|
|
44
|
+
tokenEndpointAuthMethod: input.tokenEndpointAuthMethod,
|
|
43
45
|
params: {
|
|
44
46
|
grant_type: "authorization_code",
|
|
45
47
|
code: input.code,
|
|
@@ -58,6 +60,7 @@ export async function refreshAccessToken(input) {
|
|
|
58
60
|
tokenEndpoint: input.tokenEndpoint,
|
|
59
61
|
clientId: input.clientId,
|
|
60
62
|
clientSecret: input.clientSecret,
|
|
63
|
+
tokenEndpointAuthMethod: input.tokenEndpointAuthMethod,
|
|
61
64
|
params: {
|
|
62
65
|
grant_type: "refresh_token",
|
|
63
66
|
refresh_token: input.refreshToken,
|
|
@@ -69,21 +72,28 @@ export async function refreshAccessToken(input) {
|
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
74
|
async function requestTokens(input) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
const method = normalizeOAuthTokenEndpointAuthMethod(input.tokenEndpointAuthMethod) ??
|
|
76
|
+
(input.clientSecret === undefined ? "none" : "client_secret_post");
|
|
77
|
+
if (method !== "none" && (input.clientSecret === undefined || input.clientSecret.trim() === ""))
|
|
78
|
+
throw new Error("OAuth token endpoint authentication requires a client secret");
|
|
79
|
+
const body = new URLSearchParams(input.params);
|
|
80
|
+
const headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded" });
|
|
81
|
+
if (method === "client_secret_basic") {
|
|
82
|
+
const encoded = new URLSearchParams({ credential: input.clientId }).toString().slice("credential=".length);
|
|
83
|
+
const encodedSecret = new URLSearchParams({ credential: input.clientSecret }).toString().slice("credential=".length);
|
|
84
|
+
headers.set("Authorization", `Basic ${Buffer.from(`${encoded}:${encodedSecret}`).toString("base64")}`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
body.set("client_id", input.clientId);
|
|
88
|
+
if (method === "client_secret_post")
|
|
89
|
+
body.set("client_secret", input.clientSecret);
|
|
78
90
|
}
|
|
79
91
|
input.signal?.throwIfAborted();
|
|
80
92
|
const deadline = AbortSignal.timeout(30_000);
|
|
81
93
|
const signal = input.signal === undefined ? deadline : AbortSignal.any([input.signal, deadline]);
|
|
82
94
|
const response = await fetchMcpResponse(input.fetch, input.tokenEndpoint, {
|
|
83
95
|
method: "POST",
|
|
84
|
-
headers
|
|
85
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
86
|
-
},
|
|
96
|
+
headers,
|
|
87
97
|
body: body.toString(),
|
|
88
98
|
signal
|
|
89
99
|
});
|
|
@@ -70,13 +70,29 @@ export interface StoredOAuthTokens {
|
|
|
70
70
|
expiresAt: number | null;
|
|
71
71
|
scope?: string;
|
|
72
72
|
}
|
|
73
|
+
/** Full RFC 7591 response, including JSON provider extensions. */
|
|
74
|
+
export interface OAuthClientRegistration extends Record<string, unknown> {
|
|
75
|
+
client_id: string;
|
|
76
|
+
client_secret?: string | null;
|
|
77
|
+
redirect_uris?: string[] | null;
|
|
78
|
+
grant_types?: string[] | null;
|
|
79
|
+
response_types?: string[] | null;
|
|
80
|
+
contacts?: string[] | null;
|
|
81
|
+
client_id_issued_at?: number | null;
|
|
82
|
+
client_secret_expires_at?: number | null;
|
|
83
|
+
token_endpoint_auth_method?: string | null;
|
|
84
|
+
}
|
|
85
|
+
export interface StoredOAuthClient {
|
|
86
|
+
clientId: string;
|
|
87
|
+
clientSecret?: string;
|
|
88
|
+
registration?: OAuthClientRegistration;
|
|
89
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
90
|
+
}
|
|
91
|
+
export type OAuthTokenEndpointAuthMethod = "none" | "client_secret_post" | "client_secret_basic";
|
|
73
92
|
export interface StoredOAuthSession {
|
|
74
93
|
resource: string;
|
|
75
94
|
authorizationServer: string;
|
|
76
|
-
client:
|
|
77
|
-
clientId: string;
|
|
78
|
-
clientSecret?: string;
|
|
79
|
-
};
|
|
95
|
+
client: StoredOAuthClient;
|
|
80
96
|
tokens?: StoredOAuthTokens;
|
|
81
97
|
/** A refresh was begun; its winning response may not have been persisted. */
|
|
82
98
|
refreshState?: "pending";
|
|
@@ -104,11 +120,16 @@ export interface DefaultOAuthClientProviderOptions {
|
|
|
104
120
|
clientId?: string;
|
|
105
121
|
clientSecret?: string;
|
|
106
122
|
metadata?: OAuthClientMetadata;
|
|
123
|
+
/** Import a complete registration owned by the caller. */
|
|
124
|
+
registration?: OAuthClientRegistration;
|
|
125
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
107
126
|
} | {
|
|
108
127
|
mode: "static";
|
|
109
128
|
clientId: string;
|
|
110
129
|
clientSecret?: string;
|
|
111
130
|
metadata?: OAuthClientMetadata;
|
|
131
|
+
registration?: OAuthClientRegistration;
|
|
132
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
112
133
|
};
|
|
113
134
|
/** Disable interactive authorization while allowing cached tokens and silent refresh. */
|
|
114
135
|
allowInteractive?: boolean;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { createAuthStoreSessionStore, } from "./client/auth-store-session-store.js";
|
|
2
|
+
export { parseOAuthClientRegistration } from "./client/client-registration.js";
|
|
2
3
|
export { createDefaultOAuthClientProvider, createOAuthClientProvider, } from "./client/default-oauth-client-provider.js";
|
|
3
4
|
export { buildSuccessPage, createLoopbackAuthorizationSession, extractCodeFromInput, } from "./client/loopback-authorization.js";
|
|
4
5
|
export { generateCodeChallenge, generateCodeVerifier, } from "./client/pkce.js";
|
|
5
6
|
export { OAuthError, } from "./client/token-endpoint.js";
|
|
6
7
|
export { canonicalizeResourceIndicator, } from "./resource-indicator.js";
|
|
7
8
|
export { createJwksTokenVerifier, } from "./server/jwks-token-verifier.js";
|
|
8
|
-
export type { DefaultOAuthClientProviderOptions, OAuthAuthorizationServerMetadata, OAuthClientMetadata, OAuthClientProvider, OAuthClientProviderOptions, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthSessionStore, OAuthUnauthorizedChallenge, StoredOAuthSession, StoredOAuthTokens, } from "./client/types.js";
|
|
9
|
+
export type { DefaultOAuthClientProviderOptions, OAuthAuthorizationServerMetadata, OAuthClientMetadata, OAuthClientRegistration, OAuthClientProvider, OAuthClientProviderOptions, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthSessionStore, OAuthUnauthorizedChallenge, OAuthTokenEndpointAuthMethod, StoredOAuthSession, StoredOAuthClient, StoredOAuthTokens, } from "./client/types.js";
|
|
9
10
|
export type { JwksTokenVerifier, JwksTokenVerifierOptions, JwksVerifiedAccessToken, } from "./server/jwks-token-verifier.js";
|
|
10
11
|
export type { LoopbackAuthorizationOptions, LoopbackAuthorizationSession, OAuthLandingPage, } from "./client/loopback-authorization.js";
|
|
11
12
|
export { readBoundedResponseText } from "./http-response.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createAuthStoreSessionStore, } from "./client/auth-store-session-store.js";
|
|
2
|
+
export { parseOAuthClientRegistration } from "./client/client-registration.js";
|
|
2
3
|
export { createDefaultOAuthClientProvider, createOAuthClientProvider, } from "./client/default-oauth-client-provider.js";
|
|
3
4
|
export { buildSuccessPage, createLoopbackAuthorizationSession, extractCodeFromInput, } from "./client/loopback-authorization.js";
|
|
4
5
|
export { generateCodeChallenge, generateCodeVerifier, } from "./client/pkce.js";
|
|
@@ -173,13 +173,29 @@ interface StoredOAuthTokens {
|
|
|
173
173
|
expiresAt: number | null;
|
|
174
174
|
scope?: string;
|
|
175
175
|
}
|
|
176
|
+
/** Full RFC 7591 response, including JSON provider extensions. */
|
|
177
|
+
interface OAuthClientRegistration extends Record<string, unknown> {
|
|
178
|
+
client_id: string;
|
|
179
|
+
client_secret?: string | null;
|
|
180
|
+
redirect_uris?: string[] | null;
|
|
181
|
+
grant_types?: string[] | null;
|
|
182
|
+
response_types?: string[] | null;
|
|
183
|
+
contacts?: string[] | null;
|
|
184
|
+
client_id_issued_at?: number | null;
|
|
185
|
+
client_secret_expires_at?: number | null;
|
|
186
|
+
token_endpoint_auth_method?: string | null;
|
|
187
|
+
}
|
|
188
|
+
interface StoredOAuthClient {
|
|
189
|
+
clientId: string;
|
|
190
|
+
clientSecret?: string;
|
|
191
|
+
registration?: OAuthClientRegistration;
|
|
192
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
193
|
+
}
|
|
194
|
+
type OAuthTokenEndpointAuthMethod = "none" | "client_secret_post" | "client_secret_basic";
|
|
176
195
|
interface StoredOAuthSession {
|
|
177
196
|
resource: string;
|
|
178
197
|
authorizationServer: string;
|
|
179
|
-
client:
|
|
180
|
-
clientId: string;
|
|
181
|
-
clientSecret?: string;
|
|
182
|
-
};
|
|
198
|
+
client: StoredOAuthClient;
|
|
183
199
|
tokens?: StoredOAuthTokens;
|
|
184
200
|
/** A refresh was begun; its winning response may not have been persisted. */
|
|
185
201
|
refreshState?: "pending";
|
|
@@ -207,11 +223,16 @@ interface DefaultOAuthClientProviderOptions {
|
|
|
207
223
|
clientId?: string;
|
|
208
224
|
clientSecret?: string;
|
|
209
225
|
metadata?: OAuthClientMetadata;
|
|
226
|
+
/** Import a complete registration owned by the caller. */
|
|
227
|
+
registration?: OAuthClientRegistration;
|
|
228
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
210
229
|
} | {
|
|
211
230
|
mode: "static";
|
|
212
231
|
clientId: string;
|
|
213
232
|
clientSecret?: string;
|
|
214
233
|
metadata?: OAuthClientMetadata;
|
|
234
|
+
registration?: OAuthClientRegistration;
|
|
235
|
+
tokenEndpointAuthMethod?: OAuthTokenEndpointAuthMethod;
|
|
215
236
|
};
|
|
216
237
|
/** Disable interactive authorization while allowing cached tokens and silent refresh. */
|
|
217
238
|
allowInteractive?: boolean;
|
|
@@ -3395,6 +3395,141 @@ var SubscriptionManager = class {
|
|
|
3395
3395
|
}
|
|
3396
3396
|
};
|
|
3397
3397
|
|
|
3398
|
+
// ../mcp-oauth/dist/client/scope.js
|
|
3399
|
+
function normalizeOAuthScope(scope) {
|
|
3400
|
+
if (scope === void 0)
|
|
3401
|
+
return void 0;
|
|
3402
|
+
if (typeof scope !== "string" || [...scope].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) > 126 || char === '"' || char === "\\"))
|
|
3403
|
+
throw new Error("Invalid OAuth scope syntax");
|
|
3404
|
+
const normalized = [...new Set(scope.split(" ").filter(Boolean))].sort().join(" ");
|
|
3405
|
+
return normalized || void 0;
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
// ../mcp-oauth/dist/client/token-auth-method.js
|
|
3409
|
+
function normalizeOAuthTokenEndpointAuthMethod(value) {
|
|
3410
|
+
if (value === void 0 || value === null)
|
|
3411
|
+
return void 0;
|
|
3412
|
+
if (value !== "none" && value !== "client_secret_post" && value !== "client_secret_basic")
|
|
3413
|
+
throw new Error("Unsupported OAuth token endpoint authentication method");
|
|
3414
|
+
return value;
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
// ../mcp-oauth/dist/client/client-registration.js
|
|
3418
|
+
function parseOAuthClientRegistration(value) {
|
|
3419
|
+
const invalid = () => new Error("Invalid OAuth client registration metadata");
|
|
3420
|
+
let nodes = 0;
|
|
3421
|
+
function copy(input, depth) {
|
|
3422
|
+
if (++nodes > 2e4 || depth > 64)
|
|
3423
|
+
throw invalid();
|
|
3424
|
+
if (input === null || typeof input === "boolean" || typeof input === "string")
|
|
3425
|
+
return input;
|
|
3426
|
+
if (typeof input === "number" && Number.isFinite(input))
|
|
3427
|
+
return input;
|
|
3428
|
+
if (typeof input !== "object" || input === null)
|
|
3429
|
+
throw invalid();
|
|
3430
|
+
const descriptors = Object.getOwnPropertyDescriptors(input);
|
|
3431
|
+
if (Array.isArray(input)) {
|
|
3432
|
+
const length = descriptors.length?.value;
|
|
3433
|
+
if (length > 2e4)
|
|
3434
|
+
throw invalid();
|
|
3435
|
+
const result2 = [];
|
|
3436
|
+
for (let index = 0; index < length; index++) {
|
|
3437
|
+
const descriptor = descriptors[String(index)];
|
|
3438
|
+
if (descriptor === void 0 || !Object.hasOwn(descriptor, "value"))
|
|
3439
|
+
throw invalid();
|
|
3440
|
+
result2.push(copy(descriptor.value, depth + 1));
|
|
3441
|
+
}
|
|
3442
|
+
return result2;
|
|
3443
|
+
}
|
|
3444
|
+
if (Object.getPrototypeOf(input) !== Object.prototype && Object.getPrototypeOf(input) !== null)
|
|
3445
|
+
throw invalid();
|
|
3446
|
+
return Object.fromEntries(Object.entries(descriptors).filter(([, descriptor]) => descriptor.enumerable).map(([key2, descriptor]) => {
|
|
3447
|
+
if (!Object.hasOwn(descriptor, "value"))
|
|
3448
|
+
throw invalid();
|
|
3449
|
+
return [key2, copy(descriptor.value, depth + 1)];
|
|
3450
|
+
}));
|
|
3451
|
+
}
|
|
3452
|
+
let result;
|
|
3453
|
+
try {
|
|
3454
|
+
result = copy(value, 0);
|
|
3455
|
+
} catch {
|
|
3456
|
+
throw invalid();
|
|
3457
|
+
}
|
|
3458
|
+
if (typeof result !== "object" || result === null || Array.isArray(result))
|
|
3459
|
+
throw invalid();
|
|
3460
|
+
const record2 = result;
|
|
3461
|
+
if (!Object.hasOwn(record2, "client_id") || typeof record2.client_id !== "string" || record2.client_id.trim() === "")
|
|
3462
|
+
throw new Error("OAuth client registration response missing client_id");
|
|
3463
|
+
for (const key2 of [
|
|
3464
|
+
"client_id",
|
|
3465
|
+
"client_secret",
|
|
3466
|
+
"token_endpoint_auth_method",
|
|
3467
|
+
"application_type",
|
|
3468
|
+
"client_name",
|
|
3469
|
+
"client_uri",
|
|
3470
|
+
"logo_uri",
|
|
3471
|
+
"scope",
|
|
3472
|
+
"tos_uri",
|
|
3473
|
+
"policy_uri",
|
|
3474
|
+
"jwks_uri",
|
|
3475
|
+
"software_id",
|
|
3476
|
+
"software_version",
|
|
3477
|
+
"software_statement",
|
|
3478
|
+
"registration_access_token",
|
|
3479
|
+
"registration_client_uri",
|
|
3480
|
+
"issuer"
|
|
3481
|
+
]) {
|
|
3482
|
+
if (Object.hasOwn(record2, key2) && record2[key2] !== null && typeof record2[key2] !== "string")
|
|
3483
|
+
throw invalid();
|
|
3484
|
+
}
|
|
3485
|
+
if (typeof record2.client_secret === "string" && record2.client_secret.trim() === "")
|
|
3486
|
+
throw invalid();
|
|
3487
|
+
for (const key2 of ["redirect_uris", "grant_types", "response_types", "contacts"]) {
|
|
3488
|
+
const entry = record2[key2];
|
|
3489
|
+
if (Object.hasOwn(record2, key2) && entry !== null && (!Array.isArray(entry) || entry.some((item) => typeof item !== "string")))
|
|
3490
|
+
throw invalid();
|
|
3491
|
+
}
|
|
3492
|
+
for (const key2 of ["client_id_issued_at", "client_secret_expires_at"]) {
|
|
3493
|
+
const entry = record2[key2];
|
|
3494
|
+
if (Object.hasOwn(record2, key2) && entry !== null && (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 0))
|
|
3495
|
+
throw invalid();
|
|
3496
|
+
}
|
|
3497
|
+
try {
|
|
3498
|
+
normalizeOAuthScope(Object.hasOwn(record2, "scope") && record2.scope !== null ? record2.scope : void 0);
|
|
3499
|
+
} catch {
|
|
3500
|
+
throw invalid();
|
|
3501
|
+
}
|
|
3502
|
+
if (Buffer.byteLength(JSON.stringify(record2), "utf8") > 64 * 1024)
|
|
3503
|
+
throw invalid();
|
|
3504
|
+
return record2;
|
|
3505
|
+
}
|
|
3506
|
+
function normalizeStoredOAuthClient(value) {
|
|
3507
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
3508
|
+
return null;
|
|
3509
|
+
const record2 = value;
|
|
3510
|
+
const clientId = Object.hasOwn(record2, "clientId") ? record2.clientId : void 0;
|
|
3511
|
+
const clientSecret = Object.hasOwn(record2, "clientSecret") ? record2.clientSecret : void 0;
|
|
3512
|
+
if (typeof clientId !== "string" || clientId.trim() === "" || clientSecret !== void 0 && (typeof clientSecret !== "string" || clientSecret.trim() === ""))
|
|
3513
|
+
return null;
|
|
3514
|
+
const client = { clientId: clientId.trim(), ...clientSecret === void 0 ? {} : { clientSecret: clientSecret.trim() } };
|
|
3515
|
+
const method = normalizeOAuthTokenEndpointAuthMethod(Object.hasOwn(record2, "tokenEndpointAuthMethod") ? record2.tokenEndpointAuthMethod : void 0);
|
|
3516
|
+
if (Object.hasOwn(record2, "registration") && record2.registration !== void 0) {
|
|
3517
|
+
const registration = parseOAuthClientRegistration(record2.registration);
|
|
3518
|
+
const registeredSecret = Object.hasOwn(registration, "client_secret") ? registration.client_secret?.trim() : void 0;
|
|
3519
|
+
if (registration.client_id.trim() !== client.clientId || registeredSecret !== client.clientSecret)
|
|
3520
|
+
throw new Error("OAuth client registration does not match the client identity");
|
|
3521
|
+
client.registration = registration;
|
|
3522
|
+
const registrationMethod = normalizeOAuthTokenEndpointAuthMethod(Object.hasOwn(registration, "token_endpoint_auth_method") ? registration.token_endpoint_auth_method : void 0);
|
|
3523
|
+
if (method !== void 0 && registrationMethod !== void 0 && method !== registrationMethod)
|
|
3524
|
+
throw new Error("OAuth token endpoint authentication conflicts with the client registration");
|
|
3525
|
+
if (registrationMethod !== void 0)
|
|
3526
|
+
client.tokenEndpointAuthMethod = registrationMethod;
|
|
3527
|
+
}
|
|
3528
|
+
if (method !== void 0)
|
|
3529
|
+
client.tokenEndpointAuthMethod = method;
|
|
3530
|
+
return client;
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3398
3533
|
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
3399
3534
|
import crypto from "node:crypto";
|
|
3400
3535
|
import path4 from "node:path";
|
|
@@ -4128,14 +4263,8 @@ function createAuthStoreClientStore(options, namespace) {
|
|
|
4128
4263
|
} catch {
|
|
4129
4264
|
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
4130
4265
|
}
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
const client = { clientId };
|
|
4134
|
-
if (isObjectRecord(parsed) && Object.prototype.hasOwnProperty.call(parsed, "clientSecret")) {
|
|
4135
|
-
client.clientSecret = getOwnEntry3(parsed, "clientSecret");
|
|
4136
|
-
}
|
|
4137
|
-
return client;
|
|
4138
|
-
}
|
|
4266
|
+
if (isObjectRecord(parsed) && typeof getOwnEntry3(parsed, "clientId") === "string")
|
|
4267
|
+
return parsed;
|
|
4139
4268
|
throw new Error("Stored OAuth client must be a JSON object with clientId");
|
|
4140
4269
|
},
|
|
4141
4270
|
async save(issuer, client) {
|
|
@@ -4201,14 +4330,7 @@ function isStoredOAuthSession(value) {
|
|
|
4201
4330
|
if (!isObjectRecord(value)) {
|
|
4202
4331
|
return false;
|
|
4203
4332
|
}
|
|
4204
|
-
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") &&
|
|
4205
|
-
}
|
|
4206
|
-
function isStoredOAuthClient(value) {
|
|
4207
|
-
if (!isObjectRecord(value) || !isNonBlankOwnString(value, "clientId")) {
|
|
4208
|
-
return false;
|
|
4209
|
-
}
|
|
4210
|
-
const clientSecret = getOwnEntry3(value, "clientSecret");
|
|
4211
|
-
return clientSecret === void 0 || typeof clientSecret === "string" && clientSecret.trim().length > 0;
|
|
4333
|
+
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && normalizeStoredOAuthClient(getOwnEntry3(value, "client")) !== null && isStoredOAuthDiscovery(getOwnEntry3(value, "discovery")) && (getOwnEntry3(value, "requestedScope") === void 0 || isNonBlankOwnString(value, "requestedScope")) && (getOwnEntry3(value, "refreshState") === void 0 || getOwnEntry3(value, "refreshState") === "pending" && getOwnEntry3(value, "tokens") === void 0) && isStoredOAuthTokensOrMissing(getOwnEntry3(value, "tokens"));
|
|
4212
4334
|
}
|
|
4213
4335
|
function isStoredOAuthDiscovery(value) {
|
|
4214
4336
|
if (!isObjectRecord(value)) {
|
|
@@ -4242,16 +4364,6 @@ function isNonBlankOwnString(record2, key2) {
|
|
|
4242
4364
|
return value !== void 0 && value.trim().length > 0;
|
|
4243
4365
|
}
|
|
4244
4366
|
|
|
4245
|
-
// ../mcp-oauth/dist/client/scope.js
|
|
4246
|
-
function normalizeOAuthScope(scope) {
|
|
4247
|
-
if (scope === void 0)
|
|
4248
|
-
return void 0;
|
|
4249
|
-
if (typeof scope !== "string" || [...scope].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) > 126 || char === '"' || char === "\\"))
|
|
4250
|
-
throw new Error("Invalid OAuth scope syntax");
|
|
4251
|
-
const normalized = [...new Set(scope.split(" ").filter(Boolean))].sort().join(" ");
|
|
4252
|
-
return normalized || void 0;
|
|
4253
|
-
}
|
|
4254
|
-
|
|
4255
4367
|
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
4256
4368
|
import { isIP } from "node:net";
|
|
4257
4369
|
|
|
@@ -4665,6 +4777,7 @@ async function exchangeAuthorizationCode(input) {
|
|
|
4665
4777
|
tokenEndpoint: input.tokenEndpoint,
|
|
4666
4778
|
clientId: input.clientId,
|
|
4667
4779
|
clientSecret: input.clientSecret,
|
|
4780
|
+
tokenEndpointAuthMethod: input.tokenEndpointAuthMethod,
|
|
4668
4781
|
params: {
|
|
4669
4782
|
grant_type: "authorization_code",
|
|
4670
4783
|
code: input.code,
|
|
@@ -4683,6 +4796,7 @@ async function refreshAccessToken(input) {
|
|
|
4683
4796
|
tokenEndpoint: input.tokenEndpoint,
|
|
4684
4797
|
clientId: input.clientId,
|
|
4685
4798
|
clientSecret: input.clientSecret,
|
|
4799
|
+
tokenEndpointAuthMethod: input.tokenEndpointAuthMethod,
|
|
4686
4800
|
params: {
|
|
4687
4801
|
grant_type: "refresh_token",
|
|
4688
4802
|
refresh_token: input.refreshToken,
|
|
@@ -4694,21 +4808,26 @@ async function refreshAccessToken(input) {
|
|
|
4694
4808
|
});
|
|
4695
4809
|
}
|
|
4696
4810
|
async function requestTokens(input) {
|
|
4697
|
-
const
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4811
|
+
const method = normalizeOAuthTokenEndpointAuthMethod(input.tokenEndpointAuthMethod) ?? (input.clientSecret === void 0 ? "none" : "client_secret_post");
|
|
4812
|
+
if (method !== "none" && (input.clientSecret === void 0 || input.clientSecret.trim() === ""))
|
|
4813
|
+
throw new Error("OAuth token endpoint authentication requires a client secret");
|
|
4814
|
+
const body = new URLSearchParams(input.params);
|
|
4815
|
+
const headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded" });
|
|
4816
|
+
if (method === "client_secret_basic") {
|
|
4817
|
+
const encoded = new URLSearchParams({ credential: input.clientId }).toString().slice("credential=".length);
|
|
4818
|
+
const encodedSecret = new URLSearchParams({ credential: input.clientSecret }).toString().slice("credential=".length);
|
|
4819
|
+
headers.set("Authorization", `Basic ${Buffer.from(`${encoded}:${encodedSecret}`).toString("base64")}`);
|
|
4820
|
+
} else {
|
|
4821
|
+
body.set("client_id", input.clientId);
|
|
4822
|
+
if (method === "client_secret_post")
|
|
4823
|
+
body.set("client_secret", input.clientSecret);
|
|
4703
4824
|
}
|
|
4704
4825
|
input.signal?.throwIfAborted();
|
|
4705
4826
|
const deadline = AbortSignal.timeout(3e4);
|
|
4706
4827
|
const signal = input.signal === void 0 ? deadline : AbortSignal.any([input.signal, deadline]);
|
|
4707
4828
|
const response = await fetchMcpResponse(input.fetch, input.tokenEndpoint, {
|
|
4708
4829
|
method: "POST",
|
|
4709
|
-
headers
|
|
4710
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
4711
|
-
},
|
|
4830
|
+
headers,
|
|
4712
4831
|
body: body.toString(),
|
|
4713
4832
|
signal
|
|
4714
4833
|
});
|
|
@@ -4861,6 +4980,9 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4861
4980
|
assertPersistenceNamespace(options.persistenceNamespace);
|
|
4862
4981
|
const clientMetadata = getClientMetadata(options.client);
|
|
4863
4982
|
const requestedScope = clientMetadata?.scope;
|
|
4983
|
+
const configuredClient = normalizeConfiguredClient(options.client);
|
|
4984
|
+
const requestedTokenMethod = normalizeOAuthTokenEndpointAuthMethod(options.client.tokenEndpointAuthMethod);
|
|
4985
|
+
const configuredTokenMethod = requestedTokenMethod ?? configuredClient?.tokenEndpointAuthMethod;
|
|
4864
4986
|
const sessionStore = options.sessionStore ?? createAuthStoreSessionStore(options.authStore, options.persistenceNamespace);
|
|
4865
4987
|
const clientStore = options.authStore === void 0 ? null : createAuthStoreClientStore(options.authStore, options.persistenceNamespace);
|
|
4866
4988
|
const now = options.now ?? Date.now;
|
|
@@ -4878,7 +5000,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4878
5000
|
const initialGrant = options.initialGrant === void 0 ? void 0 : {
|
|
4879
5001
|
resource: canonicalizeResourceIndicator(options.initialGrant.resource),
|
|
4880
5002
|
tokens: normalizeStoredTokens(options.initialGrant.tokens),
|
|
4881
|
-
client:
|
|
5003
|
+
client: configuredClient
|
|
4882
5004
|
};
|
|
4883
5005
|
if (initialGrant !== void 0 && (initialGrant.tokens === void 0 || initialGrant.client === null))
|
|
4884
5006
|
throw new Error("OAuth initial grant requires valid tokens and the original client ID");
|
|
@@ -4982,13 +5104,15 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4982
5104
|
if (forceRefresh && rejectedTokens !== void 0 && (rejectedTokens === null || session?.tokens === void 0 || !sameTokenGrant(session.tokens, rejectedTokens)))
|
|
4983
5105
|
forceRefresh = false;
|
|
4984
5106
|
const sessionDiscovery = resolveDiscovery(discovery, session);
|
|
4985
|
-
if ((options.client.mode === "static" || initialGrant !== void 0) && session !== null && (session.tokens !== void 0 || session.refreshState === "pending")) {
|
|
4986
|
-
const configured =
|
|
5107
|
+
if ((options.client.mode === "static" || configuredClient?.registration !== void 0 || initialGrant !== void 0) && session !== null && (session.tokens !== void 0 || session.refreshState === "pending")) {
|
|
5108
|
+
const configured = configuredClient;
|
|
4987
5109
|
if (configured === null || configured.clientId !== session.client.clientId || configured.clientSecret !== session.client.clientSecret)
|
|
4988
5110
|
throw new Error("Stored session belongs to a different OAuth client; use separate persistence or explicitly reset it");
|
|
4989
5111
|
}
|
|
4990
5112
|
if (requestedScope !== void 0 && session?.tokens !== void 0 && normalizeOAuthScope(session.tokens.scope ?? session.requestedScope) !== requestedScope)
|
|
4991
5113
|
throw new Error("Stored session does not match the requested OAuth scope; authorize again or select separate persistence");
|
|
5114
|
+
if (configuredTokenMethod !== void 0 && session !== null && (session.tokens !== void 0 || session.refreshState === "pending") && (session.client.tokenEndpointAuthMethod ?? (session.client.clientSecret === void 0 ? "none" : "client_secret_post")) !== configuredTokenMethod)
|
|
5115
|
+
throw new Error("Stored session does not match the requested OAuth token endpoint authentication; select separate persistence or reset it");
|
|
4992
5116
|
if (session?.refreshState === "pending") {
|
|
4993
5117
|
if (!allowInteractive || options.allowInteractive === false || sessionDiscovery === void 0)
|
|
4994
5118
|
throw new Error("OAuth refresh outcome is unknown; authorize again before using this resource");
|
|
@@ -5021,6 +5145,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5021
5145
|
if (session.tokens?.refreshToken === void 0) {
|
|
5022
5146
|
return session;
|
|
5023
5147
|
}
|
|
5148
|
+
assertTokenEndpointAuthentication(session.client, discovery.authorizationServerMetadata);
|
|
5024
5149
|
const pendingSession = { ...clearSessionTokens(session), refreshState: "pending" };
|
|
5025
5150
|
await saveSession(resource, pendingSession);
|
|
5026
5151
|
signal?.throwIfAborted();
|
|
@@ -5032,6 +5157,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5032
5157
|
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
5033
5158
|
clientId: session.client.clientId,
|
|
5034
5159
|
clientSecret: session.client.clientSecret,
|
|
5160
|
+
tokenEndpointAuthMethod: session.client.tokenEndpointAuthMethod,
|
|
5035
5161
|
refreshToken: session.tokens.refreshToken,
|
|
5036
5162
|
resource,
|
|
5037
5163
|
fetch: fetch2,
|
|
@@ -5095,6 +5221,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5095
5221
|
let resolvedClient = null;
|
|
5096
5222
|
try {
|
|
5097
5223
|
resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch2, signal);
|
|
5224
|
+
assertTokenEndpointAuthentication(resolvedClient.client, discovery.authorizationServerMetadata);
|
|
5098
5225
|
const sessionWithoutTokens = {
|
|
5099
5226
|
resource,
|
|
5100
5227
|
authorizationServer: discovery.authorizationServer,
|
|
@@ -5118,6 +5245,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5118
5245
|
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
5119
5246
|
clientId: resolvedClient.client.clientId,
|
|
5120
5247
|
clientSecret: resolvedClient.client.clientSecret,
|
|
5248
|
+
tokenEndpointAuthMethod: resolvedClient.client.tokenEndpointAuthMethod,
|
|
5121
5249
|
code,
|
|
5122
5250
|
codeVerifier: verifier,
|
|
5123
5251
|
redirectUri: loopback.redirectUri,
|
|
@@ -5157,8 +5285,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5157
5285
|
}
|
|
5158
5286
|
async function resolveClient(existingSession, discovery, redirectUri, fetch2, parentSignal) {
|
|
5159
5287
|
parentSignal?.throwIfAborted();
|
|
5160
|
-
|
|
5161
|
-
if (options.client.mode === "static") {
|
|
5288
|
+
if (options.client.mode === "static" || configuredClient?.registration !== void 0) {
|
|
5162
5289
|
if (configuredClient === null) {
|
|
5163
5290
|
throw new Error("OAuth client_id must not be blank");
|
|
5164
5291
|
}
|
|
@@ -5205,7 +5332,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5205
5332
|
};
|
|
5206
5333
|
}
|
|
5207
5334
|
}
|
|
5208
|
-
const
|
|
5335
|
+
const supported = getSupportedTokenAuthMethods(discovery.authorizationServerMetadata);
|
|
5336
|
+
const registrationMethod = requestedTokenMethod ?? (supported === void 0 ? "none" : ["none", "client_secret_basic", "client_secret_post"].find((method) => supported.includes(method)));
|
|
5337
|
+
if (registrationMethod === void 0 || supported !== void 0 && !supported.includes(registrationMethod))
|
|
5338
|
+
throw new Error("Authorization server does not support the requested OAuth token endpoint authentication");
|
|
5339
|
+
const registrationBody = buildClientRegistrationBody(clientMetadata, redirectUri, registrationMethod);
|
|
5209
5340
|
const deadline = AbortSignal.timeout(3e4);
|
|
5210
5341
|
const signal = parentSignal === void 0 ? deadline : AbortSignal.any([parentSignal, deadline]);
|
|
5211
5342
|
const response = await fetchMcpResponse(fetch2, registrationEndpoint, {
|
|
@@ -5217,14 +5348,14 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5217
5348
|
signal
|
|
5218
5349
|
});
|
|
5219
5350
|
const payload = await readOAuthJsonObjectResponse(response, signal);
|
|
5220
|
-
const
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
}
|
|
5224
|
-
const clientSecret = getOwnString2(payload, "client_secret");
|
|
5351
|
+
const registration = parseOAuthClientRegistration(payload);
|
|
5352
|
+
const registeredSecret = getOwnString2(registration, "client_secret");
|
|
5353
|
+
const responseMethod = normalizeOAuthTokenEndpointAuthMethod(getOwnEntry6(registration, "token_endpoint_auth_method")) ?? requestedTokenMethod ?? (supported === void 0 ? void 0 : normalizeOAuthTokenEndpointAuthMethod(registrationMethod));
|
|
5225
5354
|
const registeredClient = {
|
|
5226
|
-
clientId:
|
|
5227
|
-
|
|
5355
|
+
clientId: registration.client_id.trim(),
|
|
5356
|
+
...registeredSecret === void 0 ? {} : { clientSecret: registeredSecret.trim() },
|
|
5357
|
+
...responseMethod === void 0 ? {} : { tokenEndpointAuthMethod: responseMethod },
|
|
5358
|
+
registration
|
|
5228
5359
|
};
|
|
5229
5360
|
await saveRegisteredClient(discovery.authorizationServer, registeredClient);
|
|
5230
5361
|
return {
|
|
@@ -5237,7 +5368,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5237
5368
|
return normalizeLoadedSession(await sessionStore.load(resource));
|
|
5238
5369
|
}
|
|
5239
5370
|
async function saveSession(resource, session) {
|
|
5240
|
-
await sessionStore.save(resource, session);
|
|
5371
|
+
await sessionStore.save(resource, structuredClone(session));
|
|
5241
5372
|
}
|
|
5242
5373
|
async function clearSession(resource) {
|
|
5243
5374
|
await sessionStore.clear(resource);
|
|
@@ -5250,7 +5381,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5250
5381
|
return null;
|
|
5251
5382
|
}
|
|
5252
5383
|
const client = await clientStore.load(issuer);
|
|
5253
|
-
const normalizedClient = client === null ? null :
|
|
5384
|
+
const normalizedClient = client === null ? null : normalizeStoredOAuthClient(client);
|
|
5254
5385
|
if (client !== null && normalizedClient === null) {
|
|
5255
5386
|
await clientStore.clear(issuer);
|
|
5256
5387
|
return null;
|
|
@@ -5323,7 +5454,7 @@ function normalizeLoadedSession(session) {
|
|
|
5323
5454
|
const refreshState = getOwnEntry6(session, "refreshState");
|
|
5324
5455
|
if (refreshState !== void 0 && (refreshState !== "pending" || getOwnEntry6(session, "tokens") !== void 0))
|
|
5325
5456
|
throw new Error("Stored OAuth refresh state is invalid");
|
|
5326
|
-
const client =
|
|
5457
|
+
const client = normalizeStoredOAuthClient(getOwnEntry6(session, "client"));
|
|
5327
5458
|
if (client === null) {
|
|
5328
5459
|
return { ...session, client: { clientId: "" }, tokens: void 0 };
|
|
5329
5460
|
}
|
|
@@ -5333,25 +5464,6 @@ function normalizeLoadedSession(session) {
|
|
|
5333
5464
|
tokens: normalizeStoredTokens(getOwnEntry6(session, "tokens"))
|
|
5334
5465
|
};
|
|
5335
5466
|
}
|
|
5336
|
-
function normalizeStoredClient(value) {
|
|
5337
|
-
if (!isObjectRecord3(value)) {
|
|
5338
|
-
return null;
|
|
5339
|
-
}
|
|
5340
|
-
const clientId = getOwnString2(value, "clientId");
|
|
5341
|
-
if (clientId === void 0 || clientId.trim().length === 0) {
|
|
5342
|
-
return null;
|
|
5343
|
-
}
|
|
5344
|
-
const normalizedClientId = clientId.trim();
|
|
5345
|
-
const clientSecret = getOwnEntry6(value, "clientSecret");
|
|
5346
|
-
if (clientSecret === void 0) {
|
|
5347
|
-
return { clientId: normalizedClientId };
|
|
5348
|
-
}
|
|
5349
|
-
if (typeof clientSecret !== "string" || clientSecret.trim().length === 0) {
|
|
5350
|
-
return null;
|
|
5351
|
-
}
|
|
5352
|
-
const normalizedClientSecret = clientSecret.trim();
|
|
5353
|
-
return { clientId: normalizedClientId, clientSecret: normalizedClientSecret };
|
|
5354
|
-
}
|
|
5355
5467
|
function normalizeStoredTokens(value) {
|
|
5356
5468
|
if (value === void 0 || !isObjectRecord3(value)) {
|
|
5357
5469
|
return void 0;
|
|
@@ -5387,12 +5499,12 @@ function getClientMetadata(client) {
|
|
|
5387
5499
|
};
|
|
5388
5500
|
}
|
|
5389
5501
|
function normalizeConfiguredClient(client) {
|
|
5390
|
-
const
|
|
5391
|
-
|
|
5502
|
+
const registration = client.registration === void 0 ? void 0 : parseOAuthClientRegistration(client.registration);
|
|
5503
|
+
const clientId = normalizeOptionalOAuthString(client.clientId) ?? registration?.client_id.trim();
|
|
5504
|
+
if (clientId === void 0)
|
|
5392
5505
|
return null;
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
return clientSecret === void 0 ? { clientId } : { clientId, clientSecret };
|
|
5506
|
+
const clientSecret = normalizeOptionalOAuthString(client.clientSecret) ?? (registration === void 0 ? void 0 : getOwnString2(registration, "client_secret")?.trim());
|
|
5507
|
+
return normalizeStoredOAuthClient({ clientId, clientSecret, registration, tokenEndpointAuthMethod: client.tokenEndpointAuthMethod });
|
|
5396
5508
|
}
|
|
5397
5509
|
function normalizeOptionalOAuthString(value) {
|
|
5398
5510
|
if (value === void 0) {
|
|
@@ -5492,12 +5604,28 @@ function assertRequestMatchesResource(requestUrl, resource) {
|
|
|
5492
5604
|
throw new Error(`OAuth request URL ${requestUrl} does not match discovered resource ${resource}`);
|
|
5493
5605
|
}
|
|
5494
5606
|
}
|
|
5495
|
-
function
|
|
5607
|
+
function getSupportedTokenAuthMethods(metadata) {
|
|
5608
|
+
const value = getOwnEntry6(metadata, "token_endpoint_auth_methods_supported");
|
|
5609
|
+
if (value === void 0)
|
|
5610
|
+
return void 0;
|
|
5611
|
+
if (!Array.isArray(value) || value.length > 128 || value.some((method) => typeof method !== "string"))
|
|
5612
|
+
throw new Error("Invalid OAuth token endpoint authentication metadata");
|
|
5613
|
+
return value;
|
|
5614
|
+
}
|
|
5615
|
+
function assertTokenEndpointAuthentication(client, metadata) {
|
|
5616
|
+
const method = client.tokenEndpointAuthMethod ?? (client.clientSecret === void 0 ? "none" : "client_secret_post");
|
|
5617
|
+
if (method !== "none" && client.clientSecret === void 0)
|
|
5618
|
+
throw new Error("OAuth token endpoint authentication requires a client secret");
|
|
5619
|
+
const supported = getSupportedTokenAuthMethods(metadata);
|
|
5620
|
+
if (supported !== void 0 && !supported.includes(method))
|
|
5621
|
+
throw new Error("Authorization server does not support the requested OAuth token endpoint authentication");
|
|
5622
|
+
}
|
|
5623
|
+
function buildClientRegistrationBody(metadata, redirectUri, tokenEndpointAuthMethod) {
|
|
5496
5624
|
const body = {
|
|
5497
5625
|
redirect_uris: [redirectUri],
|
|
5498
5626
|
grant_types: ["authorization_code", "refresh_token"],
|
|
5499
5627
|
response_types: ["code"],
|
|
5500
|
-
token_endpoint_auth_method:
|
|
5628
|
+
token_endpoint_auth_method: tokenEndpointAuthMethod
|
|
5501
5629
|
};
|
|
5502
5630
|
const clientName = metadata === void 0 ? void 0 : getOwnString2(metadata, "clientName");
|
|
5503
5631
|
const scope = metadata === void 0 ? void 0 : getOwnString2(metadata, "scope");
|