toolcraft 0.0.91 → 0.0.93
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/composition.json +60 -0
- package/dist/composition.d.ts +15 -0
- package/dist/composition.js +10 -0
- package/dist/composition.json +60 -0
- package/node_modules/@poe-code/agent-defs/package.json +9 -5
- package/node_modules/@poe-code/agent-human-in-loop/package.json +8 -4
- package/node_modules/@poe-code/agent-mcp-config/package.json +8 -4
- package/node_modules/@poe-code/config-mutations/package.json +8 -4
- package/node_modules/@poe-code/frontmatter/package.json +8 -4
- package/node_modules/@poe-code/process-runner/package.json +8 -4
- package/node_modules/@poe-code/task-list/package.json +8 -4
- package/node_modules/auth-store/package.json +3 -4
- package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +660 -2
- package/node_modules/tiny-mcp-client/dist/index.js +3870 -1
- package/node_modules/tiny-mcp-client/package.json +8 -4
- package/node_modules/tiny-mcp-client/scripts/build.mjs +63 -0
- package/node_modules/toolcraft-design/package.json +3 -7
- package/package.json +11 -7
- package/node_modules/mcp-oauth/LICENSE +0 -21
- package/node_modules/mcp-oauth/README.md +0 -31
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +0 -14
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +0 -169
- package/node_modules/mcp-oauth/dist/client/authorization-state.d.ts +0 -8
- package/node_modules/mcp-oauth/dist/client/authorization-state.js +0 -47
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.d.ts +0 -3
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +0 -627
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +0 -20
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +0 -207
- package/node_modules/mcp-oauth/dist/client/pkce.d.ts +0 -2
- package/node_modules/mcp-oauth/dist/client/pkce.js +0 -7
- package/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +0 -40
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +0 -164
- package/node_modules/mcp-oauth/dist/client/types.d.ts +0 -113
- package/node_modules/mcp-oauth/dist/client/types.js +0 -1
- package/node_modules/mcp-oauth/dist/index.d.ts +0 -10
- package/node_modules/mcp-oauth/dist/index.js +0 -7
- package/node_modules/mcp-oauth/dist/resource-indicator.d.ts +0 -1
- package/node_modules/mcp-oauth/dist/resource-indicator.js +0 -11
- package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.d.ts +0 -28
- package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +0 -304
- package/node_modules/mcp-oauth/dist/types.compile-check.d.ts +0 -1
- package/node_modules/mcp-oauth/dist/types.compile-check.js +0 -22
- package/node_modules/mcp-oauth/package.json +0 -32
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +0 -556
- package/node_modules/tiny-mcp-client/dist/internal.js +0 -2688
- package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.js +0 -37
- package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.js +0 -50
- package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.js +0 -50
- package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.js +0 -51
- package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.js +0 -99
- package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.js +0 -56
- package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.d.ts +0 -1
- package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.js +0 -145
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +0 -24
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +0 -382
|
@@ -1,627 +0,0 @@
|
|
|
1
|
-
import { URL } from "node:url";
|
|
2
|
-
import { createAuthStoreClientStore, createAuthStoreSessionStore } from "./auth-store-session-store.js";
|
|
3
|
-
import { createLoopbackAuthorizationSession } from "./loopback-authorization.js";
|
|
4
|
-
import { createAuthorizationState } from "./authorization-state.js";
|
|
5
|
-
import { generateCodeChallenge, generateCodeVerifier } from "./pkce.js";
|
|
6
|
-
import { exchangeAuthorizationCode, OAuthError, refreshAccessToken, isRetryableOAuthError, readOAuthJsonObjectResponse } from "./token-endpoint.js";
|
|
7
|
-
import { canonicalizeResourceIndicator } from "../resource-indicator.js";
|
|
8
|
-
const MAX_JS_DATE_MS = 8_640_000_000_000_000;
|
|
9
|
-
export function createOAuthClientProvider(options) {
|
|
10
|
-
if (isProviderOptions(options)) {
|
|
11
|
-
return options.provider;
|
|
12
|
-
}
|
|
13
|
-
return createDefaultOAuthClientProvider(options);
|
|
14
|
-
}
|
|
15
|
-
export function createDefaultOAuthClientProvider(options) {
|
|
16
|
-
const sessionStore = options.sessionStore ?? createAuthStoreSessionStore(options.authStore);
|
|
17
|
-
const clientStore = options.authStore === undefined ? null : createAuthStoreClientStore(options.authStore);
|
|
18
|
-
const now = options.now ?? Date.now;
|
|
19
|
-
const registeredClients = new Map();
|
|
20
|
-
const refreshPromises = new Map();
|
|
21
|
-
const authorizationPromises = new Map();
|
|
22
|
-
return {
|
|
23
|
-
async authorizeRequest(input) {
|
|
24
|
-
assertNoAccessTokenInUrl(input.requestUrl, "Protected resource request URL");
|
|
25
|
-
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
26
|
-
const session = await ensureAuthorizedSession(requestUrl, undefined, input.fetch, false);
|
|
27
|
-
const accessToken = session?.tokens?.accessToken;
|
|
28
|
-
if (session === null ||
|
|
29
|
-
accessToken === undefined ||
|
|
30
|
-
session.tokens === undefined ||
|
|
31
|
-
isExpired(session.tokens, now)) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
assertRequestMatchesResource(requestUrl, session.resource);
|
|
35
|
-
input.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
36
|
-
},
|
|
37
|
-
async handleUnauthorized(input) {
|
|
38
|
-
try {
|
|
39
|
-
assertNoAccessTokenInUrl(input.requestUrl, "Protected resource request URL");
|
|
40
|
-
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
41
|
-
const resource = canonicalizeResourceIndicator(input.discovery.resource);
|
|
42
|
-
assertRequestMatchesResource(requestUrl, resource);
|
|
43
|
-
const forceRefresh = hasCachedAccessToken(await loadSession(resource)) &&
|
|
44
|
-
input.challenge?.params.error === "invalid_token";
|
|
45
|
-
const session = await ensureAuthorizedSession(resource, {
|
|
46
|
-
...input.discovery,
|
|
47
|
-
resource
|
|
48
|
-
}, input.fetch, true, forceRefresh);
|
|
49
|
-
if (session?.tokens?.accessToken === undefined) {
|
|
50
|
-
return { action: "fail" };
|
|
51
|
-
}
|
|
52
|
-
return { action: "retry" };
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
return {
|
|
56
|
-
action: "fail",
|
|
57
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
async function ensureAuthorizedSession(resource, discovery, fetch, allowInteractive, forceRefresh = false) {
|
|
63
|
-
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
64
|
-
let session = await loadSession(canonicalResource);
|
|
65
|
-
const sessionDiscovery = resolveDiscovery(discovery, session);
|
|
66
|
-
if (session?.tokens !== undefined && !forceRefresh && !isExpired(session.tokens, now)) {
|
|
67
|
-
return session;
|
|
68
|
-
}
|
|
69
|
-
if (session?.tokens?.refreshToken !== undefined &&
|
|
70
|
-
sessionDiscovery !== undefined &&
|
|
71
|
-
(forceRefresh || isExpired(session.tokens, now))) {
|
|
72
|
-
session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch);
|
|
73
|
-
if (session?.tokens !== undefined && !isExpired(session.tokens, now)) {
|
|
74
|
-
return session;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (forceRefresh && session?.tokens !== undefined) {
|
|
78
|
-
session = clearSessionTokens(session);
|
|
79
|
-
await saveSession(canonicalResource, session);
|
|
80
|
-
}
|
|
81
|
-
if (!allowInteractive || sessionDiscovery === undefined) {
|
|
82
|
-
return session;
|
|
83
|
-
}
|
|
84
|
-
return authorizeSession(canonicalResource, session, sessionDiscovery, fetch);
|
|
85
|
-
}
|
|
86
|
-
async function refreshSession(resource, session, discovery, fetch) {
|
|
87
|
-
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
88
|
-
const inFlight = refreshPromises.get(resource);
|
|
89
|
-
if (inFlight !== undefined) {
|
|
90
|
-
return inFlight;
|
|
91
|
-
}
|
|
92
|
-
const promise = (async () => {
|
|
93
|
-
try {
|
|
94
|
-
if (session.tokens?.refreshToken === undefined) {
|
|
95
|
-
return session;
|
|
96
|
-
}
|
|
97
|
-
let refreshAttempted = false;
|
|
98
|
-
let refreshedTokens;
|
|
99
|
-
while (true) {
|
|
100
|
-
try {
|
|
101
|
-
refreshedTokens = await refreshAccessToken({
|
|
102
|
-
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
103
|
-
clientId: session.client.clientId,
|
|
104
|
-
clientSecret: session.client.clientSecret,
|
|
105
|
-
refreshToken: session.tokens.refreshToken,
|
|
106
|
-
resource,
|
|
107
|
-
fetch,
|
|
108
|
-
now
|
|
109
|
-
});
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
if (error instanceof OAuthError && error.error === "invalid_grant") {
|
|
114
|
-
const clearedSession = clearSessionTokens(session);
|
|
115
|
-
await saveSession(resource, clearedSession);
|
|
116
|
-
return clearedSession;
|
|
117
|
-
}
|
|
118
|
-
if (shouldReRegisterStoredDynamicClient(error, await loadRegisteredClient(discovery.authorizationServer), false)) {
|
|
119
|
-
await clearRegisteredClient(discovery.authorizationServer);
|
|
120
|
-
await clearSession(resource);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
if (!refreshAttempted && isRetryableOAuthError(error)) {
|
|
124
|
-
refreshAttempted = true;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
throw error;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const updatedSession = {
|
|
131
|
-
...session,
|
|
132
|
-
tokens: {
|
|
133
|
-
...refreshedTokens,
|
|
134
|
-
refreshToken: refreshedTokens.refreshToken ?? session.tokens.refreshToken
|
|
135
|
-
},
|
|
136
|
-
discovery: toStoredDiscovery(discovery)
|
|
137
|
-
};
|
|
138
|
-
await saveSession(resource, updatedSession);
|
|
139
|
-
return updatedSession;
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
refreshPromises.delete(resource);
|
|
143
|
-
}
|
|
144
|
-
})();
|
|
145
|
-
refreshPromises.set(resource, promise);
|
|
146
|
-
return promise;
|
|
147
|
-
}
|
|
148
|
-
async function authorizeSession(resource, existingSession, discovery, fetch) {
|
|
149
|
-
const inFlight = authorizationPromises.get(resource);
|
|
150
|
-
if (inFlight !== undefined) {
|
|
151
|
-
return inFlight;
|
|
152
|
-
}
|
|
153
|
-
const promise = (async () => {
|
|
154
|
-
assertS256PkceSupport(discovery.authorizationServerMetadata);
|
|
155
|
-
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
156
|
-
let currentSession = existingSession;
|
|
157
|
-
let transientRetryAttempted = false;
|
|
158
|
-
let reRegistrationAttempted = false;
|
|
159
|
-
while (true) {
|
|
160
|
-
const loopback = await createLoopbackAuthorizationSession({
|
|
161
|
-
openBrowser: options.browser.openBrowser,
|
|
162
|
-
readLine: options.browser.readLine,
|
|
163
|
-
createServer: options.browser.createServer,
|
|
164
|
-
landingPage: options.browser.landingPage
|
|
165
|
-
});
|
|
166
|
-
let resolvedClient = null;
|
|
167
|
-
try {
|
|
168
|
-
resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch);
|
|
169
|
-
const sessionWithoutTokens = {
|
|
170
|
-
resource,
|
|
171
|
-
authorizationServer: discovery.authorizationServer,
|
|
172
|
-
client: resolvedClient.client,
|
|
173
|
-
discovery: toStoredDiscovery(discovery)
|
|
174
|
-
};
|
|
175
|
-
await saveSession(resource, sessionWithoutTokens);
|
|
176
|
-
const verifier = generateCodeVerifier();
|
|
177
|
-
const challenge = generateCodeChallenge(verifier);
|
|
178
|
-
const authorizationUrl = buildAuthorizationUrl({
|
|
179
|
-
metadata: discovery.authorizationServerMetadata,
|
|
180
|
-
resource,
|
|
181
|
-
clientId: resolvedClient.client.clientId,
|
|
182
|
-
redirectUri: loopback.redirectUri,
|
|
183
|
-
codeChallenge: challenge,
|
|
184
|
-
clientMetadata: getClientMetadata(options.client)
|
|
185
|
-
});
|
|
186
|
-
const code = await loopback.waitForCode(authorizationUrl);
|
|
187
|
-
const tokens = await exchangeAuthorizationCode({
|
|
188
|
-
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
189
|
-
clientId: resolvedClient.client.clientId,
|
|
190
|
-
clientSecret: resolvedClient.client.clientSecret,
|
|
191
|
-
code,
|
|
192
|
-
codeVerifier: verifier,
|
|
193
|
-
redirectUri: loopback.redirectUri,
|
|
194
|
-
resource,
|
|
195
|
-
fetch,
|
|
196
|
-
now
|
|
197
|
-
});
|
|
198
|
-
const session = {
|
|
199
|
-
...sessionWithoutTokens,
|
|
200
|
-
tokens
|
|
201
|
-
};
|
|
202
|
-
await saveSession(resource, session);
|
|
203
|
-
return session;
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
if (shouldReRegisterStoredDynamicClient(error, resolvedClient, reRegistrationAttempted)) {
|
|
207
|
-
reRegistrationAttempted = true;
|
|
208
|
-
await clearRegisteredClient(discovery.authorizationServer);
|
|
209
|
-
await clearSession(resource);
|
|
210
|
-
currentSession = null;
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (!transientRetryAttempted && isRetryableOAuthError(error)) {
|
|
214
|
-
transientRetryAttempted = true;
|
|
215
|
-
await clearSession(resource);
|
|
216
|
-
currentSession = null;
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
throw error;
|
|
220
|
-
}
|
|
221
|
-
finally {
|
|
222
|
-
loopback.close();
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
})();
|
|
226
|
-
const finalPromise = promise.finally(() => {
|
|
227
|
-
authorizationPromises.delete(resource);
|
|
228
|
-
});
|
|
229
|
-
authorizationPromises.set(resource, finalPromise);
|
|
230
|
-
return finalPromise;
|
|
231
|
-
}
|
|
232
|
-
async function resolveClient(existingSession, discovery, redirectUri, fetch) {
|
|
233
|
-
const configuredClient = normalizeConfiguredClient(options.client);
|
|
234
|
-
if (options.client.mode === "static") {
|
|
235
|
-
if (configuredClient === null) {
|
|
236
|
-
throw new Error("OAuth client_id must not be blank");
|
|
237
|
-
}
|
|
238
|
-
return {
|
|
239
|
-
kind: "static",
|
|
240
|
-
fromStoredRegistration: false,
|
|
241
|
-
client: configuredClient
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
const registrationEndpoint = getOwnString(discovery.authorizationServerMetadata, "registration_endpoint");
|
|
245
|
-
if (registrationEndpoint === undefined && configuredClient !== null) {
|
|
246
|
-
return {
|
|
247
|
-
kind: "static",
|
|
248
|
-
fromStoredRegistration: false,
|
|
249
|
-
client: configuredClient
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
const storedClient = await loadRegisteredClient(discovery.authorizationServer);
|
|
253
|
-
if (storedClient !== null) {
|
|
254
|
-
return {
|
|
255
|
-
kind: "dynamic",
|
|
256
|
-
fromStoredRegistration: true,
|
|
257
|
-
client: storedClient
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
if (registrationEndpoint === undefined) {
|
|
261
|
-
if (existingSession !== null && existingSession.client.clientId.length > 0) {
|
|
262
|
-
return {
|
|
263
|
-
kind: "dynamic",
|
|
264
|
-
fromStoredRegistration: true,
|
|
265
|
-
client: existingSession.client
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
throw new Error("Authorization server metadata is missing registration_endpoint");
|
|
269
|
-
}
|
|
270
|
-
if (existingSession !== null && existingSession.client.clientId.length > 0) {
|
|
271
|
-
const isConfiguredStaticFallback = configuredClient !== null &&
|
|
272
|
-
existingSession.client.clientId === configuredClient.clientId &&
|
|
273
|
-
existingSession.client.clientSecret === configuredClient.clientSecret;
|
|
274
|
-
if (!isConfiguredStaticFallback) {
|
|
275
|
-
await saveRegisteredClient(discovery.authorizationServer, existingSession.client);
|
|
276
|
-
return {
|
|
277
|
-
kind: "dynamic",
|
|
278
|
-
fromStoredRegistration: true,
|
|
279
|
-
client: existingSession.client
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
const registrationBody = buildClientRegistrationBody(getClientMetadata(options.client), redirectUri);
|
|
284
|
-
const response = await fetch(registrationEndpoint, {
|
|
285
|
-
method: "POST",
|
|
286
|
-
headers: {
|
|
287
|
-
"Content-Type": "application/json"
|
|
288
|
-
},
|
|
289
|
-
body: JSON.stringify(registrationBody)
|
|
290
|
-
});
|
|
291
|
-
const payload = await readOAuthJsonObjectResponse(response);
|
|
292
|
-
const clientId = getOwnString(payload, "client_id");
|
|
293
|
-
if (clientId === undefined || clientId.trim().length === 0) {
|
|
294
|
-
throw new Error("OAuth client registration response missing client_id");
|
|
295
|
-
}
|
|
296
|
-
const clientSecret = getOwnString(payload, "client_secret");
|
|
297
|
-
const registeredClient = {
|
|
298
|
-
clientId: clientId.trim(),
|
|
299
|
-
clientSecret: clientSecret !== undefined && clientSecret.trim().length > 0
|
|
300
|
-
? clientSecret.trim()
|
|
301
|
-
: undefined
|
|
302
|
-
};
|
|
303
|
-
await saveRegisteredClient(discovery.authorizationServer, registeredClient);
|
|
304
|
-
return {
|
|
305
|
-
kind: "dynamic",
|
|
306
|
-
fromStoredRegistration: false,
|
|
307
|
-
client: registeredClient
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
async function loadSession(resource) {
|
|
311
|
-
return normalizeLoadedSession(await sessionStore.load(resource));
|
|
312
|
-
}
|
|
313
|
-
async function saveSession(resource, session) {
|
|
314
|
-
await sessionStore.save(resource, session);
|
|
315
|
-
}
|
|
316
|
-
async function clearSession(resource) {
|
|
317
|
-
await sessionStore.clear(resource);
|
|
318
|
-
}
|
|
319
|
-
async function loadRegisteredClient(issuer) {
|
|
320
|
-
if (registeredClients.has(issuer)) {
|
|
321
|
-
return registeredClients.get(issuer) ?? null;
|
|
322
|
-
}
|
|
323
|
-
if (clientStore === null) {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
const client = await clientStore.load(issuer);
|
|
327
|
-
const normalizedClient = client === null ? null : normalizeStoredClient(client);
|
|
328
|
-
if (client !== null && normalizedClient === null) {
|
|
329
|
-
await clientStore.clear(issuer);
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
registeredClients.set(issuer, normalizedClient);
|
|
333
|
-
return normalizedClient;
|
|
334
|
-
}
|
|
335
|
-
async function saveRegisteredClient(issuer, client) {
|
|
336
|
-
registeredClients.set(issuer, client);
|
|
337
|
-
if (clientStore !== null) {
|
|
338
|
-
await clientStore.save(issuer, client);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
async function clearRegisteredClient(issuer) {
|
|
342
|
-
registeredClients.delete(issuer);
|
|
343
|
-
if (clientStore !== null) {
|
|
344
|
-
await clientStore.clear(issuer);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
function isProviderOptions(options) {
|
|
349
|
-
return Object.prototype.hasOwnProperty.call(options, "provider");
|
|
350
|
-
}
|
|
351
|
-
function isExpired(tokens, now) {
|
|
352
|
-
return tokens.expiresAt !== null && tokens.expiresAt <= now();
|
|
353
|
-
}
|
|
354
|
-
function resolveDiscovery(discovery, session) {
|
|
355
|
-
if (discovery !== undefined) {
|
|
356
|
-
return {
|
|
357
|
-
...discovery,
|
|
358
|
-
resource: canonicalizeResourceIndicator(discovery.resource)
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
if (session === null) {
|
|
362
|
-
return undefined;
|
|
363
|
-
}
|
|
364
|
-
const metadata = session.discovery.authorizationServerMetadata;
|
|
365
|
-
const issuer = getOwnString(metadata, "issuer");
|
|
366
|
-
const authorizationEndpoint = getOwnString(metadata, "authorization_endpoint");
|
|
367
|
-
const tokenEndpoint = getOwnString(metadata, "token_endpoint");
|
|
368
|
-
const codeChallengeMethodsSupported = getOwnStringArray(metadata, "code_challenge_methods_supported");
|
|
369
|
-
if (issuer === undefined ||
|
|
370
|
-
authorizationEndpoint === undefined ||
|
|
371
|
-
tokenEndpoint === undefined ||
|
|
372
|
-
codeChallengeMethodsSupported === undefined ||
|
|
373
|
-
!codeChallengeMethodsSupported.includes("S256")) {
|
|
374
|
-
return undefined;
|
|
375
|
-
}
|
|
376
|
-
return {
|
|
377
|
-
resource: canonicalizeResourceIndicator(session.resource),
|
|
378
|
-
resourceMetadataUrl: session.discovery.resourceMetadataUrl,
|
|
379
|
-
resourceMetadata: session.discovery
|
|
380
|
-
.resourceMetadata,
|
|
381
|
-
authorizationServer: session.authorizationServer,
|
|
382
|
-
authorizationServerMetadataUrl: "",
|
|
383
|
-
authorizationServerMetadata: metadata
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
function clearSessionTokens(session) {
|
|
387
|
-
const nextSession = { ...session };
|
|
388
|
-
delete nextSession.tokens;
|
|
389
|
-
return nextSession;
|
|
390
|
-
}
|
|
391
|
-
function hasCachedAccessToken(session) {
|
|
392
|
-
return session?.tokens?.accessToken !== undefined;
|
|
393
|
-
}
|
|
394
|
-
function normalizeLoadedSession(session) {
|
|
395
|
-
if (session === null) {
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
|
-
const client = normalizeStoredClient(getOwnEntry(session, "client"));
|
|
399
|
-
if (client === null) {
|
|
400
|
-
return { ...session, client: { clientId: "" }, tokens: undefined };
|
|
401
|
-
}
|
|
402
|
-
return {
|
|
403
|
-
...session,
|
|
404
|
-
client,
|
|
405
|
-
tokens: normalizeStoredTokens(getOwnEntry(session, "tokens"))
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
function normalizeStoredClient(value) {
|
|
409
|
-
if (!isObjectRecord(value)) {
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
const clientId = getOwnString(value, "clientId");
|
|
413
|
-
if (clientId === undefined || clientId.trim().length === 0) {
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
const normalizedClientId = clientId.trim();
|
|
417
|
-
const clientSecret = getOwnEntry(value, "clientSecret");
|
|
418
|
-
if (clientSecret === undefined) {
|
|
419
|
-
return { clientId: normalizedClientId };
|
|
420
|
-
}
|
|
421
|
-
if (typeof clientSecret !== "string" || clientSecret.trim().length === 0) {
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
const normalizedClientSecret = clientSecret.trim();
|
|
425
|
-
return { clientId: normalizedClientId, clientSecret: normalizedClientSecret };
|
|
426
|
-
}
|
|
427
|
-
function normalizeStoredTokens(value) {
|
|
428
|
-
if (value === undefined || !isObjectRecord(value)) {
|
|
429
|
-
return undefined;
|
|
430
|
-
}
|
|
431
|
-
const accessToken = getOwnString(value, "accessToken");
|
|
432
|
-
const tokenType = getOwnString(value, "tokenType");
|
|
433
|
-
const expiresAt = getOwnEntry(value, "expiresAt");
|
|
434
|
-
const refreshToken = getOwnEntry(value, "refreshToken");
|
|
435
|
-
const scope = getOwnString(value, "scope");
|
|
436
|
-
const normalizedAccessToken = accessToken?.trim();
|
|
437
|
-
const normalizedRefreshToken = typeof refreshToken === "string" ? refreshToken.trim() : undefined;
|
|
438
|
-
const normalizedScope = scope?.trim();
|
|
439
|
-
if (accessToken === undefined ||
|
|
440
|
-
normalizedAccessToken === undefined ||
|
|
441
|
-
normalizedAccessToken.length === 0 ||
|
|
442
|
-
tokenType !== "Bearer" ||
|
|
443
|
-
!(expiresAt === null ||
|
|
444
|
-
(typeof expiresAt === "number" &&
|
|
445
|
-
Number.isSafeInteger(expiresAt) &&
|
|
446
|
-
expiresAt <= MAX_JS_DATE_MS &&
|
|
447
|
-
Number.isFinite(new Date(expiresAt).getTime()))) ||
|
|
448
|
-
(refreshToken !== undefined &&
|
|
449
|
-
(typeof refreshToken !== "string" ||
|
|
450
|
-
normalizedRefreshToken === undefined ||
|
|
451
|
-
normalizedRefreshToken.length === 0))) {
|
|
452
|
-
return undefined;
|
|
453
|
-
}
|
|
454
|
-
return {
|
|
455
|
-
accessToken: normalizedAccessToken,
|
|
456
|
-
tokenType,
|
|
457
|
-
expiresAt,
|
|
458
|
-
...(normalizedRefreshToken === undefined ? {} : { refreshToken: normalizedRefreshToken }),
|
|
459
|
-
...(normalizedScope === undefined || normalizedScope.length === 0
|
|
460
|
-
? {}
|
|
461
|
-
: { scope: normalizedScope })
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
function getClientMetadata(client) {
|
|
465
|
-
if (client.metadata === undefined) {
|
|
466
|
-
return undefined;
|
|
467
|
-
}
|
|
468
|
-
return {
|
|
469
|
-
clientName: normalizeOptionalOAuthString(client.metadata.clientName),
|
|
470
|
-
scope: normalizeOptionalOAuthString(client.metadata.scope),
|
|
471
|
-
softwareId: normalizeOptionalOAuthString(client.metadata.softwareId),
|
|
472
|
-
softwareVersion: normalizeOptionalOAuthString(client.metadata.softwareVersion)
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
function normalizeConfiguredClient(client) {
|
|
476
|
-
const clientId = normalizeOptionalOAuthString(client.clientId);
|
|
477
|
-
if (clientId === undefined) {
|
|
478
|
-
return null;
|
|
479
|
-
}
|
|
480
|
-
const clientSecret = normalizeOptionalOAuthString(client.clientSecret);
|
|
481
|
-
return clientSecret === undefined ? { clientId } : { clientId, clientSecret };
|
|
482
|
-
}
|
|
483
|
-
function normalizeOptionalOAuthString(value) {
|
|
484
|
-
if (value === undefined) {
|
|
485
|
-
return undefined;
|
|
486
|
-
}
|
|
487
|
-
const trimmed = value.trim();
|
|
488
|
-
return trimmed.length === 0 ? undefined : trimmed;
|
|
489
|
-
}
|
|
490
|
-
function getOwnEntry(record, key) {
|
|
491
|
-
return Object.prototype.hasOwnProperty.call(record, key)
|
|
492
|
-
? record[key]
|
|
493
|
-
: undefined;
|
|
494
|
-
}
|
|
495
|
-
function isObjectRecord(value) {
|
|
496
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
497
|
-
}
|
|
498
|
-
function getOwnString(record, key) {
|
|
499
|
-
const value = getOwnEntry(record, key);
|
|
500
|
-
return typeof value === "string" ? value : undefined;
|
|
501
|
-
}
|
|
502
|
-
function requireOwnString(record, key, label) {
|
|
503
|
-
const value = getOwnString(record, key);
|
|
504
|
-
if (value === undefined) {
|
|
505
|
-
throw new Error(`${label} is missing ${key}`);
|
|
506
|
-
}
|
|
507
|
-
return value;
|
|
508
|
-
}
|
|
509
|
-
function getOwnStringArray(record, key) {
|
|
510
|
-
const value = getOwnEntry(record, key);
|
|
511
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string")
|
|
512
|
-
? value
|
|
513
|
-
: undefined;
|
|
514
|
-
}
|
|
515
|
-
function buildAuthorizationUrl(input) {
|
|
516
|
-
const authorizationEndpoint = requireOwnString(input.metadata, "authorization_endpoint", "Authorization server metadata");
|
|
517
|
-
const issuer = requireOwnString(input.metadata, "issuer", "Authorization server metadata");
|
|
518
|
-
const url = new URL(authorizationEndpoint);
|
|
519
|
-
const resource = canonicalizeResourceIndicator(input.resource);
|
|
520
|
-
const state = createAuthorizationState({
|
|
521
|
-
issuer,
|
|
522
|
-
requireIssuer: getOwnEntry(input.metadata, "authorization_response_iss_parameter_supported") === true
|
|
523
|
-
});
|
|
524
|
-
url.searchParams.set("response_type", "code");
|
|
525
|
-
url.searchParams.set("client_id", input.clientId);
|
|
526
|
-
url.searchParams.set("redirect_uri", input.redirectUri);
|
|
527
|
-
url.searchParams.set("code_challenge", input.codeChallenge);
|
|
528
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
529
|
-
url.searchParams.set("resource", resource);
|
|
530
|
-
url.searchParams.set("state", state);
|
|
531
|
-
if (input.clientMetadata?.scope !== undefined && input.clientMetadata.scope.length > 0) {
|
|
532
|
-
url.searchParams.set("scope", input.clientMetadata.scope);
|
|
533
|
-
}
|
|
534
|
-
return url.toString();
|
|
535
|
-
}
|
|
536
|
-
function assertS256PkceSupport(metadata) {
|
|
537
|
-
if (!getOwnStringArray(metadata, "code_challenge_methods_supported")?.includes("S256")) {
|
|
538
|
-
throw new Error("Authorization server metadata must advertise code_challenge_methods_supported including S256");
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
function normalizeHostname(hostname) {
|
|
542
|
-
return hostname.endsWith(".") ? hostname.slice(0, -1).toLowerCase() : hostname.toLowerCase();
|
|
543
|
-
}
|
|
544
|
-
function isLoopbackHostname(hostname) {
|
|
545
|
-
const normalizedHostname = normalizeHostname(hostname);
|
|
546
|
-
return (normalizedHostname === "localhost" ||
|
|
547
|
-
normalizedHostname === "::1" ||
|
|
548
|
-
normalizedHostname.startsWith("127."));
|
|
549
|
-
}
|
|
550
|
-
function assertSecureUrl(value, label) {
|
|
551
|
-
const url = new URL(value);
|
|
552
|
-
if (url.protocol === "https:") {
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
if (url.protocol === "http:" && isLoopbackHostname(url.hostname)) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
throw new Error(`${label} must use https unless it targets a loopback host`);
|
|
559
|
-
}
|
|
560
|
-
function assertSecureOAuthFlowEndpoints(metadata) {
|
|
561
|
-
const authorizationEndpoint = requireOwnString(metadata, "authorization_endpoint", "Authorization server metadata");
|
|
562
|
-
const tokenEndpoint = requireOwnString(metadata, "token_endpoint", "Authorization server metadata");
|
|
563
|
-
const registrationEndpoint = getOwnString(metadata, "registration_endpoint");
|
|
564
|
-
assertNoAccessTokenInUrl(authorizationEndpoint, "Authorization endpoint");
|
|
565
|
-
assertNoAccessTokenInUrl(tokenEndpoint, "Token endpoint");
|
|
566
|
-
assertSecureUrl(authorizationEndpoint, "Authorization endpoint");
|
|
567
|
-
assertSecureUrl(tokenEndpoint, "Token endpoint");
|
|
568
|
-
if (registrationEndpoint !== undefined) {
|
|
569
|
-
assertNoAccessTokenInUrl(registrationEndpoint, "Registration endpoint");
|
|
570
|
-
assertSecureUrl(registrationEndpoint, "Registration endpoint");
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
function assertNoAccessTokenInUrl(value, label) {
|
|
574
|
-
const url = value instanceof URL ? new URL(value.toString()) : new URL(value);
|
|
575
|
-
if (url.searchParams.has("access_token")) {
|
|
576
|
-
throw new Error(`${label} must not include access_token in the URI`);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
function assertRequestMatchesResource(requestUrl, resource) {
|
|
580
|
-
if (requestUrl !== resource) {
|
|
581
|
-
throw new Error(`OAuth request URL ${requestUrl} does not match discovered resource ${resource}`);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
function buildClientRegistrationBody(metadata, redirectUri) {
|
|
585
|
-
const body = {
|
|
586
|
-
redirect_uris: [redirectUri],
|
|
587
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
588
|
-
response_types: ["code"],
|
|
589
|
-
token_endpoint_auth_method: "none"
|
|
590
|
-
};
|
|
591
|
-
const clientName = metadata === undefined ? undefined : getOwnString(metadata, "clientName");
|
|
592
|
-
const scope = metadata === undefined ? undefined : getOwnString(metadata, "scope");
|
|
593
|
-
const softwareId = metadata === undefined ? undefined : getOwnString(metadata, "softwareId");
|
|
594
|
-
const softwareVersion = metadata === undefined ? undefined : getOwnString(metadata, "softwareVersion");
|
|
595
|
-
if (clientName !== undefined && clientName.length > 0) {
|
|
596
|
-
body.client_name = clientName;
|
|
597
|
-
}
|
|
598
|
-
if (scope !== undefined && scope.length > 0) {
|
|
599
|
-
body.scope = scope;
|
|
600
|
-
}
|
|
601
|
-
if (softwareId !== undefined && softwareId.length > 0) {
|
|
602
|
-
body.software_id = softwareId;
|
|
603
|
-
}
|
|
604
|
-
if (softwareVersion !== undefined && softwareVersion.length > 0) {
|
|
605
|
-
body.software_version = softwareVersion;
|
|
606
|
-
}
|
|
607
|
-
return body;
|
|
608
|
-
}
|
|
609
|
-
function toStoredDiscovery(discovery) {
|
|
610
|
-
return {
|
|
611
|
-
resourceMetadataUrl: discovery.resourceMetadataUrl,
|
|
612
|
-
resourceMetadata: discovery.resourceMetadata,
|
|
613
|
-
authorizationServerMetadata: discovery.authorizationServerMetadata
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
function shouldReRegisterStoredDynamicClient(error, client, alreadyAttempted) {
|
|
617
|
-
if (!(error instanceof OAuthError) || error.error !== "invalid_client" || alreadyAttempted) {
|
|
618
|
-
return false;
|
|
619
|
-
}
|
|
620
|
-
if (client === null) {
|
|
621
|
-
return false;
|
|
622
|
-
}
|
|
623
|
-
if ("kind" in client) {
|
|
624
|
-
return client.kind === "dynamic" && client.fromStoredRegistration;
|
|
625
|
-
}
|
|
626
|
-
return true;
|
|
627
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import http from "node:http";
|
|
2
|
-
export interface OAuthLandingPage {
|
|
3
|
-
title: string;
|
|
4
|
-
body: string;
|
|
5
|
-
}
|
|
6
|
-
export interface LoopbackAuthorizationOptions {
|
|
7
|
-
openBrowser?: (url: string) => Promise<void>;
|
|
8
|
-
readLine?: () => Promise<string>;
|
|
9
|
-
createServer?: () => http.Server;
|
|
10
|
-
landingPage?: OAuthLandingPage;
|
|
11
|
-
callbackPath?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface LoopbackAuthorizationSession {
|
|
14
|
-
redirectUri: string;
|
|
15
|
-
waitForCode(authorizationUrl: string): Promise<string>;
|
|
16
|
-
close(): void;
|
|
17
|
-
}
|
|
18
|
-
export declare function createLoopbackAuthorizationSession(options?: LoopbackAuthorizationOptions): Promise<LoopbackAuthorizationSession>;
|
|
19
|
-
export declare function extractCodeFromInput(input: string): string | null;
|
|
20
|
-
export declare function buildSuccessPage(landingPage?: OAuthLandingPage): string;
|