fastmcp 4.23.11 → 4.23.13
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/FastMCP.cjs +3 -3
- package/dist/FastMCP.d.cts +2 -2
- package/dist/FastMCP.d.ts +2 -2
- package/dist/FastMCP.js +2 -2
- package/dist/{OAuthProvider-BcDb5bOz.d.cts → OAuthProvider-84BWWDBF.d.cts} +55 -1
- package/dist/{OAuthProvider-BcDb5bOz.d.ts → OAuthProvider-84BWWDBF.d.ts} +55 -1
- package/dist/auth/index.cjs +4 -2
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +10 -2
- package/dist/auth/index.d.ts +10 -2
- package/dist/auth/index.js +3 -1
- package/dist/bin/fastmcp.cjs +22 -2
- package/dist/bin/fastmcp.cjs.map +1 -1
- package/dist/bin/fastmcp.js +22 -2
- package/dist/bin/fastmcp.js.map +1 -1
- package/dist/{chunk-X5ITW7FV.cjs → chunk-4P2GDM7W.cjs} +74 -35
- package/dist/chunk-4P2GDM7W.cjs.map +1 -0
- package/dist/{chunk-4OWHPJEL.cjs → chunk-BV4PWTT6.cjs} +214 -43
- package/dist/chunk-BV4PWTT6.cjs.map +1 -0
- package/dist/{chunk-G44SPVP7.js → chunk-CAQLTSJ5.js} +211 -40
- package/dist/chunk-CAQLTSJ5.js.map +1 -0
- package/dist/{chunk-XUUJOVC7.js → chunk-ULNQF5KG.js} +57 -18
- package/dist/chunk-ULNQF5KG.js.map +1 -0
- package/dist/examples/custom-routes.cjs +3 -3
- package/dist/examples/custom-routes.js +2 -2
- package/dist/openapi/index.cjs +7 -7
- package/dist/openapi/index.d.cts +1 -1
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +2 -2
- package/package.json +2 -1
- package/dist/chunk-4OWHPJEL.cjs.map +0 -1
- package/dist/chunk-G44SPVP7.js.map +0 -1
- package/dist/chunk-X5ITW7FV.cjs.map +0 -1
- package/dist/chunk-XUUJOVC7.js.map +0 -1
|
@@ -39,6 +39,21 @@ var _zod = require('zod');
|
|
|
39
39
|
|
|
40
40
|
// src/auth/OAuthProxyStateStore.ts
|
|
41
41
|
|
|
42
|
+
|
|
43
|
+
// src/auth/types.ts
|
|
44
|
+
var DEFAULT_ACCESS_TOKEN_TTL = 3600;
|
|
45
|
+
var DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = 31536e3;
|
|
46
|
+
var DEFAULT_REFRESH_TOKEN_TTL = 2592e3;
|
|
47
|
+
var TOKEN_ENDPOINT_AUTH_METHODS = [
|
|
48
|
+
"client_secret_basic",
|
|
49
|
+
"client_secret_post",
|
|
50
|
+
"none"
|
|
51
|
+
];
|
|
52
|
+
var DEFAULT_AUTHORIZATION_CODE_TTL = 300;
|
|
53
|
+
var DEFAULT_TRANSACTION_TTL = 600;
|
|
54
|
+
var DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS = 1e4;
|
|
55
|
+
|
|
56
|
+
// src/auth/OAuthProxyStateStore.ts
|
|
42
57
|
var STORAGE_KEY_PREFIX = {
|
|
43
58
|
client: "client:",
|
|
44
59
|
code: "code:",
|
|
@@ -76,7 +91,11 @@ var proxyDcrClientStorageSchema = _zod.z.object({
|
|
|
76
91
|
metadata: dcrClientMetadataSchema.optional(),
|
|
77
92
|
redirectUris: _zod.z.array(_zod.z.string()),
|
|
78
93
|
registeredAt: storedDateSchema,
|
|
79
|
-
source: _zod.z.literal("cimd").optional()
|
|
94
|
+
source: _zod.z.literal("cimd").optional(),
|
|
95
|
+
// Optional: registrations written before it was recorded do not carry it.
|
|
96
|
+
// Listed so that it survives a read — an unlisted key is stripped, which
|
|
97
|
+
// would quietly turn every confidential client back into a legacy one.
|
|
98
|
+
tokenEndpointAuthMethod: _zod.z.enum(TOKEN_ENDPOINT_AUTH_METHODS).optional()
|
|
80
99
|
});
|
|
81
100
|
var upstreamTokenSetStorageSchema = _zod.z.object({
|
|
82
101
|
accessToken: _zod.z.string(),
|
|
@@ -277,14 +296,6 @@ var OAuthProxyStateStore = class {
|
|
|
277
296
|
}
|
|
278
297
|
};
|
|
279
298
|
|
|
280
|
-
// src/auth/types.ts
|
|
281
|
-
var DEFAULT_ACCESS_TOKEN_TTL = 3600;
|
|
282
|
-
var DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = 31536e3;
|
|
283
|
-
var DEFAULT_REFRESH_TOKEN_TTL = 2592e3;
|
|
284
|
-
var DEFAULT_AUTHORIZATION_CODE_TTL = 300;
|
|
285
|
-
var DEFAULT_TRANSACTION_TTL = 600;
|
|
286
|
-
var DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS = 1e4;
|
|
287
|
-
|
|
288
299
|
// src/cancelResponseBody.ts
|
|
289
300
|
var cancelResponseBody = async (response) => {
|
|
290
301
|
if (!response.body) {
|
|
@@ -484,7 +495,9 @@ var ClaimsExtractor = (_class = class {
|
|
|
484
495
|
"iss",
|
|
485
496
|
"jti",
|
|
486
497
|
"nbf",
|
|
487
|
-
"scope"
|
|
498
|
+
"scope",
|
|
499
|
+
// Whether a proxy token is an access or a refresh token.
|
|
500
|
+
"token_use"
|
|
488
501
|
])}
|
|
489
502
|
constructor(config) {;_class.prototype.__init.call(this);
|
|
490
503
|
if (typeof config === "boolean") {
|
|
@@ -973,7 +986,10 @@ var JWTIssuer = class {
|
|
|
973
986
|
jti,
|
|
974
987
|
scope,
|
|
975
988
|
// Merge additional claims (custom claims from upstream)
|
|
976
|
-
...additionalClaims || {}
|
|
989
|
+
...additionalClaims || {},
|
|
990
|
+
// After the spread: what kind of token this is is the proxy's call, and
|
|
991
|
+
// no additional claim may change it.
|
|
992
|
+
token_use: "access"
|
|
977
993
|
};
|
|
978
994
|
return this.signToken(claims);
|
|
979
995
|
}
|
|
@@ -992,7 +1008,9 @@ var JWTIssuer = class {
|
|
|
992
1008
|
jti,
|
|
993
1009
|
scope,
|
|
994
1010
|
// Merge additional claims (custom claims from upstream)
|
|
995
|
-
...additionalClaims || {}
|
|
1011
|
+
...additionalClaims || {},
|
|
1012
|
+
// After the spread — see issueAccessToken.
|
|
1013
|
+
token_use: "refresh"
|
|
996
1014
|
};
|
|
997
1015
|
return this.signToken(claims);
|
|
998
1016
|
}
|
|
@@ -1332,6 +1350,10 @@ var RESERVED_AUTHORIZATION_PARAMS = /* @__PURE__ */ new Set([
|
|
|
1332
1350
|
"scope",
|
|
1333
1351
|
"state"
|
|
1334
1352
|
]);
|
|
1353
|
+
var passthroughRefreshBindingSchema = _zod.z.object({
|
|
1354
|
+
clientAuthenticated: _zod.z.boolean(),
|
|
1355
|
+
clientId: _zod.z.string()
|
|
1356
|
+
});
|
|
1335
1357
|
var OAuthProxy = (_class4 = class {
|
|
1336
1358
|
__init5() {this.claimsExtractor = null}
|
|
1337
1359
|
__init6() {this.cleanupInterval = null}
|
|
@@ -1453,17 +1475,18 @@ var OAuthProxy = (_class4 = class {
|
|
|
1453
1475
|
"code_challenge_method 'plain' is not supported; use S256"
|
|
1454
1476
|
);
|
|
1455
1477
|
}
|
|
1456
|
-
if (registeredClient
|
|
1478
|
+
if (isPublicClient(registeredClient)) {
|
|
1479
|
+
const kind = describeUnauthenticatedClient(registeredClient);
|
|
1457
1480
|
if (!params.code_challenge) {
|
|
1458
1481
|
throw new OAuthProxyError(
|
|
1459
1482
|
"invalid_request",
|
|
1460
|
-
|
|
1483
|
+
`code_challenge is required for ${kind}`
|
|
1461
1484
|
);
|
|
1462
1485
|
}
|
|
1463
1486
|
if (params.code_challenge_method !== "S256") {
|
|
1464
1487
|
throw new OAuthProxyError(
|
|
1465
1488
|
"invalid_request",
|
|
1466
|
-
|
|
1489
|
+
`code_challenge_method must be S256 for ${kind}`
|
|
1467
1490
|
);
|
|
1468
1491
|
}
|
|
1469
1492
|
}
|
|
@@ -1497,10 +1520,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1497
1520
|
"Only authorization_code grant type is supported"
|
|
1498
1521
|
);
|
|
1499
1522
|
}
|
|
1500
|
-
const
|
|
1501
|
-
if (!registeredClient) {
|
|
1502
|
-
throw new OAuthProxyError("invalid_client", "Unknown client_id");
|
|
1503
|
-
}
|
|
1523
|
+
const { authenticated, client } = await this.authenticateClient(request);
|
|
1504
1524
|
const consumed = await this.stateStore.consumeClientCode(request.code);
|
|
1505
1525
|
if (!consumed) {
|
|
1506
1526
|
throw new OAuthProxyError(
|
|
@@ -1516,13 +1536,16 @@ var OAuthProxy = (_class4 = class {
|
|
|
1516
1536
|
);
|
|
1517
1537
|
}
|
|
1518
1538
|
const clientCode = consumed.clientCode;
|
|
1519
|
-
if (clientCode.clientId !==
|
|
1520
|
-
throw new OAuthProxyError(
|
|
1539
|
+
if (clientCode.clientId !== client.clientId) {
|
|
1540
|
+
throw new OAuthProxyError(
|
|
1541
|
+
"invalid_grant",
|
|
1542
|
+
"Authorization code was issued to another client"
|
|
1543
|
+
);
|
|
1521
1544
|
}
|
|
1522
|
-
if (
|
|
1545
|
+
if (!authenticated && (!clientCode.codeChallenge || clientCode.codeChallengeMethod !== "S256")) {
|
|
1523
1546
|
throw new OAuthProxyError(
|
|
1524
1547
|
"invalid_grant",
|
|
1525
|
-
|
|
1548
|
+
`S256 PKCE is required for ${describeUnauthenticatedClient(client)}`
|
|
1526
1549
|
);
|
|
1527
1550
|
}
|
|
1528
1551
|
if (clientCode.codeChallenge) {
|
|
@@ -1550,7 +1573,8 @@ var OAuthProxy = (_class4 = class {
|
|
|
1550
1573
|
if (this.config.enableTokenSwap && this.jwtIssuer) {
|
|
1551
1574
|
return await this.issueSwappedTokens(
|
|
1552
1575
|
clientCode.clientId,
|
|
1553
|
-
clientCode.upstreamTokens
|
|
1576
|
+
clientCode.upstreamTokens,
|
|
1577
|
+
authenticated
|
|
1554
1578
|
);
|
|
1555
1579
|
} else {
|
|
1556
1580
|
const response = {
|
|
@@ -1559,6 +1583,11 @@ var OAuthProxy = (_class4 = class {
|
|
|
1559
1583
|
token_type: clientCode.upstreamTokens.tokenType
|
|
1560
1584
|
};
|
|
1561
1585
|
if (clientCode.upstreamTokens.refreshToken) {
|
|
1586
|
+
await this.savePassthroughRefreshBinding(
|
|
1587
|
+
clientCode.upstreamTokens.refreshToken,
|
|
1588
|
+
{ clientAuthenticated: authenticated, clientId: client.clientId },
|
|
1589
|
+
clientCode.upstreamTokens.refreshExpiresIn
|
|
1590
|
+
);
|
|
1562
1591
|
response.refresh_token = clientCode.upstreamTokens.refreshToken;
|
|
1563
1592
|
}
|
|
1564
1593
|
if (clientCode.upstreamTokens.idToken) {
|
|
@@ -1580,10 +1609,11 @@ var OAuthProxy = (_class4 = class {
|
|
|
1580
1609
|
"Only refresh_token grant type is supported"
|
|
1581
1610
|
);
|
|
1582
1611
|
}
|
|
1612
|
+
const caller = await this.authenticateClient(request);
|
|
1583
1613
|
if (this.config.enableTokenSwap && this.jwtIssuer) {
|
|
1584
|
-
return await this.handleSwapModeRefresh(request);
|
|
1614
|
+
return await this.handleSwapModeRefresh(request, caller);
|
|
1585
1615
|
}
|
|
1586
|
-
return await this.handlePassthroughRefresh(request);
|
|
1616
|
+
return await this.handlePassthroughRefresh(request, caller);
|
|
1587
1617
|
}
|
|
1588
1618
|
/**
|
|
1589
1619
|
* Get OAuth discovery metadata
|
|
@@ -1721,6 +1751,9 @@ var OAuthProxy = (_class4 = class {
|
|
|
1721
1751
|
if (!result.valid || !_optionalChain([result, 'access', _11 => _11.claims, 'optionalAccess', _12 => _12.jti])) {
|
|
1722
1752
|
return null;
|
|
1723
1753
|
}
|
|
1754
|
+
if (result.claims.token_use !== "access") {
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1724
1757
|
const mapping = await this.tokenStorage.get(
|
|
1725
1758
|
`mapping:${result.claims.jti}`
|
|
1726
1759
|
);
|
|
@@ -1750,8 +1783,15 @@ var OAuthProxy = (_class4 = class {
|
|
|
1750
1783
|
);
|
|
1751
1784
|
}
|
|
1752
1785
|
}
|
|
1786
|
+
const tokenEndpointAuthMethod = request.token_endpoint_auth_method || "client_secret_basic";
|
|
1787
|
+
if (!isTokenEndpointAuthMethod(tokenEndpointAuthMethod)) {
|
|
1788
|
+
throw new OAuthProxyError(
|
|
1789
|
+
"invalid_client_metadata",
|
|
1790
|
+
`Unsupported token_endpoint_auth_method: ${tokenEndpointAuthMethod}. Supported: ${TOKEN_ENDPOINT_AUTH_METHODS.join(", ")}`
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1753
1793
|
const proxyClientId = _crypto.randomBytes.call(void 0, 16).toString("hex");
|
|
1754
|
-
const proxyClientSecret = _crypto.randomBytes.call(void 0, 32).toString("base64url");
|
|
1794
|
+
const proxyClientSecret = tokenEndpointAuthMethod === "none" ? void 0 : _crypto.randomBytes.call(void 0, 32).toString("base64url");
|
|
1755
1795
|
const client = {
|
|
1756
1796
|
callbackUrl: request.redirect_uris[0],
|
|
1757
1797
|
clientId: proxyClientId,
|
|
@@ -1770,7 +1810,8 @@ var OAuthProxy = (_class4 = class {
|
|
|
1770
1810
|
tos_uri: request.tos_uri
|
|
1771
1811
|
},
|
|
1772
1812
|
redirectUris: request.redirect_uris,
|
|
1773
|
-
registeredAt: /* @__PURE__ */ new Date()
|
|
1813
|
+
registeredAt: /* @__PURE__ */ new Date(),
|
|
1814
|
+
tokenEndpointAuthMethod
|
|
1774
1815
|
};
|
|
1775
1816
|
await this.stateStore.saveRegisteredClient(client);
|
|
1776
1817
|
this.stateStore.cacheRegisteredClient(client);
|
|
@@ -1780,8 +1821,9 @@ var OAuthProxy = (_class4 = class {
|
|
|
1780
1821
|
// Echo back optional metadata
|
|
1781
1822
|
client_name: request.client_name,
|
|
1782
1823
|
client_secret: proxyClientSecret,
|
|
1783
|
-
|
|
1784
|
-
|
|
1824
|
+
// RFC 7591 §3.2.1: required alongside a secret, and only then.
|
|
1825
|
+
client_secret_expires_at: proxyClientSecret ? 0 : void 0,
|
|
1826
|
+
// 0: never
|
|
1785
1827
|
client_uri: request.client_uri,
|
|
1786
1828
|
contacts: request.contacts,
|
|
1787
1829
|
grant_types: request.grant_types || [
|
|
@@ -1797,11 +1839,49 @@ var OAuthProxy = (_class4 = class {
|
|
|
1797
1839
|
scope: request.scope,
|
|
1798
1840
|
software_id: request.software_id,
|
|
1799
1841
|
software_version: request.software_version,
|
|
1800
|
-
token_endpoint_auth_method:
|
|
1842
|
+
token_endpoint_auth_method: tokenEndpointAuthMethod,
|
|
1801
1843
|
tos_uri: request.tos_uri
|
|
1802
1844
|
};
|
|
1803
1845
|
return response;
|
|
1804
1846
|
}
|
|
1847
|
+
/**
|
|
1848
|
+
* RFC 6749 §2.3 / §3.2.1: identify the client behind a token request and,
|
|
1849
|
+
* when it presents a secret, authenticate it.
|
|
1850
|
+
*
|
|
1851
|
+
* A client registered with a secret method must present its secret. A
|
|
1852
|
+
* public client must not present one: it was issued none. A registration
|
|
1853
|
+
* that predates recorded auth methods may do either — it was issued a
|
|
1854
|
+
* secret whatever it asked for — and is treated as unauthenticated when it
|
|
1855
|
+
* leaves it out, which the grants then hold it to.
|
|
1856
|
+
*
|
|
1857
|
+
* Throws `invalid_client` on any failure. Nothing here touches grant state,
|
|
1858
|
+
* so a caller that fails cannot spend a code or refresh token.
|
|
1859
|
+
*/
|
|
1860
|
+
async authenticateClient(request) {
|
|
1861
|
+
if (!request.client_id) {
|
|
1862
|
+
throw new OAuthProxyError("invalid_client", "client_id is required");
|
|
1863
|
+
}
|
|
1864
|
+
const client = await this.resolveClient(request.client_id);
|
|
1865
|
+
if (!client) {
|
|
1866
|
+
throw new OAuthProxyError("invalid_client", "Unknown client_id");
|
|
1867
|
+
}
|
|
1868
|
+
if (request.client_secret) {
|
|
1869
|
+
if (!client.clientSecret || !secretsMatch(request.client_secret, client.clientSecret)) {
|
|
1870
|
+
throw new OAuthProxyError(
|
|
1871
|
+
"invalid_client",
|
|
1872
|
+
"Client authentication failed"
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
return { authenticated: true, client };
|
|
1876
|
+
}
|
|
1877
|
+
if (client.tokenEndpointAuthMethod === "client_secret_basic" || client.tokenEndpointAuthMethod === "client_secret_post") {
|
|
1878
|
+
throw new OAuthProxyError(
|
|
1879
|
+
"invalid_client",
|
|
1880
|
+
"Client authentication failed: client_secret is required"
|
|
1881
|
+
);
|
|
1882
|
+
}
|
|
1883
|
+
return { authenticated: false, client };
|
|
1884
|
+
}
|
|
1805
1885
|
/**
|
|
1806
1886
|
* Calculate access token TTL from upstream tokens
|
|
1807
1887
|
*/
|
|
@@ -2019,7 +2099,29 @@ var OAuthProxy = (_class4 = class {
|
|
|
2019
2099
|
/**
|
|
2020
2100
|
* Handle passthrough mode refresh - forward refresh token directly to upstream
|
|
2021
2101
|
*/
|
|
2022
|
-
async handlePassthroughRefresh(request) {
|
|
2102
|
+
async handlePassthroughRefresh(request, caller) {
|
|
2103
|
+
const bindingKey = this.passthroughRefreshKey(request.refresh_token);
|
|
2104
|
+
const binding = passthroughRefreshBindingSchema.safeParse(
|
|
2105
|
+
await this.tokenStorage.get(bindingKey)
|
|
2106
|
+
);
|
|
2107
|
+
if (!binding.success) {
|
|
2108
|
+
throw new OAuthProxyError(
|
|
2109
|
+
"invalid_grant",
|
|
2110
|
+
"Invalid or expired refresh token"
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
if (binding.data.clientId !== caller.client.clientId) {
|
|
2114
|
+
throw new OAuthProxyError(
|
|
2115
|
+
"invalid_grant",
|
|
2116
|
+
"Refresh token was issued to another client"
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
if (binding.data.clientAuthenticated && !caller.authenticated) {
|
|
2120
|
+
throw new OAuthProxyError(
|
|
2121
|
+
"invalid_client",
|
|
2122
|
+
"Client authentication failed: client_secret is required"
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
2023
2125
|
const useBasicAuth = this.config.upstreamTokenEndpointAuthMethod === "client_secret_basic";
|
|
2024
2126
|
const bodyParams = {
|
|
2025
2127
|
grant_type: "refresh_token",
|
|
@@ -2050,6 +2152,17 @@ var OAuthProxy = (_class4 = class {
|
|
|
2050
2152
|
throw new OAuthProxyError(errorCode, errorDescription);
|
|
2051
2153
|
}
|
|
2052
2154
|
const tokens = await this.parseTokenResponse(tokenResponse);
|
|
2155
|
+
await this.savePassthroughRefreshBinding(
|
|
2156
|
+
tokens.refresh_token || request.refresh_token,
|
|
2157
|
+
{
|
|
2158
|
+
clientAuthenticated: caller.authenticated,
|
|
2159
|
+
clientId: caller.client.clientId
|
|
2160
|
+
},
|
|
2161
|
+
tokens.refresh_expires_in
|
|
2162
|
+
);
|
|
2163
|
+
if (tokens.refresh_token && tokens.refresh_token !== request.refresh_token) {
|
|
2164
|
+
await this.tokenStorage.delete(bindingKey);
|
|
2165
|
+
}
|
|
2053
2166
|
return {
|
|
2054
2167
|
access_token: tokens.access_token,
|
|
2055
2168
|
expires_in: tokens.expires_in || 3600,
|
|
@@ -2062,21 +2175,30 @@ var OAuthProxy = (_class4 = class {
|
|
|
2062
2175
|
/**
|
|
2063
2176
|
* Handle swap mode refresh - verify FastMCP JWT and issue new tokens
|
|
2064
2177
|
*/
|
|
2065
|
-
async handleSwapModeRefresh(request) {
|
|
2178
|
+
async handleSwapModeRefresh(request, caller) {
|
|
2066
2179
|
if (!this.jwtIssuer) {
|
|
2067
2180
|
throw new Error("JWT issuer not initialized");
|
|
2068
2181
|
}
|
|
2069
2182
|
const verifyResult = await this.jwtIssuer.verify(request.refresh_token);
|
|
2070
|
-
if (!verifyResult.valid) {
|
|
2183
|
+
if (!verifyResult.valid || !verifyResult.claims) {
|
|
2071
2184
|
throw new OAuthProxyError(
|
|
2072
2185
|
"invalid_grant",
|
|
2073
2186
|
"Invalid or expired refresh token"
|
|
2074
2187
|
);
|
|
2075
2188
|
}
|
|
2076
|
-
|
|
2189
|
+
if (verifyResult.claims.token_use !== "refresh") {
|
|
2190
|
+
throw new OAuthProxyError("invalid_grant", "Not a refresh token");
|
|
2191
|
+
}
|
|
2192
|
+
const jti = verifyResult.claims.jti;
|
|
2077
2193
|
if (!jti) {
|
|
2078
2194
|
throw new OAuthProxyError("invalid_grant", "Refresh token missing JTI");
|
|
2079
2195
|
}
|
|
2196
|
+
if (verifyResult.claims.client_id !== caller.client.clientId) {
|
|
2197
|
+
throw new OAuthProxyError(
|
|
2198
|
+
"invalid_grant",
|
|
2199
|
+
"Refresh token was issued to another client"
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2080
2202
|
const mapping = await this.takeMapping(jti);
|
|
2081
2203
|
if (!mapping) {
|
|
2082
2204
|
throw new OAuthProxyError(
|
|
@@ -2085,6 +2207,18 @@ var OAuthProxy = (_class4 = class {
|
|
|
2085
2207
|
);
|
|
2086
2208
|
}
|
|
2087
2209
|
try {
|
|
2210
|
+
if (mapping.clientId !== caller.client.clientId) {
|
|
2211
|
+
throw new OAuthProxyError(
|
|
2212
|
+
"invalid_grant",
|
|
2213
|
+
"Refresh token was issued to another client"
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
if (mapping.clientAuthenticated !== false && !caller.authenticated) {
|
|
2217
|
+
throw new OAuthProxyError(
|
|
2218
|
+
"invalid_client",
|
|
2219
|
+
"Client authentication failed: client_secret is required"
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2088
2222
|
const upstreamTokens = await this.tokenStorage.get(
|
|
2089
2223
|
`upstream:${mapping.upstreamTokenKey}`
|
|
2090
2224
|
);
|
|
@@ -2120,7 +2254,8 @@ var OAuthProxy = (_class4 = class {
|
|
|
2120
2254
|
return await this.issueSwappedTokensForRefresh(
|
|
2121
2255
|
mapping.clientId,
|
|
2122
2256
|
refreshedUpstreamTokens,
|
|
2123
|
-
mapping.upstreamTokenKey
|
|
2257
|
+
mapping.upstreamTokenKey,
|
|
2258
|
+
caller.authenticated
|
|
2124
2259
|
);
|
|
2125
2260
|
} catch (error) {
|
|
2126
2261
|
try {
|
|
@@ -2138,7 +2273,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2138
2273
|
* Issue swapped tokens (JWT pattern)
|
|
2139
2274
|
* Issues short-lived FastMCP JWTs and stores upstream tokens securely
|
|
2140
2275
|
*/
|
|
2141
|
-
async issueSwappedTokens(clientId, upstreamTokens) {
|
|
2276
|
+
async issueSwappedTokens(clientId, upstreamTokens, clientAuthenticated) {
|
|
2142
2277
|
if (!this.jwtIssuer) {
|
|
2143
2278
|
throw new Error("JWT issuer not initialized");
|
|
2144
2279
|
}
|
|
@@ -2197,6 +2332,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2197
2332
|
await this.tokenStorage.save(
|
|
2198
2333
|
`mapping:${refreshJti}`,
|
|
2199
2334
|
{
|
|
2335
|
+
clientAuthenticated,
|
|
2200
2336
|
clientId,
|
|
2201
2337
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2202
2338
|
expiresAt: new Date(Date.now() + refreshTokenTtl * 1e3),
|
|
@@ -2213,7 +2349,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2213
2349
|
/**
|
|
2214
2350
|
* Issue swapped tokens for refresh flow
|
|
2215
2351
|
*/
|
|
2216
|
-
async issueSwappedTokensForRefresh(clientId, upstreamTokens, upstreamTokenKey) {
|
|
2352
|
+
async issueSwappedTokensForRefresh(clientId, upstreamTokens, upstreamTokenKey, clientAuthenticated) {
|
|
2217
2353
|
if (!this.jwtIssuer) {
|
|
2218
2354
|
throw new Error("JWT issuer not initialized");
|
|
2219
2355
|
}
|
|
@@ -2256,6 +2392,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2256
2392
|
await this.tokenStorage.save(
|
|
2257
2393
|
`mapping:${refreshJti}`,
|
|
2258
2394
|
{
|
|
2395
|
+
clientAuthenticated,
|
|
2259
2396
|
clientId,
|
|
2260
2397
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2261
2398
|
expiresAt: new Date(Date.now() + refreshTokenTtl * 1e3),
|
|
@@ -2282,7 +2419,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2282
2419
|
transaction.clientId,
|
|
2283
2420
|
transaction.clientCallbackUrl
|
|
2284
2421
|
);
|
|
2285
|
-
return _nullishCoalesce(_optionalChain([registeredClient, 'optionalAccess',
|
|
2422
|
+
return _nullishCoalesce(_optionalChain([registeredClient, 'optionalAccess', _15 => _15.redirectUris, 'access', _16 => _16.includes, 'call', _17 => _17(transaction.clientCallbackUrl)]), () => ( false));
|
|
2286
2423
|
}
|
|
2287
2424
|
/**
|
|
2288
2425
|
* Match URI against pattern (supports wildcards)
|
|
@@ -2328,6 +2465,14 @@ var OAuthProxy = (_class4 = class {
|
|
|
2328
2465
|
const rawJson = await response.json();
|
|
2329
2466
|
return tokenResponseSchema.parse(rawJson);
|
|
2330
2467
|
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Storage key for the client binding of a passthrough-mode upstream refresh
|
|
2470
|
+
* token. Keyed by its hash: storage keys are not encrypted, and the token
|
|
2471
|
+
* itself is a live upstream credential.
|
|
2472
|
+
*/
|
|
2473
|
+
passthroughRefreshKey(refreshToken) {
|
|
2474
|
+
return `passthrough-refresh:${sha256(refreshToken)}`;
|
|
2475
|
+
}
|
|
2331
2476
|
/**
|
|
2332
2477
|
* Redirect to upstream OAuth provider
|
|
2333
2478
|
*/
|
|
@@ -2460,6 +2605,18 @@ var OAuthProxy = (_class4 = class {
|
|
|
2460
2605
|
}
|
|
2461
2606
|
await this.tokenStorage.save(`mapping:${jti}`, mapping, ttl);
|
|
2462
2607
|
}
|
|
2608
|
+
/**
|
|
2609
|
+
* Record which client a passthrough-mode upstream refresh token was handed
|
|
2610
|
+
* to. It lives as long as the upstream says the token does, or as the
|
|
2611
|
+
* configured refresh-token lifetime when it does not say.
|
|
2612
|
+
*/
|
|
2613
|
+
async savePassthroughRefreshBinding(refreshToken, binding, refreshExpiresIn) {
|
|
2614
|
+
await this.tokenStorage.save(
|
|
2615
|
+
this.passthroughRefreshKey(refreshToken),
|
|
2616
|
+
binding,
|
|
2617
|
+
_nullishCoalesce(_nullishCoalesce(refreshExpiresIn, () => ( this.config.refreshTokenTtl)), () => ( DEFAULT_REFRESH_TOKEN_TTL))
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2463
2620
|
/**
|
|
2464
2621
|
* Start periodic cleanup of expired transactions and codes
|
|
2465
2622
|
*/
|
|
@@ -2524,6 +2681,19 @@ var OAuthProxy = (_class4 = class {
|
|
|
2524
2681
|
);
|
|
2525
2682
|
}
|
|
2526
2683
|
}, _class4);
|
|
2684
|
+
var isPublicClient = (client) => client.source === "cimd" || client.tokenEndpointAuthMethod === "none";
|
|
2685
|
+
var describeUnauthenticatedClient = (client) => {
|
|
2686
|
+
if (client.source === "cimd") {
|
|
2687
|
+
return "Client ID Metadata Document clients";
|
|
2688
|
+
}
|
|
2689
|
+
if (client.tokenEndpointAuthMethod === "none") {
|
|
2690
|
+
return 'clients registered with token_endpoint_auth_method "none"';
|
|
2691
|
+
}
|
|
2692
|
+
return "clients that do not authenticate with their client_secret";
|
|
2693
|
+
};
|
|
2694
|
+
var isTokenEndpointAuthMethod = (value) => TOKEN_ENDPOINT_AUTH_METHODS.includes(value);
|
|
2695
|
+
var sha256 = (value) => _crypto.createHash.call(void 0, "sha256").update(value).digest("base64url");
|
|
2696
|
+
var secretsMatch = (presented, expected) => equalsConstantTime(sha256(presented), sha256(expected));
|
|
2527
2697
|
var OAuthProxyError = class extends Error {
|
|
2528
2698
|
constructor(code, description, statusCode = 400) {
|
|
2529
2699
|
super(code);
|
|
@@ -2584,7 +2754,7 @@ var AuthProvider = class {
|
|
|
2584
2754
|
if (!request) {
|
|
2585
2755
|
return void 0;
|
|
2586
2756
|
}
|
|
2587
|
-
const authHeader = _optionalChain([request, 'access',
|
|
2757
|
+
const authHeader = _optionalChain([request, 'access', _18 => _18.headers, 'optionalAccess', _19 => _19.authorization]);
|
|
2588
2758
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
2589
2759
|
return void 0;
|
|
2590
2760
|
}
|
|
@@ -2978,7 +3148,7 @@ var DiskStore = (_class5 = class {
|
|
|
2978
3148
|
await _promises.rename.call(void 0, source, destination);
|
|
2979
3149
|
return;
|
|
2980
3150
|
} catch (error) {
|
|
2981
|
-
const code = _optionalChain([error, 'optionalAccess',
|
|
3151
|
+
const code = _optionalChain([error, 'optionalAccess', _20 => _20.code]);
|
|
2982
3152
|
if (process.platform !== "win32" || attempt >= 5 || !["EACCES", "EBUSY", "EPERM"].includes(_nullishCoalesce(code, () => ( "")))) {
|
|
2983
3153
|
throw error;
|
|
2984
3154
|
}
|
|
@@ -3152,5 +3322,6 @@ Original error: ${error.message}`
|
|
|
3152
3322
|
|
|
3153
3323
|
|
|
3154
3324
|
|
|
3155
|
-
|
|
3156
|
-
|
|
3325
|
+
|
|
3326
|
+
exports.cancelResponseBody = cancelResponseBody; exports.getAuthSession = getAuthSession; exports.requireAll = requireAll; exports.requireAny = requireAny; exports.requireAuth = requireAuth; exports.requireRole = requireRole; exports.requireScopes = requireScopes; exports.DEFAULT_ACCESS_TOKEN_TTL = DEFAULT_ACCESS_TOKEN_TTL; exports.DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH; exports.DEFAULT_REFRESH_TOKEN_TTL = DEFAULT_REFRESH_TOKEN_TTL; exports.TOKEN_ENDPOINT_AUTH_METHODS = TOKEN_ENDPOINT_AUTH_METHODS; exports.DEFAULT_AUTHORIZATION_CODE_TTL = DEFAULT_AUTHORIZATION_CODE_TTL; exports.DEFAULT_TRANSACTION_TTL = DEFAULT_TRANSACTION_TTL; exports.ConsentManager = ConsentManager; exports.JWTIssuer = JWTIssuer; exports.PKCEUtils = PKCEUtils; exports.EncryptedTokenStorage = EncryptedTokenStorage; exports.MemoryTokenStorage = MemoryTokenStorage; exports.OAuthProxy = OAuthProxy; exports.OAuthProxyError = OAuthProxyError; exports.AuthProvider = AuthProvider; exports.AzureProvider = AzureProvider; exports.GitHubProvider = GitHubProvider; exports.GoogleProvider = GoogleProvider; exports.OAuthProvider = OAuthProvider; exports.DiskStore = DiskStore; exports.JWKSVerifier = JWKSVerifier;
|
|
3327
|
+
//# sourceMappingURL=chunk-BV4PWTT6.cjs.map
|