fix(design): bound requests and prevent mutation replay

This commit is contained in:
2026-08-19 12:51:46 +08:00
parent 1ba68a9e41
commit 87e4140f8a
7 changed files with 447 additions and 55 deletions

View File

@@ -1,10 +1,11 @@
import { createHash } from 'node:crypto';
import { NIANCODE_AUTH_CONFIG } from '../api/auth-config';
import { proxyAwareFetch } from '../utils/proxy-fetch';
import { proxyAwareFetch, runWithDeadline } from '../utils/proxy-fetch';
import { logger } from '../utils/logger';
import { WORKS_SQUARE_SESSION_IDLE_TIMEOUT_MS } from '../../shared/auth-session';
const TOKEN_REFRESH_SKEW_MS = 30_000;
const WORKS_SQUARE_AUTH_REQUEST_TIMEOUT_MS = 30_000;
const SESSION_STORE_SCHEMA_VERSION = 1;
export { WORKS_SQUARE_SESSION_IDLE_TIMEOUT_MS } from '../../shared/auth-session';
@@ -716,7 +717,7 @@ async function waitForCredentialPersistence(): Promise<boolean> {
async function refreshWorksSquareSession(
session: StoredWorksSquareSession,
generation: number,
options: { fetchImpl?: typeof fetch; nowMs?: number } = {},
options: { fetchImpl?: typeof fetch; nowMs?: number; requestTimeoutMs?: number } = {},
): Promise<string | null> {
if (!session.refreshToken) return null;
@@ -726,17 +727,25 @@ async function refreshWorksSquareSession(
grant_type: 'refresh_token',
refresh_token: session.refreshToken,
});
const response = await fetchImpl(`${NIANCODE_AUTH_CONFIG.gatewayAuthUrl.replace(/\/+$/, '')}/oauth2/token`, {
method: 'POST',
headers: {
Authorization: createBasicAuthHeader(
NIANCODE_AUTH_CONFIG.clientId,
NIANCODE_AUTH_CONFIG.clientSecret,
),
'Content-Type': 'application/x-www-form-urlencoded',
},
body,
});
const { response, payload } = await runWithDeadline(async (signal) => {
const response = await fetchImpl(
`${NIANCODE_AUTH_CONFIG.gatewayAuthUrl.replace(/\/+$/, '')}/oauth2/token`,
{
method: 'POST',
headers: {
Authorization: createBasicAuthHeader(
NIANCODE_AUTH_CONFIG.clientId,
NIANCODE_AUTH_CONFIG.clientSecret,
),
'Content-Type': 'application/x-www-form-urlencoded',
},
body,
signal,
},
);
const payload = response.ok ? await readResponsePayload(response) : null;
return { response, payload };
}, options.requestTimeoutMs ?? WORKS_SQUARE_AUTH_REQUEST_TIMEOUT_MS);
if (!response.ok) {
logger.warn('[works-square-session] Refresh failed', { status: response.status });
@@ -746,7 +755,6 @@ async function refreshWorksSquareSession(
return null;
}
const payload = await readResponsePayload(response);
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
logger.warn('[works-square-session] Refresh returned an invalid payload');
return null;
@@ -780,6 +788,7 @@ export async function getValidWorksSquareAccessToken(
fetchImpl?: typeof fetch;
nowMs?: number;
forceRefresh?: boolean;
requestTimeoutMs?: number;
} = {},
): Promise<string | null> {
if (restoreStatus === 'unavailable') {