import { type DeviceTokenResponse } from "./oauth-device-flow.js"; /** * Exchanges a stored refresh token for a fresh access token at `{serverTarget}/oauth/token` * (`grant_type=refresh_token`) — the shared TypeScript refresher of unified-machine-auth 04 §3. * It is the HTTP seam only; the {@link MachineCredentialProvider} owns the machine store, the * cross-process lock, and the refresh scheduling. * * The 04 §3 wire rules are load-bearing and pinned by tests: * * - **The request presents the family's stored `clientId`** (rule 2), carried per-request in * {@link TokenRefreshRequest} — this class has NO client id of its own, so a component default * can never leak into another family's refresh (the production `client_id mismatch` metric, * mirror of probe Q1). * - **`scope` and `resource` are omitted ENTIRELY** (rule 3). The server falls back to the stored * grant; sending a component default would permanently narrow an agent family to that scope * (P0-3 — Unity's legacy refresher did exactly this). O11 may later reintroduce `resource` * with the canonical value; that is a deliberate follow-up, never a default here. * - The network timeout defaults to {@link REFRESH_HTTP_TIMEOUT} (15 s) — the 04 §2 lock-protocol * constant, explicitly set so a live lock holder inside one HTTP call can never be declared * stale (`REFRESH_HTTP_TIMEOUT < LOCK_STALE_MS`). * * It **fails closed**: any non-success, missing access token, or exception becomes a * {@link TokenRefreshResult} failure — never a throw past the boundary, and it never logs token * material. A refresh-token family-revoke (rotation-reuse detection on the server) surfaces here as * a failure with the server's error (`invalid_grant`), which the provider turns into a clean * `login required`. */ /** The result of a refresh attempt — a value, never a throw. */ export type TokenRefreshResult = { ok: true; accessToken: string; refreshToken?: string; expiresAt?: string; } | { ok: false; reason: string; }; /** * One refresh request (04 §3): the family's rotating refresh token plus the **stored `clientId` * of that family** — for `families.legacy` (mint client unknown by definition) the caller passes * its component-default id (§3.7, status-quo behavior). `scope`/`resource` are deliberately NOT * part of this shape (rule 3). */ export interface TokenRefreshRequest { /** The family's current rotating refresh token. */ refreshToken: string; /** The OAuth client id the family was minted under (stored per family — 04 §1/D8). */ clientId: string; /** The credential's server target (AS root or `/mcp` hub URL — normalized before use). */ serverTarget?: string; /** Cancellation. */ signal?: AbortSignal; } /** The refresh transport seam (injectable for tests). */ export interface TokenRefresher { refresh(request: TokenRefreshRequest): Promise; } /** * Normalize a stored server target to the AS root: trim a trailing slash and a trailing `/mcp` hub * segment so `/oauth/token` resolves on the authorization-server root. Mirrors the C# * `HttpTokenRefresher.NormalizeBase`. */ export declare function normalizeServerBase(serverTarget: string | undefined | null): string | null; /** * Build the RFC 6749 refresh-token grant form (04 §3): exactly `grant_type` + `refresh_token` + * `client_id`, nothing else. `scope` and `resource` are omitted ENTIRELY (rule 3) — the server * falls back to the stored grant, and a component-default `scope` here would permanently narrow * an agent family (P0-3). */ export declare function buildRefreshForm(refreshToken: string, clientId: string): URLSearchParams; /** Turn a parsed token response into a {@link TokenRefreshResult} (mirrors the C# `BuildResult`). */ export declare function buildRefreshResult(isSuccessStatus: boolean, statusCode: number, parsed: DeviceTokenResponse | null, now?: () => number): TokenRefreshResult; /** Options for the default fetch-backed refresher. */ export interface HttpTokenRefresherOptions { /** The AS root used when a credential carries no `serverTarget`. */ defaultServerBaseUrl: string; /** Injectable for tests; defaults to the global `fetch`. */ fetchImpl?: typeof fetch; /** * Per-request network timeout (ms). Defaults to {@link REFRESH_HTTP_TIMEOUT} (15 s) — the 04 §2 * ordered lock constant (`REFRESH_HTTP_TIMEOUT < LOCK_STALE_MS < ACQUIRE_BUDGET`). Overriding it * above `LOCK_STALE_MS` in shipping code breaks the cross-language ordering invariant. */ timeoutMs?: number; /** Injectable clock (ms since epoch); defaults to `Date.now`. */ now?: () => number; } /** The default {@link TokenRefresher}: a form-encoded `grant_type=refresh_token` POST via `fetch`. */ export declare class HttpTokenRefresher implements TokenRefresher { private readonly _defaultBase; private readonly _fetch; private readonly _timeoutMs; private readonly _now; constructor(options: HttpTokenRefresherOptions); refresh(request: TokenRefreshRequest): Promise; } //# sourceMappingURL=token-refresher.d.ts.map