fastmcp 4.12.4 → 4.12.6
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/README.md +8 -5
- 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-B_E71LUD.d.cts → OAuthProvider-B_OJFNC0.d.cts} +25 -5
- package/dist/{OAuthProvider-B_E71LUD.d.ts → OAuthProvider-B_OJFNC0.d.ts} +25 -5
- package/dist/auth/index.cjs +2 -2
- package/dist/auth/index.d.cts +2 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +1 -1
- package/dist/{chunk-FKX5QBJV.js → chunk-HIIPZVNY.js} +117 -10
- package/dist/chunk-HIIPZVNY.js.map +1 -0
- package/dist/{chunk-ENMHHQJF.cjs → chunk-RQ6222DU.cjs} +90 -39
- package/dist/chunk-RQ6222DU.cjs.map +1 -0
- package/dist/{chunk-2ZFQSPXZ.js → chunk-SZ4XCW5X.js} +90 -39
- package/dist/chunk-SZ4XCW5X.js.map +1 -0
- package/dist/{chunk-DD63MJG7.cjs → chunk-U36NARZG.cjs} +117 -10
- package/dist/chunk-U36NARZG.cjs.map +1 -0
- package/dist/examples/custom-routes.cjs +3 -3
- package/dist/examples/custom-routes.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-2ZFQSPXZ.js.map +0 -1
- package/dist/chunk-DD63MJG7.cjs.map +0 -1
- package/dist/chunk-ENMHHQJF.cjs.map +0 -1
- package/dist/chunk-FKX5QBJV.js.map +0 -1
|
@@ -1802,51 +1802,62 @@ var OAuthProxy = class {
|
|
|
1802
1802
|
if (!jti) {
|
|
1803
1803
|
throw new OAuthProxyError("invalid_grant", "Refresh token missing JTI");
|
|
1804
1804
|
}
|
|
1805
|
-
const mapping = await this.
|
|
1805
|
+
const mapping = await this.takeMapping(jti);
|
|
1806
1806
|
if (!mapping) {
|
|
1807
1807
|
throw new OAuthProxyError(
|
|
1808
1808
|
"invalid_grant",
|
|
1809
1809
|
"Refresh token already used or expired"
|
|
1810
1810
|
);
|
|
1811
1811
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
if (!upstreamTokens) {
|
|
1816
|
-
throw new OAuthProxyError(
|
|
1817
|
-
"invalid_grant",
|
|
1818
|
-
"Upstream tokens not found or expired"
|
|
1812
|
+
try {
|
|
1813
|
+
const upstreamTokens = await this.tokenStorage.get(
|
|
1814
|
+
`upstream:${mapping.upstreamTokenKey}`
|
|
1819
1815
|
);
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1816
|
+
if (!upstreamTokens) {
|
|
1817
|
+
throw new OAuthProxyError(
|
|
1818
|
+
"invalid_grant",
|
|
1819
|
+
"Upstream tokens not found or expired"
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
if (!upstreamTokens.refreshToken) {
|
|
1823
|
+
throw new OAuthProxyError(
|
|
1824
|
+
"invalid_grant",
|
|
1825
|
+
"No upstream refresh token available"
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
const refreshedUpstreamTokens = await this.refreshUpstreamTokens(
|
|
1829
|
+
upstreamTokens.refreshToken,
|
|
1830
|
+
request.scope
|
|
1825
1831
|
);
|
|
1832
|
+
if (refreshedUpstreamTokens.scope.length === 0) {
|
|
1833
|
+
refreshedUpstreamTokens.scope = upstreamTokens.scope;
|
|
1834
|
+
}
|
|
1835
|
+
const refreshTokenTtl = refreshedUpstreamTokens.refreshExpiresIn ?? this.config.refreshTokenTtl ?? DEFAULT_REFRESH_TOKEN_TTL;
|
|
1836
|
+
const accessTokenTtl = this.calculateAccessTokenTtl(
|
|
1837
|
+
refreshedUpstreamTokens
|
|
1838
|
+
);
|
|
1839
|
+
const upstreamStorageTtl = Math.max(accessTokenTtl, refreshTokenTtl, 1);
|
|
1840
|
+
await this.tokenStorage.save(
|
|
1841
|
+
`upstream:${mapping.upstreamTokenKey}`,
|
|
1842
|
+
refreshedUpstreamTokens,
|
|
1843
|
+
upstreamStorageTtl
|
|
1844
|
+
);
|
|
1845
|
+
return await this.issueSwappedTokensForRefresh(
|
|
1846
|
+
mapping.clientId,
|
|
1847
|
+
refreshedUpstreamTokens,
|
|
1848
|
+
mapping.upstreamTokenKey
|
|
1849
|
+
);
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
try {
|
|
1852
|
+
await this.restoreMapping(jti, mapping);
|
|
1853
|
+
} catch (restoreError) {
|
|
1854
|
+
console.error(
|
|
1855
|
+
`Failed to restore refresh token mapping ${jti}:`,
|
|
1856
|
+
restoreError
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
throw error;
|
|
1826
1860
|
}
|
|
1827
|
-
const refreshedUpstreamTokens = await this.refreshUpstreamTokens(
|
|
1828
|
-
upstreamTokens.refreshToken,
|
|
1829
|
-
request.scope
|
|
1830
|
-
);
|
|
1831
|
-
if (refreshedUpstreamTokens.scope.length === 0) {
|
|
1832
|
-
refreshedUpstreamTokens.scope = upstreamTokens.scope;
|
|
1833
|
-
}
|
|
1834
|
-
const refreshTokenTtl = refreshedUpstreamTokens.refreshExpiresIn ?? this.config.refreshTokenTtl ?? DEFAULT_REFRESH_TOKEN_TTL;
|
|
1835
|
-
const accessTokenTtl = this.calculateAccessTokenTtl(
|
|
1836
|
-
refreshedUpstreamTokens
|
|
1837
|
-
);
|
|
1838
|
-
const upstreamStorageTtl = Math.max(accessTokenTtl, refreshTokenTtl, 1);
|
|
1839
|
-
await this.tokenStorage.save(
|
|
1840
|
-
`upstream:${mapping.upstreamTokenKey}`,
|
|
1841
|
-
refreshedUpstreamTokens,
|
|
1842
|
-
upstreamStorageTtl
|
|
1843
|
-
);
|
|
1844
|
-
return await this.issueSwappedTokensForRefresh(
|
|
1845
|
-
mapping.clientId,
|
|
1846
|
-
refreshedUpstreamTokens,
|
|
1847
|
-
mapping.upstreamTokenKey,
|
|
1848
|
-
jti
|
|
1849
|
-
);
|
|
1850
1861
|
}
|
|
1851
1862
|
/**
|
|
1852
1863
|
* Issue swapped tokens (JWT pattern)
|
|
@@ -1927,11 +1938,10 @@ var OAuthProxy = class {
|
|
|
1927
1938
|
/**
|
|
1928
1939
|
* Issue swapped tokens for refresh flow
|
|
1929
1940
|
*/
|
|
1930
|
-
async issueSwappedTokensForRefresh(clientId, upstreamTokens, upstreamTokenKey
|
|
1941
|
+
async issueSwappedTokensForRefresh(clientId, upstreamTokens, upstreamTokenKey) {
|
|
1931
1942
|
if (!this.jwtIssuer) {
|
|
1932
1943
|
throw new Error("JWT issuer not initialized");
|
|
1933
1944
|
}
|
|
1934
|
-
await this.tokenStorage.delete(`mapping:${oldJti}`);
|
|
1935
1945
|
const customClaims = await this.extractUpstreamClaims(upstreamTokens);
|
|
1936
1946
|
const accessTokenTtl = this.calculateAccessTokenTtl(upstreamTokens);
|
|
1937
1947
|
const refreshTokenTtl = upstreamTokens.refreshToken ? upstreamTokens.refreshExpiresIn ?? this.config.refreshTokenTtl ?? DEFAULT_REFRESH_TOKEN_TTL : 0;
|
|
@@ -2108,6 +2118,29 @@ var OAuthProxy = class {
|
|
|
2108
2118
|
tokenType: tokens.token_type || "Bearer"
|
|
2109
2119
|
};
|
|
2110
2120
|
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Put a claimed refresh-token mapping back after a rotation failed, keeping
|
|
2123
|
+
* whatever lifetime it had left.
|
|
2124
|
+
*
|
|
2125
|
+
* A mapping that is already expired, or that carries no usable expiry, is
|
|
2126
|
+
* dropped instead. Inventing a TTL here would extend the lifetime of a
|
|
2127
|
+
* credential in a security path, and every mapping this proxy writes records
|
|
2128
|
+
* `expiresAt`.
|
|
2129
|
+
*/
|
|
2130
|
+
async restoreMapping(jti, mapping) {
|
|
2131
|
+
if (!mapping.expiresAt) {
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
const expiresAt = new Date(mapping.expiresAt).getTime();
|
|
2135
|
+
if (Number.isNaN(expiresAt)) {
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
const ttl = Math.ceil((expiresAt - Date.now()) / 1e3);
|
|
2139
|
+
if (ttl <= 0) {
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
await this.tokenStorage.save(`mapping:${jti}`, mapping, ttl);
|
|
2143
|
+
}
|
|
2111
2144
|
/**
|
|
2112
2145
|
* Start periodic cleanup of expired transactions and codes
|
|
2113
2146
|
*/
|
|
@@ -2116,6 +2149,24 @@ var OAuthProxy = class {
|
|
|
2116
2149
|
this.cleanup();
|
|
2117
2150
|
}, 6e4);
|
|
2118
2151
|
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Atomically claim a token mapping, mirroring `OAuthProxyStateStore`: at most
|
|
2154
|
+
* one caller — across all processes sharing the storage — can receive a given
|
|
2155
|
+
* mapping, which is what makes single use enforceable. Falls back to a
|
|
2156
|
+
* non-atomic get + delete for storages that do not implement `take`.
|
|
2157
|
+
*/
|
|
2158
|
+
async takeMapping(jti) {
|
|
2159
|
+
const key = `mapping:${jti}`;
|
|
2160
|
+
if (this.tokenStorage.take) {
|
|
2161
|
+
return await this.tokenStorage.take(key);
|
|
2162
|
+
}
|
|
2163
|
+
const stored = await this.tokenStorage.get(key);
|
|
2164
|
+
if (stored === null) {
|
|
2165
|
+
return null;
|
|
2166
|
+
}
|
|
2167
|
+
await this.tokenStorage.delete(key);
|
|
2168
|
+
return stored;
|
|
2169
|
+
}
|
|
2119
2170
|
/**
|
|
2120
2171
|
* Validate a redirect URI against the configured allow-list.
|
|
2121
2172
|
*
|
|
@@ -2733,4 +2784,4 @@ export {
|
|
|
2733
2784
|
DiskStore,
|
|
2734
2785
|
JWKSVerifier
|
|
2735
2786
|
};
|
|
2736
|
-
//# sourceMappingURL=chunk-
|
|
2787
|
+
//# sourceMappingURL=chunk-SZ4XCW5X.js.map
|