tiny-http-mcp-server 0.1.34 → 0.1.36
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 +23 -2
- package/node_modules/mcp-oauth/dist/client/client-registration.d.ts +2 -0
- package/node_modules/mcp-oauth/dist/client/client-registration.js +45 -0
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +58 -7
- package/node_modules/mcp-oauth/dist/client/types.d.ts +11 -1
- package/node_modules/mcp-oauth/dist/index.d.ts +1 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +11 -1
- package/node_modules/tiny-mcp-client/dist/index.js +607 -518
- package/package.json +1 -1
|
@@ -3395,6 +3395,311 @@ var SubscriptionManager = class {
|
|
|
3395
3395
|
}
|
|
3396
3396
|
};
|
|
3397
3397
|
|
|
3398
|
+
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
3399
|
+
import http from "node:http";
|
|
3400
|
+
|
|
3401
|
+
// ../mcp-oauth/dist/client/authorization-state.js
|
|
3402
|
+
import crypto from "node:crypto";
|
|
3403
|
+
function createAuthorizationState(input) {
|
|
3404
|
+
const payload = {
|
|
3405
|
+
v: 1,
|
|
3406
|
+
n: crypto.randomBytes(16).toString("base64url"),
|
|
3407
|
+
i: input.issuer,
|
|
3408
|
+
r: input.requireIssuer
|
|
3409
|
+
};
|
|
3410
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
3411
|
+
}
|
|
3412
|
+
function parseAuthorizationState(value) {
|
|
3413
|
+
if (value === null || value.length === 0) {
|
|
3414
|
+
return null;
|
|
3415
|
+
}
|
|
3416
|
+
try {
|
|
3417
|
+
const decoded = Buffer.from(value, "base64url").toString("utf8");
|
|
3418
|
+
const parsed = JSON.parse(decoded);
|
|
3419
|
+
if (!isObjectRecord(parsed)) {
|
|
3420
|
+
return null;
|
|
3421
|
+
}
|
|
3422
|
+
const version = getOwnEntry(parsed, "v");
|
|
3423
|
+
const nonce = getOwnEntry(parsed, "n");
|
|
3424
|
+
const issuer = getOwnEntry(parsed, "i");
|
|
3425
|
+
const requireIssuer = getOwnEntry(parsed, "r");
|
|
3426
|
+
if (version !== 1 || typeof nonce !== "string" || nonce.length === 0 || typeof issuer !== "string" || issuer.length === 0 || typeof requireIssuer !== "boolean") {
|
|
3427
|
+
return null;
|
|
3428
|
+
}
|
|
3429
|
+
return {
|
|
3430
|
+
issuer,
|
|
3431
|
+
requireIssuer
|
|
3432
|
+
};
|
|
3433
|
+
} catch {
|
|
3434
|
+
return null;
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
function isObjectRecord(value) {
|
|
3438
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3439
|
+
}
|
|
3440
|
+
function getOwnEntry(record2, key2) {
|
|
3441
|
+
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3444
|
+
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
3445
|
+
async function createLoopbackAuthorizationSession(options = {}) {
|
|
3446
|
+
options.signal?.throwIfAborted();
|
|
3447
|
+
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
3448
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
3449
|
+
throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
|
|
3450
|
+
const target = loopbackTarget(options);
|
|
3451
|
+
const server = options.createServer ? options.createServer() : http.createServer();
|
|
3452
|
+
const controller = new AbortController();
|
|
3453
|
+
let closed = false;
|
|
3454
|
+
let used = false;
|
|
3455
|
+
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
3456
|
+
const teardown = () => {
|
|
3457
|
+
if (closed)
|
|
3458
|
+
return;
|
|
3459
|
+
closed = true;
|
|
3460
|
+
clearTimeout(timer);
|
|
3461
|
+
options.signal?.removeEventListener("abort", callerAbort);
|
|
3462
|
+
server.closeAllConnections?.();
|
|
3463
|
+
server.close();
|
|
3464
|
+
};
|
|
3465
|
+
const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
3466
|
+
timer.unref?.();
|
|
3467
|
+
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
3468
|
+
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
3469
|
+
if (options.signal?.aborted)
|
|
3470
|
+
callerAbort();
|
|
3471
|
+
let port;
|
|
3472
|
+
try {
|
|
3473
|
+
port = await startServer(server, target.port, target.host, controller.signal);
|
|
3474
|
+
} catch (error) {
|
|
3475
|
+
controller.abort(error);
|
|
3476
|
+
throw error;
|
|
3477
|
+
}
|
|
3478
|
+
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
3479
|
+
return {
|
|
3480
|
+
redirectUri,
|
|
3481
|
+
async waitForCode(authorizationUrl) {
|
|
3482
|
+
controller.signal.throwIfAborted();
|
|
3483
|
+
if (used)
|
|
3484
|
+
throw new Error("OAuth authorization session has already been used");
|
|
3485
|
+
used = true;
|
|
3486
|
+
try {
|
|
3487
|
+
return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
|
|
3488
|
+
} finally {
|
|
3489
|
+
clearTimeout(timer);
|
|
3490
|
+
}
|
|
3491
|
+
},
|
|
3492
|
+
close() {
|
|
3493
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
3494
|
+
}
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3497
|
+
function loopbackTarget(options) {
|
|
3498
|
+
if (options.redirectUri !== void 0) {
|
|
3499
|
+
let url;
|
|
3500
|
+
try {
|
|
3501
|
+
url = new URL(options.redirectUri);
|
|
3502
|
+
} catch (cause) {
|
|
3503
|
+
throw new Error("Invalid OAuth loopback redirect URI", { cause });
|
|
3504
|
+
}
|
|
3505
|
+
const forbiddenQuery = ["code", "state", "error", "error_description", "iss"];
|
|
3506
|
+
if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.hash || url.port === "0" || forbiddenQuery.some((name) => url.searchParams.has(name)) || [...options.redirectUri].some((char) => char.codePointAt(0) <= 32) || options.callbackPath !== void 0 && options.callbackPath !== url.pathname)
|
|
3507
|
+
throw new Error("Invalid OAuth loopback redirect URI");
|
|
3508
|
+
return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
|
|
3509
|
+
}
|
|
3510
|
+
const callbackPath = options.callbackPath ?? "/callback";
|
|
3511
|
+
const parsed = new URL(callbackPath, "http://127.0.0.1");
|
|
3512
|
+
if (!callbackPath.startsWith("/") || parsed.origin !== "http://127.0.0.1" || parsed.pathname !== callbackPath || parsed.search || parsed.hash)
|
|
3513
|
+
throw new Error("Invalid OAuth loopback callback path");
|
|
3514
|
+
return { port: 0, host: "127.0.0.1", callbackPath };
|
|
3515
|
+
}
|
|
3516
|
+
async function startServer(server, port, host, signal) {
|
|
3517
|
+
signal.throwIfAborted();
|
|
3518
|
+
return new Promise((resolve, reject) => {
|
|
3519
|
+
const cleanup = () => {
|
|
3520
|
+
server.off("error", handleError);
|
|
3521
|
+
signal.removeEventListener("abort", aborted);
|
|
3522
|
+
};
|
|
3523
|
+
const handleError = (error) => {
|
|
3524
|
+
cleanup();
|
|
3525
|
+
reject(error);
|
|
3526
|
+
};
|
|
3527
|
+
const aborted = () => {
|
|
3528
|
+
cleanup();
|
|
3529
|
+
reject(signal.reason);
|
|
3530
|
+
};
|
|
3531
|
+
server.once("error", handleError);
|
|
3532
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
3533
|
+
try {
|
|
3534
|
+
server.listen(port, host, () => {
|
|
3535
|
+
cleanup();
|
|
3536
|
+
if (signal.aborted) {
|
|
3537
|
+
server.close();
|
|
3538
|
+
reject(signal.reason);
|
|
3539
|
+
return;
|
|
3540
|
+
}
|
|
3541
|
+
const address = server.address();
|
|
3542
|
+
if (address === null || typeof address === "string") {
|
|
3543
|
+
reject(new Error("OAuth listener has no TCP address"));
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
resolve(address.port);
|
|
3547
|
+
});
|
|
3548
|
+
} catch (error) {
|
|
3549
|
+
cleanup();
|
|
3550
|
+
reject(error);
|
|
3551
|
+
}
|
|
3552
|
+
});
|
|
3553
|
+
}
|
|
3554
|
+
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
|
|
3555
|
+
signal.throwIfAborted();
|
|
3556
|
+
const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
|
|
3557
|
+
return new Promise((resolve, reject) => {
|
|
3558
|
+
let settled = false;
|
|
3559
|
+
const settle = (fn) => {
|
|
3560
|
+
if (settled)
|
|
3561
|
+
return;
|
|
3562
|
+
settled = true;
|
|
3563
|
+
server.off("request", request);
|
|
3564
|
+
signal.removeEventListener("abort", aborted);
|
|
3565
|
+
fn();
|
|
3566
|
+
};
|
|
3567
|
+
const aborted = () => settle(() => reject(signal.reason));
|
|
3568
|
+
const request = (req, res) => {
|
|
3569
|
+
let url;
|
|
3570
|
+
try {
|
|
3571
|
+
url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
3572
|
+
} catch {
|
|
3573
|
+
res.writeHead(400);
|
|
3574
|
+
res.end("Invalid callback URL");
|
|
3575
|
+
return;
|
|
3576
|
+
}
|
|
3577
|
+
if (url.pathname !== callbackPath) {
|
|
3578
|
+
res.writeHead(404);
|
|
3579
|
+
res.end("Not found");
|
|
3580
|
+
return;
|
|
3581
|
+
}
|
|
3582
|
+
const callbackParameters = {
|
|
3583
|
+
code: url.searchParams.get("code"),
|
|
3584
|
+
error: url.searchParams.get("error"),
|
|
3585
|
+
errorDescription: url.searchParams.get("error_description"),
|
|
3586
|
+
state: url.searchParams.get("state"),
|
|
3587
|
+
iss: url.searchParams.get("iss")
|
|
3588
|
+
};
|
|
3589
|
+
try {
|
|
3590
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3591
|
+
if (callbackParameters.error !== null)
|
|
3592
|
+
throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
3593
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
3594
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3595
|
+
res.end(buildSuccessPage(options.landingPage));
|
|
3596
|
+
settle(() => resolve(code));
|
|
3597
|
+
} catch (error) {
|
|
3598
|
+
res.writeHead(400);
|
|
3599
|
+
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
3600
|
+
settle(() => reject(error));
|
|
3601
|
+
}
|
|
3602
|
+
};
|
|
3603
|
+
server.on("request", request);
|
|
3604
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
3605
|
+
if (options.readLine !== void 0) {
|
|
3606
|
+
void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
|
|
3607
|
+
if (settled)
|
|
3608
|
+
return;
|
|
3609
|
+
const callbackParameters = extractCallbackParametersFromInput(input);
|
|
3610
|
+
if (callbackParameters === null)
|
|
3611
|
+
throw new Error("OAuth callback missing authorization code");
|
|
3612
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3613
|
+
if (callbackParameters.error !== null)
|
|
3614
|
+
throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
3615
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
3616
|
+
settle(() => resolve(code));
|
|
3617
|
+
}).catch((error) => settle(() => reject(error)));
|
|
3618
|
+
}
|
|
3619
|
+
if (options.openBrowser !== void 0) {
|
|
3620
|
+
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
3621
|
+
}
|
|
3622
|
+
});
|
|
3623
|
+
}
|
|
3624
|
+
function extractCallbackParametersFromInput(input) {
|
|
3625
|
+
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
3626
|
+
if (trimmed.length === 0) {
|
|
3627
|
+
return null;
|
|
3628
|
+
}
|
|
3629
|
+
try {
|
|
3630
|
+
const url = new URL(trimmed);
|
|
3631
|
+
return {
|
|
3632
|
+
code: url.searchParams.get("code"),
|
|
3633
|
+
error: url.searchParams.get("error"),
|
|
3634
|
+
errorDescription: url.searchParams.get("error_description"),
|
|
3635
|
+
state: url.searchParams.get("state"),
|
|
3636
|
+
iss: url.searchParams.get("iss")
|
|
3637
|
+
};
|
|
3638
|
+
} catch {
|
|
3639
|
+
return {
|
|
3640
|
+
code: trimmed,
|
|
3641
|
+
error: null,
|
|
3642
|
+
errorDescription: null,
|
|
3643
|
+
state: null,
|
|
3644
|
+
iss: null
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
3649
|
+
const url = new URL(authorizationUrl);
|
|
3650
|
+
const state = url.searchParams.get("state");
|
|
3651
|
+
const parsedState = parseAuthorizationState(state);
|
|
3652
|
+
return {
|
|
3653
|
+
state,
|
|
3654
|
+
issuer: parsedState?.issuer ?? null,
|
|
3655
|
+
requireIssuer: parsedState?.requireIssuer ?? false
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
function validateAuthorizationCallbackParameters(callback, expected) {
|
|
3659
|
+
validateAuthorizationCallbackBinding(callback, expected);
|
|
3660
|
+
if (callback.code === null || callback.code.length === 0) {
|
|
3661
|
+
throw new Error("OAuth callback missing authorization code");
|
|
3662
|
+
}
|
|
3663
|
+
return callback.code;
|
|
3664
|
+
}
|
|
3665
|
+
function validateAuthorizationCallbackBinding(callback, expected) {
|
|
3666
|
+
if (expected.state !== null) {
|
|
3667
|
+
if (callback.state === null || callback.state.length === 0) {
|
|
3668
|
+
throw new Error("OAuth callback missing state");
|
|
3669
|
+
}
|
|
3670
|
+
if (callback.state !== expected.state) {
|
|
3671
|
+
throw new Error("OAuth callback state mismatch");
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
if (expected.requireIssuer) {
|
|
3675
|
+
if (callback.iss === null || callback.iss.length === 0) {
|
|
3676
|
+
throw new Error("OAuth callback missing issuer");
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
if (callback.iss !== null && callback.iss.length > 0 && expected.issuer !== null && callback.iss !== expected.issuer) {
|
|
3680
|
+
throw new Error("OAuth callback issuer mismatch");
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
function createAuthorizationError(error, description) {
|
|
3684
|
+
return new Error(`OAuth authorization failed: ${error} \u2014 ${description}`);
|
|
3685
|
+
}
|
|
3686
|
+
function escapeHtml(text) {
|
|
3687
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
3688
|
+
}
|
|
3689
|
+
function buildSuccessPage(landingPage) {
|
|
3690
|
+
const title = landingPage?.title ?? "Connected";
|
|
3691
|
+
const body = landingPage?.body ?? "You can close this tab and return to your terminal.";
|
|
3692
|
+
return [
|
|
3693
|
+
"<!DOCTYPE html>",
|
|
3694
|
+
`<html><head><meta charset=utf-8><title>${escapeHtml(title)}</title></head>`,
|
|
3695
|
+
'<body style="font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0">',
|
|
3696
|
+
'<div style="text-align:center">',
|
|
3697
|
+
`<h1>${escapeHtml(title)}</h1>`,
|
|
3698
|
+
`<p style="color:#666">${escapeHtml(body)}</p>`,
|
|
3699
|
+
"</div></body></html>"
|
|
3700
|
+
].join("");
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3398
3703
|
// ../mcp-oauth/dist/client/scope.js
|
|
3399
3704
|
function normalizeOAuthScope(scope) {
|
|
3400
3705
|
if (scope === void 0)
|
|
@@ -3512,6 +3817,17 @@ function normalizeStoredOAuthClient(value) {
|
|
|
3512
3817
|
if (typeof clientId !== "string" || clientId.trim() === "" || clientSecret !== void 0 && (typeof clientSecret !== "string" || clientSecret.trim() === ""))
|
|
3513
3818
|
return null;
|
|
3514
3819
|
const client = { clientId: clientId.trim(), ...clientSecret === void 0 ? {} : { clientSecret: clientSecret.trim() } };
|
|
3820
|
+
const requestedRedirectUri = Object.hasOwn(record2, "requestedRedirectUri") ? record2.requestedRedirectUri : void 0;
|
|
3821
|
+
if (requestedRedirectUri !== void 0) {
|
|
3822
|
+
try {
|
|
3823
|
+
if (typeof requestedRedirectUri !== "string")
|
|
3824
|
+
throw new Error("Invalid redirect identity");
|
|
3825
|
+
loopbackTarget({ redirectUri: requestedRedirectUri });
|
|
3826
|
+
} catch {
|
|
3827
|
+
throw new Error("Invalid stored OAuth registration redirect identity");
|
|
3828
|
+
}
|
|
3829
|
+
client.requestedRedirectUri = requestedRedirectUri;
|
|
3830
|
+
}
|
|
3515
3831
|
const method = normalizeOAuthTokenEndpointAuthMethod(Object.hasOwn(record2, "tokenEndpointAuthMethod") ? record2.tokenEndpointAuthMethod : void 0);
|
|
3516
3832
|
if (Object.hasOwn(record2, "registration") && record2.registration !== void 0) {
|
|
3517
3833
|
const registration = parseOAuthClientRegistration(record2.registration);
|
|
@@ -3529,9 +3845,38 @@ function normalizeStoredOAuthClient(value) {
|
|
|
3529
3845
|
client.tokenEndpointAuthMethod = method;
|
|
3530
3846
|
return client;
|
|
3531
3847
|
}
|
|
3848
|
+
function registrationMatchesRedirect(client, requestedUri, fresh = false) {
|
|
3849
|
+
if (client.requestedRedirectUri !== void 0)
|
|
3850
|
+
return client.requestedRedirectUri === requestedUri;
|
|
3851
|
+
const redirects = client.registration?.redirect_uris;
|
|
3852
|
+
if (redirects === void 0 || redirects === null || redirects.length === 0)
|
|
3853
|
+
return true;
|
|
3854
|
+
return redirects.some((returnedUri) => {
|
|
3855
|
+
if (returnedUri === requestedUri)
|
|
3856
|
+
return true;
|
|
3857
|
+
if (!fresh)
|
|
3858
|
+
return false;
|
|
3859
|
+
let returned, requested;
|
|
3860
|
+
try {
|
|
3861
|
+
returned = new URL(returnedUri);
|
|
3862
|
+
requested = new URL(requestedUri);
|
|
3863
|
+
} catch {
|
|
3864
|
+
return false;
|
|
3865
|
+
}
|
|
3866
|
+
if (requested.protocol !== "http:" || returned.protocol !== "http:" || !["127.0.0.1", "[::1]", "localhost"].includes(requested.hostname))
|
|
3867
|
+
return false;
|
|
3868
|
+
const sameHost = returned.hostname === requested.hostname;
|
|
3869
|
+
const normalizedIpv4 = requested.hostname === "127.0.0.1" && returned.hostname === "localhost" && returned.port === "";
|
|
3870
|
+
if (!sameHost && !normalizedIpv4)
|
|
3871
|
+
return false;
|
|
3872
|
+
returned.hostname = requested.hostname;
|
|
3873
|
+
returned.port = requested.port;
|
|
3874
|
+
return returned.href === requested.href;
|
|
3875
|
+
});
|
|
3876
|
+
}
|
|
3532
3877
|
|
|
3533
3878
|
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
3534
|
-
import
|
|
3879
|
+
import crypto2 from "node:crypto";
|
|
3535
3880
|
import path4 from "node:path";
|
|
3536
3881
|
|
|
3537
3882
|
// ../auth-store/dist/encrypted-file-store.js
|
|
@@ -3955,10 +4300,10 @@ function parseEncryptedDocument(raw) {
|
|
|
3955
4300
|
if (!isRecord2(parsed)) {
|
|
3956
4301
|
return null;
|
|
3957
4302
|
}
|
|
3958
|
-
const version =
|
|
3959
|
-
const iv =
|
|
3960
|
-
const authTag =
|
|
3961
|
-
const ciphertext =
|
|
4303
|
+
const version = getOwnEntry2(parsed, "version");
|
|
4304
|
+
const iv = getOwnEntry2(parsed, "iv");
|
|
4305
|
+
const authTag = getOwnEntry2(parsed, "authTag");
|
|
4306
|
+
const ciphertext = getOwnEntry2(parsed, "ciphertext");
|
|
3962
4307
|
if (version !== ENCRYPTION_VERSION) {
|
|
3963
4308
|
return null;
|
|
3964
4309
|
}
|
|
@@ -3978,7 +4323,7 @@ function parseEncryptedDocument(raw) {
|
|
|
3978
4323
|
function isRecord2(value) {
|
|
3979
4324
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
3980
4325
|
}
|
|
3981
|
-
function
|
|
4326
|
+
function getOwnEntry2(record2, key2) {
|
|
3982
4327
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
3983
4328
|
}
|
|
3984
4329
|
function isNotFoundError(error) {
|
|
@@ -4139,552 +4484,247 @@ function createSecurityCliFailure(operation, result) {
|
|
|
4139
4484
|
return new Error(`Failed to ${operation}: security exited with code ${exitCode}`);
|
|
4140
4485
|
}
|
|
4141
4486
|
function getCommandExitCode(result) {
|
|
4142
|
-
const value =
|
|
4487
|
+
const value = getOwnEntry3(result, "exitCode");
|
|
4143
4488
|
return typeof value === "number" && Number.isInteger(value) ? value : 1;
|
|
4144
4489
|
}
|
|
4145
4490
|
function getCommandOutput(result, key2) {
|
|
4146
|
-
const value =
|
|
4147
|
-
return typeof value === "string" ? value : "";
|
|
4148
|
-
}
|
|
4149
|
-
function getOwnEntry2(record2, key2) {
|
|
4150
|
-
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4151
|
-
}
|
|
4152
|
-
|
|
4153
|
-
// ../auth-store/dist/create-secret-store.js
|
|
4154
|
-
var DEFAULT_BACKEND_ENV_VAR = "AUTH_BACKEND";
|
|
4155
|
-
var MACOS_PLATFORM = "darwin";
|
|
4156
|
-
var storeFactories = {
|
|
4157
|
-
file: (input) => {
|
|
4158
|
-
if (!input.fileStore) {
|
|
4159
|
-
throw new Error("fileStore configuration is required for file backend");
|
|
4160
|
-
}
|
|
4161
|
-
return new EncryptedFileStore(input.fileStore);
|
|
4162
|
-
},
|
|
4163
|
-
keychain: (input) => {
|
|
4164
|
-
if (!input.keychainStore) {
|
|
4165
|
-
throw new Error("keychainStore configuration is required for keychain backend");
|
|
4166
|
-
}
|
|
4167
|
-
return new KeychainStore(input.keychainStore);
|
|
4168
|
-
}
|
|
4169
|
-
};
|
|
4170
|
-
function createSecretStore(input) {
|
|
4171
|
-
const backend = resolveBackend(input);
|
|
4172
|
-
const platform = input.platform ?? process.platform;
|
|
4173
|
-
if (backend === "keychain" && platform !== MACOS_PLATFORM) {
|
|
4174
|
-
throw new Error(`Keychain backend is only supported on macOS. Current platform: ${platform}`);
|
|
4175
|
-
}
|
|
4176
|
-
const store = storeFactories[backend](input);
|
|
4177
|
-
return { backend, store };
|
|
4178
|
-
}
|
|
4179
|
-
function resolveBackend(input) {
|
|
4180
|
-
const envVar = input.backendEnvVar ?? DEFAULT_BACKEND_ENV_VAR;
|
|
4181
|
-
const configuredBackend = input.backend ?? getOwnEnvValue(input.env, envVar) ?? getOwnEnvValue(process.env, envVar);
|
|
4182
|
-
const backend = configuredBackend?.trim();
|
|
4183
|
-
if (backend === "keychain") {
|
|
4184
|
-
return "keychain";
|
|
4185
|
-
}
|
|
4186
|
-
if (backend === void 0 || backend === "file") {
|
|
4187
|
-
return "file";
|
|
4188
|
-
}
|
|
4189
|
-
throw new Error(`Unsupported auth store backend: ${backend}`);
|
|
4190
|
-
}
|
|
4191
|
-
function getOwnEnvValue(env, key2) {
|
|
4192
|
-
return env !== void 0 && Object.prototype.hasOwnProperty.call(env, key2) ? env[key2] : void 0;
|
|
4193
|
-
}
|
|
4194
|
-
|
|
4195
|
-
// ../mcp-oauth/dist/resource-indicator.js
|
|
4196
|
-
function canonicalizeResourceIndicator(value) {
|
|
4197
|
-
let url;
|
|
4198
|
-
try {
|
|
4199
|
-
url = value instanceof URL ? new URL(value.toString()) : new URL(value);
|
|
4200
|
-
} catch {
|
|
4201
|
-
throw new Error("Resource indicator must be an absolute URL");
|
|
4202
|
-
}
|
|
4203
|
-
url.hash = "";
|
|
4204
|
-
return url.toString();
|
|
4205
|
-
}
|
|
4206
|
-
|
|
4207
|
-
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
4208
|
-
var DEFAULT_FILE_SALT = "poe-code:mcp-oauth:v1";
|
|
4209
|
-
var DEFAULT_FILE_DIRECTORY = ".poe-code/mcp-oauth";
|
|
4210
|
-
var DEFAULT_KEYCHAIN_SERVICE = "poe-code-mcp-oauth";
|
|
4211
|
-
var DEFAULT_CLIENT_FILE_SALT = "poe-code:mcp-oauth:clients:v1";
|
|
4212
|
-
var DEFAULT_CLIENT_FILE_DIRECTORY = ".poe-code/mcp-oauth/clients";
|
|
4213
|
-
var DEFAULT_CLIENT_KEYCHAIN_SERVICE = "poe-code-mcp-oauth-clients";
|
|
4214
|
-
var MAX_JS_DATE_MS = 864e13;
|
|
4215
|
-
function createAuthStoreSessionStore(options = {}, namespace) {
|
|
4216
|
-
assertPersistenceNamespace(namespace);
|
|
4217
|
-
return {
|
|
4218
|
-
async withLock(resource, operation, lockOptions) {
|
|
4219
|
-
const store = createResourceSecretStore(resource, options, namespace);
|
|
4220
|
-
if (store.withLock === void 0)
|
|
4221
|
-
throw new Error("OAuth secret-store backend does not support transaction locks");
|
|
4222
|
-
return store.withLock(operation, lockOptions);
|
|
4223
|
-
},
|
|
4224
|
-
async load(resource) {
|
|
4225
|
-
const store = createResourceSecretStore(resource, options, namespace);
|
|
4226
|
-
const value = await store.get();
|
|
4227
|
-
if (value === null) {
|
|
4228
|
-
return null;
|
|
4229
|
-
}
|
|
4230
|
-
let parsed;
|
|
4231
|
-
try {
|
|
4232
|
-
parsed = JSON.parse(value);
|
|
4233
|
-
} catch {
|
|
4234
|
-
throw new Error("Stored OAuth session must be valid JSON; reset the store explicitly to recover");
|
|
4235
|
-
}
|
|
4236
|
-
if (isStoredOAuthSession(parsed)) {
|
|
4237
|
-
return parsed;
|
|
4238
|
-
}
|
|
4239
|
-
throw new Error("Stored OAuth session must match the expected shape");
|
|
4240
|
-
},
|
|
4241
|
-
async save(resource, session) {
|
|
4242
|
-
const store = createResourceSecretStore(resource, options, namespace);
|
|
4243
|
-
await store.set(JSON.stringify(session));
|
|
4244
|
-
},
|
|
4245
|
-
async clear(resource) {
|
|
4246
|
-
const store = createResourceSecretStore(resource, options, namespace);
|
|
4247
|
-
await store.delete();
|
|
4248
|
-
}
|
|
4249
|
-
};
|
|
4250
|
-
}
|
|
4251
|
-
function createAuthStoreClientStore(options, namespace) {
|
|
4252
|
-
assertPersistenceNamespace(namespace);
|
|
4253
|
-
return {
|
|
4254
|
-
async load(issuer) {
|
|
4255
|
-
const store = createIssuerSecretStore(issuer, options, namespace);
|
|
4256
|
-
const value = await store.get();
|
|
4257
|
-
if (value === null) {
|
|
4258
|
-
return null;
|
|
4259
|
-
}
|
|
4260
|
-
let parsed;
|
|
4261
|
-
try {
|
|
4262
|
-
parsed = JSON.parse(value);
|
|
4263
|
-
} catch {
|
|
4264
|
-
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
4265
|
-
}
|
|
4266
|
-
if (isObjectRecord(parsed) && typeof getOwnEntry3(parsed, "clientId") === "string")
|
|
4267
|
-
return parsed;
|
|
4268
|
-
throw new Error("Stored OAuth client must be a JSON object with clientId");
|
|
4269
|
-
},
|
|
4270
|
-
async save(issuer, client) {
|
|
4271
|
-
const store = createIssuerSecretStore(issuer, options, namespace);
|
|
4272
|
-
await store.set(JSON.stringify(client));
|
|
4273
|
-
},
|
|
4274
|
-
async clear(issuer) {
|
|
4275
|
-
const store = createIssuerSecretStore(issuer, options, namespace);
|
|
4276
|
-
await store.delete();
|
|
4277
|
-
}
|
|
4278
|
-
};
|
|
4279
|
-
}
|
|
4280
|
-
function createNamedSecretStore(key2, options, defaults, namespace) {
|
|
4281
|
-
const hash = crypto.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
|
|
4282
|
-
const configuredFilePath = options.fileStore?.filePath;
|
|
4283
|
-
const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
|
|
4284
|
-
const fileStore = {
|
|
4285
|
-
...options.fileStore,
|
|
4286
|
-
throwOnInvalidDocument: true,
|
|
4287
|
-
filePath: parsedFilePath === null ? void 0 : path4.join(parsedFilePath.dir, `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`),
|
|
4288
|
-
salt: options.fileStore?.salt ?? defaults.salt,
|
|
4289
|
-
defaultDirectory: options.fileStore?.defaultDirectory || defaults.directory,
|
|
4290
|
-
defaultFileName: parsedFilePath === null ? `${hash}.enc` : `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`
|
|
4291
|
-
};
|
|
4292
|
-
const keychainStore = {
|
|
4293
|
-
...options.keychainStore,
|
|
4294
|
-
service: options.keychainStore?.service ?? defaults.service,
|
|
4295
|
-
account: `${options.keychainStore?.account ?? defaults.accountPrefix}:${hash}`
|
|
4296
|
-
};
|
|
4297
|
-
return createSecretStore({ ...options, fileStore, keychainStore }).store;
|
|
4298
|
-
}
|
|
4299
|
-
function createResourceSecretStore(resource, options, namespace) {
|
|
4300
|
-
return createNamedSecretStore(canonicalizeResourceIndicator(resource), options, {
|
|
4301
|
-
salt: DEFAULT_FILE_SALT,
|
|
4302
|
-
directory: DEFAULT_FILE_DIRECTORY,
|
|
4303
|
-
service: DEFAULT_KEYCHAIN_SERVICE,
|
|
4304
|
-
accountPrefix: "provider"
|
|
4305
|
-
}, namespace);
|
|
4306
|
-
}
|
|
4307
|
-
function createIssuerSecretStore(issuer, options, namespace) {
|
|
4308
|
-
return createNamedSecretStore(issuer, options, {
|
|
4309
|
-
salt: DEFAULT_CLIENT_FILE_SALT,
|
|
4310
|
-
directory: DEFAULT_CLIENT_FILE_DIRECTORY,
|
|
4311
|
-
service: DEFAULT_CLIENT_KEYCHAIN_SERVICE,
|
|
4312
|
-
accountPrefix: "issuer"
|
|
4313
|
-
}, namespace);
|
|
4314
|
-
}
|
|
4315
|
-
function assertPersistenceNamespace(namespace) {
|
|
4316
|
-
if (namespace !== void 0 && (typeof namespace !== "string" || namespace.trim() === "" || Buffer.byteLength(namespace, "utf8") > 1024))
|
|
4317
|
-
throw new Error("OAuth persistence namespace must be a nonempty string within 1024 bytes");
|
|
4318
|
-
}
|
|
4319
|
-
function isObjectRecord(value) {
|
|
4320
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4491
|
+
const value = getOwnEntry3(result, key2);
|
|
4492
|
+
return typeof value === "string" ? value : "";
|
|
4321
4493
|
}
|
|
4322
4494
|
function getOwnEntry3(record2, key2) {
|
|
4323
4495
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4324
4496
|
}
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4497
|
+
|
|
4498
|
+
// ../auth-store/dist/create-secret-store.js
|
|
4499
|
+
var DEFAULT_BACKEND_ENV_VAR = "AUTH_BACKEND";
|
|
4500
|
+
var MACOS_PLATFORM = "darwin";
|
|
4501
|
+
var storeFactories = {
|
|
4502
|
+
file: (input) => {
|
|
4503
|
+
if (!input.fileStore) {
|
|
4504
|
+
throw new Error("fileStore configuration is required for file backend");
|
|
4505
|
+
}
|
|
4506
|
+
return new EncryptedFileStore(input.fileStore);
|
|
4507
|
+
},
|
|
4508
|
+
keychain: (input) => {
|
|
4509
|
+
if (!input.keychainStore) {
|
|
4510
|
+
throw new Error("keychainStore configuration is required for keychain backend");
|
|
4511
|
+
}
|
|
4512
|
+
return new KeychainStore(input.keychainStore);
|
|
4332
4513
|
}
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4514
|
+
};
|
|
4515
|
+
function createSecretStore(input) {
|
|
4516
|
+
const backend = resolveBackend(input);
|
|
4517
|
+
const platform = input.platform ?? process.platform;
|
|
4518
|
+
if (backend === "keychain" && platform !== MACOS_PLATFORM) {
|
|
4519
|
+
throw new Error(`Keychain backend is only supported on macOS. Current platform: ${platform}`);
|
|
4338
4520
|
}
|
|
4339
|
-
|
|
4521
|
+
const store = storeFactories[backend](input);
|
|
4522
|
+
return { backend, store };
|
|
4340
4523
|
}
|
|
4341
|
-
function
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
if (
|
|
4346
|
-
return
|
|
4347
|
-
}
|
|
4348
|
-
if (!isNonBlankOwnString(value, "accessToken") || getOwnString(value, "tokenType") !== "Bearer") {
|
|
4349
|
-
return false;
|
|
4350
|
-
}
|
|
4351
|
-
const expiresAt = getOwnEntry3(value, "expiresAt");
|
|
4352
|
-
if (expiresAt !== null && (typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt > MAX_JS_DATE_MS || !Number.isFinite(new Date(expiresAt).getTime()))) {
|
|
4353
|
-
return false;
|
|
4524
|
+
function resolveBackend(input) {
|
|
4525
|
+
const envVar = input.backendEnvVar ?? DEFAULT_BACKEND_ENV_VAR;
|
|
4526
|
+
const configuredBackend = input.backend ?? getOwnEnvValue(input.env, envVar) ?? getOwnEnvValue(process.env, envVar);
|
|
4527
|
+
const backend = configuredBackend?.trim();
|
|
4528
|
+
if (backend === "keychain") {
|
|
4529
|
+
return "keychain";
|
|
4354
4530
|
}
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
return false;
|
|
4531
|
+
if (backend === void 0 || backend === "file") {
|
|
4532
|
+
return "file";
|
|
4358
4533
|
}
|
|
4359
|
-
|
|
4360
|
-
return scope === void 0 || typeof scope === "string" && scope.trim().length > 0;
|
|
4361
|
-
}
|
|
4362
|
-
function isNonBlankOwnString(record2, key2) {
|
|
4363
|
-
const value = getOwnString(record2, key2);
|
|
4364
|
-
return value !== void 0 && value.trim().length > 0;
|
|
4534
|
+
throw new Error(`Unsupported auth store backend: ${backend}`);
|
|
4365
4535
|
}
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
import { isIP } from "node:net";
|
|
4369
|
-
|
|
4370
|
-
// ../mcp-oauth/dist/http-fetch.js
|
|
4371
|
-
async function fetchMcpResponse(fetchImplementation, input, init = {}) {
|
|
4372
|
-
const response = await fetchImplementation(input, { ...init, redirect: "error" });
|
|
4373
|
-
if (response.redirected || response.type === "opaqueredirect") {
|
|
4374
|
-
void response.body?.cancel().catch(() => void 0);
|
|
4375
|
-
throw new Error("MCP HTTP redirects are not allowed");
|
|
4376
|
-
}
|
|
4377
|
-
return response;
|
|
4536
|
+
function getOwnEnvValue(env, key2) {
|
|
4537
|
+
return env !== void 0 && Object.prototype.hasOwnProperty.call(env, key2) ? env[key2] : void 0;
|
|
4378
4538
|
}
|
|
4379
4539
|
|
|
4380
|
-
// ../mcp-oauth/dist/
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
4384
|
-
import http from "node:http";
|
|
4385
|
-
|
|
4386
|
-
// ../mcp-oauth/dist/client/authorization-state.js
|
|
4387
|
-
import crypto2 from "node:crypto";
|
|
4388
|
-
function createAuthorizationState(input) {
|
|
4389
|
-
const payload = {
|
|
4390
|
-
v: 1,
|
|
4391
|
-
n: crypto2.randomBytes(16).toString("base64url"),
|
|
4392
|
-
i: input.issuer,
|
|
4393
|
-
r: input.requireIssuer
|
|
4394
|
-
};
|
|
4395
|
-
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
4396
|
-
}
|
|
4397
|
-
function parseAuthorizationState(value) {
|
|
4398
|
-
if (value === null || value.length === 0) {
|
|
4399
|
-
return null;
|
|
4400
|
-
}
|
|
4540
|
+
// ../mcp-oauth/dist/resource-indicator.js
|
|
4541
|
+
function canonicalizeResourceIndicator(value) {
|
|
4542
|
+
let url;
|
|
4401
4543
|
try {
|
|
4402
|
-
|
|
4403
|
-
const parsed = JSON.parse(decoded);
|
|
4404
|
-
if (!isObjectRecord2(parsed)) {
|
|
4405
|
-
return null;
|
|
4406
|
-
}
|
|
4407
|
-
const version = getOwnEntry4(parsed, "v");
|
|
4408
|
-
const nonce = getOwnEntry4(parsed, "n");
|
|
4409
|
-
const issuer = getOwnEntry4(parsed, "i");
|
|
4410
|
-
const requireIssuer = getOwnEntry4(parsed, "r");
|
|
4411
|
-
if (version !== 1 || typeof nonce !== "string" || nonce.length === 0 || typeof issuer !== "string" || issuer.length === 0 || typeof requireIssuer !== "boolean") {
|
|
4412
|
-
return null;
|
|
4413
|
-
}
|
|
4414
|
-
return {
|
|
4415
|
-
issuer,
|
|
4416
|
-
requireIssuer
|
|
4417
|
-
};
|
|
4544
|
+
url = value instanceof URL ? new URL(value.toString()) : new URL(value);
|
|
4418
4545
|
} catch {
|
|
4419
|
-
|
|
4546
|
+
throw new Error("Resource indicator must be an absolute URL");
|
|
4420
4547
|
}
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4424
|
-
}
|
|
4425
|
-
function getOwnEntry4(record2, key2) {
|
|
4426
|
-
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4548
|
+
url.hash = "";
|
|
4549
|
+
return url.toString();
|
|
4427
4550
|
}
|
|
4428
4551
|
|
|
4429
|
-
// ../mcp-oauth/dist/client/
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
let used = false;
|
|
4440
|
-
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
4441
|
-
const teardown = () => {
|
|
4442
|
-
if (closed)
|
|
4443
|
-
return;
|
|
4444
|
-
closed = true;
|
|
4445
|
-
clearTimeout(timer);
|
|
4446
|
-
options.signal?.removeEventListener("abort", callerAbort);
|
|
4447
|
-
server.closeAllConnections?.();
|
|
4448
|
-
server.close();
|
|
4449
|
-
};
|
|
4450
|
-
const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
4451
|
-
timer.unref?.();
|
|
4452
|
-
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
4453
|
-
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
4454
|
-
if (options.signal?.aborted)
|
|
4455
|
-
callerAbort();
|
|
4456
|
-
let port;
|
|
4457
|
-
try {
|
|
4458
|
-
port = await startServer(server, target.port, target.host, controller.signal);
|
|
4459
|
-
} catch (error) {
|
|
4460
|
-
controller.abort(error);
|
|
4461
|
-
throw error;
|
|
4462
|
-
}
|
|
4463
|
-
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
4552
|
+
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
4553
|
+
var DEFAULT_FILE_SALT = "poe-code:mcp-oauth:v1";
|
|
4554
|
+
var DEFAULT_FILE_DIRECTORY = ".poe-code/mcp-oauth";
|
|
4555
|
+
var DEFAULT_KEYCHAIN_SERVICE = "poe-code-mcp-oauth";
|
|
4556
|
+
var DEFAULT_CLIENT_FILE_SALT = "poe-code:mcp-oauth:clients:v1";
|
|
4557
|
+
var DEFAULT_CLIENT_FILE_DIRECTORY = ".poe-code/mcp-oauth/clients";
|
|
4558
|
+
var DEFAULT_CLIENT_KEYCHAIN_SERVICE = "poe-code-mcp-oauth-clients";
|
|
4559
|
+
var MAX_JS_DATE_MS = 864e13;
|
|
4560
|
+
function createAuthStoreSessionStore(options = {}, namespace) {
|
|
4561
|
+
assertPersistenceNamespace(namespace);
|
|
4464
4562
|
return {
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
4479
|
-
}
|
|
4480
|
-
};
|
|
4481
|
-
}
|
|
4482
|
-
function loopbackTarget(options) {
|
|
4483
|
-
if (options.redirectUri !== void 0) {
|
|
4484
|
-
let url;
|
|
4485
|
-
try {
|
|
4486
|
-
url = new URL(options.redirectUri);
|
|
4487
|
-
} catch (cause) {
|
|
4488
|
-
throw new Error("Invalid OAuth loopback redirect URI", { cause });
|
|
4489
|
-
}
|
|
4490
|
-
const forbiddenQuery = ["code", "state", "error", "error_description", "iss"];
|
|
4491
|
-
if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.hash || url.port === "0" || forbiddenQuery.some((name) => url.searchParams.has(name)) || [...options.redirectUri].some((char) => char.codePointAt(0) <= 32) || options.callbackPath !== void 0 && options.callbackPath !== url.pathname)
|
|
4492
|
-
throw new Error("Invalid OAuth loopback redirect URI");
|
|
4493
|
-
return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
|
|
4494
|
-
}
|
|
4495
|
-
const callbackPath = options.callbackPath ?? "/callback";
|
|
4496
|
-
const parsed = new URL(callbackPath, "http://127.0.0.1");
|
|
4497
|
-
if (!callbackPath.startsWith("/") || parsed.origin !== "http://127.0.0.1" || parsed.pathname !== callbackPath || parsed.search || parsed.hash)
|
|
4498
|
-
throw new Error("Invalid OAuth loopback callback path");
|
|
4499
|
-
return { port: 0, host: "127.0.0.1", callbackPath };
|
|
4500
|
-
}
|
|
4501
|
-
async function startServer(server, port, host, signal) {
|
|
4502
|
-
signal.throwIfAborted();
|
|
4503
|
-
return new Promise((resolve, reject) => {
|
|
4504
|
-
const cleanup = () => {
|
|
4505
|
-
server.off("error", handleError);
|
|
4506
|
-
signal.removeEventListener("abort", aborted);
|
|
4507
|
-
};
|
|
4508
|
-
const handleError = (error) => {
|
|
4509
|
-
cleanup();
|
|
4510
|
-
reject(error);
|
|
4511
|
-
};
|
|
4512
|
-
const aborted = () => {
|
|
4513
|
-
cleanup();
|
|
4514
|
-
reject(signal.reason);
|
|
4515
|
-
};
|
|
4516
|
-
server.once("error", handleError);
|
|
4517
|
-
signal.addEventListener("abort", aborted, { once: true });
|
|
4518
|
-
try {
|
|
4519
|
-
server.listen(port, host, () => {
|
|
4520
|
-
cleanup();
|
|
4521
|
-
if (signal.aborted) {
|
|
4522
|
-
server.close();
|
|
4523
|
-
reject(signal.reason);
|
|
4524
|
-
return;
|
|
4525
|
-
}
|
|
4526
|
-
const address = server.address();
|
|
4527
|
-
if (address === null || typeof address === "string") {
|
|
4528
|
-
reject(new Error("OAuth listener has no TCP address"));
|
|
4529
|
-
return;
|
|
4530
|
-
}
|
|
4531
|
-
resolve(address.port);
|
|
4532
|
-
});
|
|
4533
|
-
} catch (error) {
|
|
4534
|
-
cleanup();
|
|
4535
|
-
reject(error);
|
|
4536
|
-
}
|
|
4537
|
-
});
|
|
4538
|
-
}
|
|
4539
|
-
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
|
|
4540
|
-
signal.throwIfAborted();
|
|
4541
|
-
const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
|
|
4542
|
-
return new Promise((resolve, reject) => {
|
|
4543
|
-
let settled = false;
|
|
4544
|
-
const settle = (fn) => {
|
|
4545
|
-
if (settled)
|
|
4546
|
-
return;
|
|
4547
|
-
settled = true;
|
|
4548
|
-
server.off("request", request);
|
|
4549
|
-
signal.removeEventListener("abort", aborted);
|
|
4550
|
-
fn();
|
|
4551
|
-
};
|
|
4552
|
-
const aborted = () => settle(() => reject(signal.reason));
|
|
4553
|
-
const request = (req, res) => {
|
|
4554
|
-
let url;
|
|
4563
|
+
async withLock(resource, operation, lockOptions) {
|
|
4564
|
+
const store = createResourceSecretStore(resource, options, namespace);
|
|
4565
|
+
if (store.withLock === void 0)
|
|
4566
|
+
throw new Error("OAuth secret-store backend does not support transaction locks");
|
|
4567
|
+
return store.withLock(operation, lockOptions);
|
|
4568
|
+
},
|
|
4569
|
+
async load(resource) {
|
|
4570
|
+
const store = createResourceSecretStore(resource, options, namespace);
|
|
4571
|
+
const value = await store.get();
|
|
4572
|
+
if (value === null) {
|
|
4573
|
+
return null;
|
|
4574
|
+
}
|
|
4575
|
+
let parsed;
|
|
4555
4576
|
try {
|
|
4556
|
-
|
|
4577
|
+
parsed = JSON.parse(value);
|
|
4557
4578
|
} catch {
|
|
4558
|
-
|
|
4559
|
-
res.end("Invalid callback URL");
|
|
4560
|
-
return;
|
|
4579
|
+
throw new Error("Stored OAuth session must be valid JSON; reset the store explicitly to recover");
|
|
4561
4580
|
}
|
|
4562
|
-
if (
|
|
4563
|
-
|
|
4564
|
-
res.end("Not found");
|
|
4565
|
-
return;
|
|
4581
|
+
if (isStoredOAuthSession(parsed)) {
|
|
4582
|
+
return parsed;
|
|
4566
4583
|
}
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4584
|
+
throw new Error("Stored OAuth session must match the expected shape");
|
|
4585
|
+
},
|
|
4586
|
+
async save(resource, session) {
|
|
4587
|
+
const store = createResourceSecretStore(resource, options, namespace);
|
|
4588
|
+
await store.set(JSON.stringify(session));
|
|
4589
|
+
},
|
|
4590
|
+
async clear(resource) {
|
|
4591
|
+
const store = createResourceSecretStore(resource, options, namespace);
|
|
4592
|
+
await store.delete();
|
|
4593
|
+
}
|
|
4594
|
+
};
|
|
4595
|
+
}
|
|
4596
|
+
function createAuthStoreClientStore(options, namespace) {
|
|
4597
|
+
assertPersistenceNamespace(namespace);
|
|
4598
|
+
return {
|
|
4599
|
+
async load(issuer) {
|
|
4600
|
+
const store = createIssuerSecretStore(issuer, options, namespace);
|
|
4601
|
+
const value = await store.get();
|
|
4602
|
+
if (value === null) {
|
|
4603
|
+
return null;
|
|
4604
|
+
}
|
|
4605
|
+
let parsed;
|
|
4574
4606
|
try {
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
4579
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4580
|
-
res.end(buildSuccessPage(options.landingPage));
|
|
4581
|
-
settle(() => resolve(code));
|
|
4582
|
-
} catch (error) {
|
|
4583
|
-
res.writeHead(400);
|
|
4584
|
-
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
4585
|
-
settle(() => reject(error));
|
|
4607
|
+
parsed = JSON.parse(value);
|
|
4608
|
+
} catch {
|
|
4609
|
+
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
4586
4610
|
}
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
if (callbackParameters.error !== null)
|
|
4599
|
-
throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
4600
|
-
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
4601
|
-
settle(() => resolve(code));
|
|
4602
|
-
}).catch((error) => settle(() => reject(error)));
|
|
4603
|
-
}
|
|
4604
|
-
if (options.openBrowser !== void 0) {
|
|
4605
|
-
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
4611
|
+
if (isObjectRecord2(parsed) && typeof getOwnEntry4(parsed, "clientId") === "string")
|
|
4612
|
+
return parsed;
|
|
4613
|
+
throw new Error("Stored OAuth client must be a JSON object with clientId");
|
|
4614
|
+
},
|
|
4615
|
+
async save(issuer, client) {
|
|
4616
|
+
const store = createIssuerSecretStore(issuer, options, namespace);
|
|
4617
|
+
await store.set(JSON.stringify(client));
|
|
4618
|
+
},
|
|
4619
|
+
async clear(issuer) {
|
|
4620
|
+
const store = createIssuerSecretStore(issuer, options, namespace);
|
|
4621
|
+
await store.delete();
|
|
4606
4622
|
}
|
|
4607
|
-
}
|
|
4608
|
-
}
|
|
4609
|
-
function extractCallbackParametersFromInput(input) {
|
|
4610
|
-
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
4611
|
-
if (trimmed.length === 0) {
|
|
4612
|
-
return null;
|
|
4613
|
-
}
|
|
4614
|
-
try {
|
|
4615
|
-
const url = new URL(trimmed);
|
|
4616
|
-
return {
|
|
4617
|
-
code: url.searchParams.get("code"),
|
|
4618
|
-
error: url.searchParams.get("error"),
|
|
4619
|
-
errorDescription: url.searchParams.get("error_description"),
|
|
4620
|
-
state: url.searchParams.get("state"),
|
|
4621
|
-
iss: url.searchParams.get("iss")
|
|
4622
|
-
};
|
|
4623
|
-
} catch {
|
|
4624
|
-
return {
|
|
4625
|
-
code: trimmed,
|
|
4626
|
-
error: null,
|
|
4627
|
-
errorDescription: null,
|
|
4628
|
-
state: null,
|
|
4629
|
-
iss: null
|
|
4630
|
-
};
|
|
4631
|
-
}
|
|
4623
|
+
};
|
|
4632
4624
|
}
|
|
4633
|
-
function
|
|
4634
|
-
const
|
|
4635
|
-
const
|
|
4636
|
-
const
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4625
|
+
function createNamedSecretStore(key2, options, defaults, namespace) {
|
|
4626
|
+
const hash = crypto2.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
|
|
4627
|
+
const configuredFilePath = options.fileStore?.filePath;
|
|
4628
|
+
const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
|
|
4629
|
+
const fileStore = {
|
|
4630
|
+
...options.fileStore,
|
|
4631
|
+
throwOnInvalidDocument: true,
|
|
4632
|
+
filePath: parsedFilePath === null ? void 0 : path4.join(parsedFilePath.dir, `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`),
|
|
4633
|
+
salt: options.fileStore?.salt ?? defaults.salt,
|
|
4634
|
+
defaultDirectory: options.fileStore?.defaultDirectory || defaults.directory,
|
|
4635
|
+
defaultFileName: parsedFilePath === null ? `${hash}.enc` : `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`
|
|
4636
|
+
};
|
|
4637
|
+
const keychainStore = {
|
|
4638
|
+
...options.keychainStore,
|
|
4639
|
+
service: options.keychainStore?.service ?? defaults.service,
|
|
4640
|
+
account: `${options.keychainStore?.account ?? defaults.accountPrefix}:${hash}`
|
|
4641
4641
|
};
|
|
4642
|
+
return createSecretStore({ ...options, fileStore, keychainStore }).store;
|
|
4642
4643
|
}
|
|
4643
|
-
function
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4644
|
+
function createResourceSecretStore(resource, options, namespace) {
|
|
4645
|
+
return createNamedSecretStore(canonicalizeResourceIndicator(resource), options, {
|
|
4646
|
+
salt: DEFAULT_FILE_SALT,
|
|
4647
|
+
directory: DEFAULT_FILE_DIRECTORY,
|
|
4648
|
+
service: DEFAULT_KEYCHAIN_SERVICE,
|
|
4649
|
+
accountPrefix: "provider"
|
|
4650
|
+
}, namespace);
|
|
4651
|
+
}
|
|
4652
|
+
function createIssuerSecretStore(issuer, options, namespace) {
|
|
4653
|
+
return createNamedSecretStore(issuer, options, {
|
|
4654
|
+
salt: DEFAULT_CLIENT_FILE_SALT,
|
|
4655
|
+
directory: DEFAULT_CLIENT_FILE_DIRECTORY,
|
|
4656
|
+
service: DEFAULT_CLIENT_KEYCHAIN_SERVICE,
|
|
4657
|
+
accountPrefix: "issuer"
|
|
4658
|
+
}, namespace);
|
|
4659
|
+
}
|
|
4660
|
+
function assertPersistenceNamespace(namespace) {
|
|
4661
|
+
if (namespace !== void 0 && (typeof namespace !== "string" || namespace.trim() === "" || Buffer.byteLength(namespace, "utf8") > 1024))
|
|
4662
|
+
throw new Error("OAuth persistence namespace must be a nonempty string within 1024 bytes");
|
|
4663
|
+
}
|
|
4664
|
+
function isObjectRecord2(value) {
|
|
4665
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4666
|
+
}
|
|
4667
|
+
function getOwnEntry4(record2, key2) {
|
|
4668
|
+
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4669
|
+
}
|
|
4670
|
+
function getOwnString(record2, key2) {
|
|
4671
|
+
const value = getOwnEntry4(record2, key2);
|
|
4672
|
+
return typeof value === "string" ? value : void 0;
|
|
4673
|
+
}
|
|
4674
|
+
function isStoredOAuthSession(value) {
|
|
4675
|
+
if (!isObjectRecord2(value)) {
|
|
4676
|
+
return false;
|
|
4647
4677
|
}
|
|
4648
|
-
return
|
|
4678
|
+
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && normalizeStoredOAuthClient(getOwnEntry4(value, "client")) !== null && isStoredOAuthDiscovery(getOwnEntry4(value, "discovery")) && (getOwnEntry4(value, "requestedScope") === void 0 || isNonBlankOwnString(value, "requestedScope")) && (getOwnEntry4(value, "refreshState") === void 0 || getOwnEntry4(value, "refreshState") === "pending" && getOwnEntry4(value, "tokens") === void 0) && isStoredOAuthTokensOrMissing(getOwnEntry4(value, "tokens"));
|
|
4649
4679
|
}
|
|
4650
|
-
function
|
|
4651
|
-
if (
|
|
4652
|
-
|
|
4653
|
-
throw new Error("OAuth callback missing state");
|
|
4654
|
-
}
|
|
4655
|
-
if (callback.state !== expected.state) {
|
|
4656
|
-
throw new Error("OAuth callback state mismatch");
|
|
4657
|
-
}
|
|
4680
|
+
function isStoredOAuthDiscovery(value) {
|
|
4681
|
+
if (!isObjectRecord2(value)) {
|
|
4682
|
+
return false;
|
|
4658
4683
|
}
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4684
|
+
return isNonBlankOwnString(value, "resourceMetadataUrl") && isObjectRecord2(getOwnEntry4(value, "resourceMetadata")) && isObjectRecord2(getOwnEntry4(value, "authorizationServerMetadata"));
|
|
4685
|
+
}
|
|
4686
|
+
function isStoredOAuthTokensOrMissing(value) {
|
|
4687
|
+
if (value === void 0) {
|
|
4688
|
+
return true;
|
|
4663
4689
|
}
|
|
4664
|
-
if (
|
|
4665
|
-
|
|
4690
|
+
if (!isObjectRecord2(value)) {
|
|
4691
|
+
return false;
|
|
4666
4692
|
}
|
|
4693
|
+
if (!isNonBlankOwnString(value, "accessToken") || getOwnString(value, "tokenType") !== "Bearer") {
|
|
4694
|
+
return false;
|
|
4695
|
+
}
|
|
4696
|
+
const expiresAt = getOwnEntry4(value, "expiresAt");
|
|
4697
|
+
if (expiresAt !== null && (typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt > MAX_JS_DATE_MS || !Number.isFinite(new Date(expiresAt).getTime()))) {
|
|
4698
|
+
return false;
|
|
4699
|
+
}
|
|
4700
|
+
const refreshToken = getOwnEntry4(value, "refreshToken");
|
|
4701
|
+
if (refreshToken !== void 0 && (typeof refreshToken !== "string" || refreshToken.trim().length === 0)) {
|
|
4702
|
+
return false;
|
|
4703
|
+
}
|
|
4704
|
+
const scope = getOwnEntry4(value, "scope");
|
|
4705
|
+
return scope === void 0 || typeof scope === "string" && scope.trim().length > 0;
|
|
4667
4706
|
}
|
|
4668
|
-
function
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
function escapeHtml(text) {
|
|
4672
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4707
|
+
function isNonBlankOwnString(record2, key2) {
|
|
4708
|
+
const value = getOwnString(record2, key2);
|
|
4709
|
+
return value !== void 0 && value.trim().length > 0;
|
|
4673
4710
|
}
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4711
|
+
|
|
4712
|
+
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
4713
|
+
import { isIP } from "node:net";
|
|
4714
|
+
|
|
4715
|
+
// ../mcp-oauth/dist/http-fetch.js
|
|
4716
|
+
async function fetchMcpResponse(fetchImplementation, input, init = {}) {
|
|
4717
|
+
const response = await fetchImplementation(input, { ...init, redirect: "error" });
|
|
4718
|
+
if (response.redirected || response.type === "opaqueredirect") {
|
|
4719
|
+
void response.body?.cancel().catch(() => void 0);
|
|
4720
|
+
throw new Error("MCP HTTP redirects are not allowed");
|
|
4721
|
+
}
|
|
4722
|
+
return response;
|
|
4686
4723
|
}
|
|
4687
4724
|
|
|
4725
|
+
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
4726
|
+
import { URL as URL2 } from "node:url";
|
|
4727
|
+
|
|
4688
4728
|
// ../mcp-oauth/dist/client/pkce.js
|
|
4689
4729
|
import crypto3 from "node:crypto";
|
|
4690
4730
|
function generateCodeVerifier() {
|
|
@@ -4999,7 +5039,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4999
5039
|
}
|
|
5000
5040
|
const initialGrant = options.initialGrant === void 0 ? void 0 : {
|
|
5001
5041
|
resource: canonicalizeResourceIndicator(options.initialGrant.resource),
|
|
5002
|
-
tokens:
|
|
5042
|
+
tokens: normalizeImportedTokens(options.initialGrant.tokens, now),
|
|
5003
5043
|
client: configuredClient
|
|
5004
5044
|
};
|
|
5005
5045
|
if (initialGrant !== void 0 && (initialGrant.tokens === void 0 || initialGrant.client === null))
|
|
@@ -5077,6 +5117,10 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5077
5117
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
5078
5118
|
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
5079
5119
|
let session = await loadSession(canonicalResource);
|
|
5120
|
+
if (session !== null)
|
|
5121
|
+
assertRegistrationIssuer(session.client, session.authorizationServer);
|
|
5122
|
+
if (configuredClient !== null && discovery !== void 0)
|
|
5123
|
+
assertRegistrationIssuer(configuredClient, discovery.authorizationServer);
|
|
5080
5124
|
if (session !== null && initialGrant?.resource === canonicalResource)
|
|
5081
5125
|
initialGrantConsumed = true;
|
|
5082
5126
|
signal?.throwIfAborted();
|
|
@@ -5122,6 +5166,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5122
5166
|
return session;
|
|
5123
5167
|
}
|
|
5124
5168
|
if (session?.tokens?.refreshToken !== void 0 && sessionDiscovery !== void 0 && (forceRefresh || isExpired(session.tokens, now))) {
|
|
5169
|
+
if (hasExpiredClientSecret(session.client, now)) {
|
|
5170
|
+
if (!allowInteractive || options.allowInteractive === false || options.client.mode === "static" || configuredClient?.registration !== void 0)
|
|
5171
|
+
throw new Error("OAuth client secret has expired; authorize again or update the imported registration");
|
|
5172
|
+
return authorizeSession(canonicalResource, clearSessionTokens(session), sessionDiscovery, fetch2, signal);
|
|
5173
|
+
}
|
|
5125
5174
|
session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
|
|
5126
5175
|
if (session?.tokens !== void 0 && !isExpired(session.tokens, now)) {
|
|
5127
5176
|
return session;
|
|
@@ -5289,6 +5338,9 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5289
5338
|
if (configuredClient === null) {
|
|
5290
5339
|
throw new Error("OAuth client_id must not be blank");
|
|
5291
5340
|
}
|
|
5341
|
+
assertRegistrationIssuer(configuredClient, discovery.authorizationServer);
|
|
5342
|
+
if (hasExpiredClientSecret(configuredClient, now))
|
|
5343
|
+
throw new Error("OAuth client secret has expired; update the imported registration");
|
|
5292
5344
|
return {
|
|
5293
5345
|
kind: "static",
|
|
5294
5346
|
fromStoredRegistration: false,
|
|
@@ -5303,7 +5355,14 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5303
5355
|
client: configuredClient
|
|
5304
5356
|
};
|
|
5305
5357
|
}
|
|
5306
|
-
|
|
5358
|
+
let storedClient = await loadRegisteredClient(discovery.authorizationServer);
|
|
5359
|
+
if (storedClient !== null) {
|
|
5360
|
+
assertRegistrationIssuer(storedClient, discovery.authorizationServer);
|
|
5361
|
+
if (hasExpiredClientSecret(storedClient, now) || !registrationMatchesRedirect(storedClient, redirectUri)) {
|
|
5362
|
+
await clearRegisteredClient(discovery.authorizationServer);
|
|
5363
|
+
storedClient = null;
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5307
5366
|
if (storedClient !== null) {
|
|
5308
5367
|
return {
|
|
5309
5368
|
kind: "dynamic",
|
|
@@ -5312,7 +5371,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5312
5371
|
};
|
|
5313
5372
|
}
|
|
5314
5373
|
if (registrationEndpoint === void 0) {
|
|
5315
|
-
if (existingSession !== null && existingSession.client.clientId.length > 0) {
|
|
5374
|
+
if (existingSession !== null && existingSession.client.clientId.length > 0 && !hasExpiredClientSecret(existingSession.client, now) && registrationMatchesRedirect(existingSession.client, redirectUri)) {
|
|
5316
5375
|
return {
|
|
5317
5376
|
kind: "dynamic",
|
|
5318
5377
|
fromStoredRegistration: true,
|
|
@@ -5321,7 +5380,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5321
5380
|
}
|
|
5322
5381
|
throw new Error("Authorization server metadata is missing registration_endpoint");
|
|
5323
5382
|
}
|
|
5324
|
-
if (existingSession !== null && existingSession.client.clientId.length > 0) {
|
|
5383
|
+
if (existingSession !== null && existingSession.client.clientId.length > 0 && !hasExpiredClientSecret(existingSession.client, now) && registrationMatchesRedirect(existingSession.client, redirectUri)) {
|
|
5325
5384
|
const isConfiguredStaticFallback = configuredClient !== null && existingSession.client.clientId === configuredClient.clientId && existingSession.client.clientSecret === configuredClient.clientSecret;
|
|
5326
5385
|
if (!isConfiguredStaticFallback) {
|
|
5327
5386
|
await saveRegisteredClient(discovery.authorizationServer, existingSession.client);
|
|
@@ -5357,11 +5416,17 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5357
5416
|
...responseMethod === void 0 ? {} : { tokenEndpointAuthMethod: responseMethod },
|
|
5358
5417
|
registration
|
|
5359
5418
|
};
|
|
5360
|
-
|
|
5419
|
+
assertRegistrationIssuer(registeredClient, discovery.authorizationServer);
|
|
5420
|
+
if (hasExpiredClientSecret(registeredClient, now))
|
|
5421
|
+
throw new Error("OAuth client secret has expired in the registration response");
|
|
5422
|
+
if (!registrationMatchesRedirect(registeredClient, redirectUri, true))
|
|
5423
|
+
throw new Error("OAuth registration response does not match the requested redirect URI");
|
|
5424
|
+
const clientWithRedirect = { ...registeredClient, requestedRedirectUri: redirectUri };
|
|
5425
|
+
await saveRegisteredClient(discovery.authorizationServer, clientWithRedirect);
|
|
5361
5426
|
return {
|
|
5362
5427
|
kind: "dynamic",
|
|
5363
5428
|
fromStoredRegistration: false,
|
|
5364
|
-
client:
|
|
5429
|
+
client: clientWithRedirect
|
|
5365
5430
|
};
|
|
5366
5431
|
}
|
|
5367
5432
|
async function loadSession(resource) {
|
|
@@ -5464,6 +5529,19 @@ function normalizeLoadedSession(session) {
|
|
|
5464
5529
|
tokens: normalizeStoredTokens(getOwnEntry6(session, "tokens"))
|
|
5465
5530
|
};
|
|
5466
5531
|
}
|
|
5532
|
+
function normalizeImportedTokens(value, now) {
|
|
5533
|
+
if (!isObjectRecord3(value))
|
|
5534
|
+
return void 0;
|
|
5535
|
+
const absolute = getOwnEntry6(value, "expiresAt");
|
|
5536
|
+
const lifetime = getOwnEntry6(value, "expiresIn");
|
|
5537
|
+
const issuedAt = getOwnEntry6(value, "issuedAt");
|
|
5538
|
+
if (lifetime !== void 0 && (typeof lifetime !== "number" || !Number.isSafeInteger(lifetime) || lifetime < 0))
|
|
5539
|
+
throw new Error("OAuth initial grant has invalid relative expiry");
|
|
5540
|
+
if (issuedAt !== void 0 && (typeof issuedAt !== "number" || !Number.isSafeInteger(issuedAt) || Math.abs(issuedAt) > MAX_JS_DATE_MS3))
|
|
5541
|
+
throw new Error("OAuth initial grant has invalid issuance time");
|
|
5542
|
+
const expiresAt = absolute !== void 0 && absolute !== null ? absolute : lifetime === void 0 ? null : (issuedAt === void 0 ? now() : issuedAt) + lifetime * 1e3;
|
|
5543
|
+
return normalizeStoredTokens({ ...value, expiresAt });
|
|
5544
|
+
}
|
|
5467
5545
|
function normalizeStoredTokens(value) {
|
|
5468
5546
|
if (value === void 0 || !isObjectRecord3(value)) {
|
|
5469
5547
|
return void 0;
|
|
@@ -5604,6 +5682,17 @@ function assertRequestMatchesResource(requestUrl, resource) {
|
|
|
5604
5682
|
throw new Error(`OAuth request URL ${requestUrl} does not match discovered resource ${resource}`);
|
|
5605
5683
|
}
|
|
5606
5684
|
}
|
|
5685
|
+
function assertRegistrationIssuer(client, issuer) {
|
|
5686
|
+
const registrationIssuer = client.registration === void 0 ? void 0 : getOwnString2(client.registration, "issuer");
|
|
5687
|
+
if (registrationIssuer !== void 0 && registrationIssuer !== issuer)
|
|
5688
|
+
throw new Error("OAuth client registration issuer does not match the authorization server");
|
|
5689
|
+
}
|
|
5690
|
+
function hasExpiredClientSecret(client, now) {
|
|
5691
|
+
if (client.clientSecret === void 0 || client.tokenEndpointAuthMethod === "none" || client.registration === void 0)
|
|
5692
|
+
return false;
|
|
5693
|
+
const expiry = getOwnEntry6(client.registration, "client_secret_expires_at");
|
|
5694
|
+
return typeof expiry === "number" && expiry !== 0 && expiry <= now() / 1e3;
|
|
5695
|
+
}
|
|
5607
5696
|
function getSupportedTokenAuthMethods(metadata) {
|
|
5608
5697
|
const value = getOwnEntry6(metadata, "token_endpoint_auth_methods_supported");
|
|
5609
5698
|
if (value === void 0)
|