fix(auth): fenced oauth refresh writes

- Fenced final OAuth refresh update and terminal-disable CAS statements by row id, serialized credential data, active lease owner, and unexpired lease time.
- Passed an AbortSignal through MCP OAuth token refresh and bounded owned refresh operations below the lease TTL while awaiting the aborted fetch to settle.
- Added regressions for stolen-lease update/disable attempts and timed-out MCP token fetch abort behavior.

Fixes #5081
This commit is contained in:
roboomp
2026-07-10 21:49:18 +00:00
parent cb32853130
commit b60dc669ea
5 changed files with 272 additions and 21 deletions
+2 -1
View File
@@ -1238,7 +1238,7 @@ export class MCPManager {
const material = selectMcpOAuthRefreshMaterial(current, auth);
return Boolean(current.refresh && material?.tokenUrl);
},
refresh: current => {
refresh: (current, signal) => {
if (current.refresh === REMOTE_REFRESH_SENTINEL) {
throw new Error("MCP OAuth refresh token is broker-redacted; local refresh is unavailable");
}
@@ -1257,6 +1257,7 @@ export class MCPManager {
return refreshMCPOAuthToken(tokenUrl, current.refresh, clientId, clientSecret, resource, {
authorizationUrl,
stripSameOriginResource: resourceIsFallback,
signal,
});
},
mergeRefreshedCredential: (current, refreshed) => {
@@ -715,6 +715,7 @@ export class MCPOAuthFlow extends OAuthCallbackFlow {
*/
export interface RefreshMCPOAuthTokenOptions {
fetch?: FetchImpl;
signal?: AbortSignal;
/**
* Authorization-server URL the original grant was minted against. Used to
* filter same-origin resource indicators on refresh. Defaults to `tokenUrl`'s
@@ -766,6 +767,7 @@ export async function refreshMCPOAuthToken(
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params.toString(),
signal: optsFromTrailing?.signal,
});
if (!response.ok) {