Skip to content

Commit d581d9d

Browse files
committed
docs: document oauth refresh manager
1 parent 9d20ad2 commit d581d9d

10 files changed

Lines changed: 53 additions & 16 deletions

src/agents/auth-profiles/oauth-lock-path.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests OAuth refresh lock path generation.
3+
* Ensures provider/profile lock keys are deterministic, collision-resistant,
4+
* short, and confined to the refresh lock directory.
5+
*/
16
import fs from "node:fs/promises";
27
import os from "node:os";
38
import path from "node:path";

src/agents/auth-profiles/oauth-lock-timeout-classification.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests OAuth refresh lock timeout classification.
3+
* Verifies only the global refresh lock becomes refresh_contention and that the
4+
* original file-lock cause is preserved.
5+
*/
16
import { describe, expect, it } from "vitest";
27
import { FILE_LOCK_TIMEOUT_ERROR_CODE, type FileLockTimeoutError } from "../../infra/file-lock.js";
38
import {

src/agents/auth-profiles/oauth-manager.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests OAuth manager store and refresh behavior.
3+
* Covers identity safety, main-store adoption, refresh persistence, fallback
4+
* recovery, and external profile overlays.
5+
*/
16
import fs from "node:fs/promises";
27
import os from "node:os";
38
import path from "node:path";

src/agents/auth-profiles/oauth-manager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* OAuth credential manager.
3+
* Resolves usable access tokens, refreshes expired credentials under global
4+
* locks, adopts safer main-store credentials, and mirrors refreshed tokens.
5+
*/
16
import type { OpenClawConfig } from "../../config/types.openclaw.js";
27
import { normalizeSecretInputString } from "../../config/types.secrets.js";
38
import { formatErrorMessage } from "../../infra/errors.js";
@@ -56,6 +61,7 @@ export type ResolvedOAuthAccess = {
5661
credential: OAuthCredential;
5762
};
5863

64+
/** Refresh failure that preserves a redacted refreshed store and credential. */
5965
export class OAuthManagerRefreshError extends OAuthRefreshFailureError {
6066
readonly profileId: string;
6167
readonly code?: string;
@@ -262,6 +268,7 @@ async function loadFreshStoredOAuthCredential(params: {
262268
return reloaded;
263269
}
264270

271+
/** Select local OAuth unless a safe external bootstrap credential should win. */
265272
export function resolveEffectiveOAuthCredential(params: {
266273
profileId: string;
267274
credential: OAuthCredential;
@@ -306,6 +313,7 @@ export function resolveEffectiveOAuthCredential(params: {
306313
return params.credential;
307314
}
308315

316+
/** Create an OAuth manager bound to provider-specific build/refresh adapters. */
309317
export function createOAuthManager(adapter: OAuthManagerAdapter) {
310318
function adoptNewerMainOAuthCredential(params: {
311319
store: AuthProfileStore;

src/agents/auth-profiles/oauth-refresh-error.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests refresh_token_reused error detection.
3+
* Protects the recovery path that adopts a winner's fresh token instead of
4+
* failing over after concurrent refresh races.
5+
*/
16
import { describe, expect, it } from "vitest";
27
import {
38
makeSeededRandom,
@@ -6,11 +11,6 @@ import {
611
} from "./oauth-test-utils.js";
712
import { isRefreshTokenReusedError } from "./oauth.js";
813

9-
// Direct tests for the refresh_token_reused classifier. This is the gate that
10-
// triggers the retry/adoption recovery path; a false negative here means we
11-
// fail over to an expensive model instead of adopting the winner's fresh
12-
// token.
13-
1414
describe("isRefreshTokenReusedError", () => {
1515
describe("positive cases", () => {
1616
it("detects the canonical OAuth snake_case code", () => {

src/agents/auth-profiles/oauth-refresh-failure.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests OAuth refresh failure hints.
3+
* Verifies typed and message-based classification plus sanitized login command
4+
* generation.
5+
*/
16
import { describe, expect, it } from "vitest";
27
import {
38
buildOAuthRefreshFailureLoginCommand,

src/agents/auth-profiles/oauth-refresh-failure.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
/**
2+
* OAuth refresh failure classification and operator hints.
3+
* Parses provider/reason codes from refresh failures and formats safe login
4+
* commands without trusting raw provider text.
5+
*/
16
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
27
import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js";
38
import { formatCliCommand } from "../../cli/command-format.js";
49

5-
// OAuth refresh failure classifiers for status/help text. Providers and reasons
6-
// are sanitized before being used in logs or suggested commands.
710
export type OAuthRefreshFailureReason =
811
| "refresh_token_reused"
912
| "invalid_grant"

src/agents/auth-profiles/oauth-refresh-lock-errors.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* OAuth refresh lock error helpers.
3+
* Distinguishes global refresh-lock contention from auth-store lock timeouts
4+
* and builds the user-facing contention error.
5+
*/
16
import { FILE_LOCK_TIMEOUT_ERROR_CODE } from "../../infra/file-lock.js";
27

38
/** Returns true when an error came from the global OAuth refresh lock. */

src/agents/auth-profiles/oauth-refresh-queue.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests in-process OAuth refresh queuing.
3+
* Ensures concurrent refresh attempts serialize and queue gates release after
4+
* both success and failure.
5+
*/
16
import path from "node:path";
27
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
38
import { resetFileLockStateForTest } from "../../infra/file-lock.js";

src/agents/auth-profiles/oauth-refresh-timeout.test.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* Tests OAuth refresh timeout invariants.
3+
* Guards the relationship between per-refresh hard timeout, stale lock window,
4+
* and retry budget.
5+
*/
16
import { describe, expect, it } from "vitest";
27
import { OAUTH_REFRESH_CALL_TIMEOUT_MS, OAUTH_REFRESH_LOCK_OPTIONS } from "./constants.js";
38

@@ -16,15 +21,6 @@ function computeMinimumRetryBudgetMs(): number {
1621
return total;
1722
}
1823

19-
// Invariant tests for the two constants that together bound the OAuth
20-
// refresh critical section. Behavioural tests for the inner `setTimeout`
21-
// mechanics are deliberately omitted: the implementation is a thin
22-
// `Promise.race` around `setTimeout`, and exercising it end-to-end requires
23-
// stepping through nested file-lock I/O that mixes awkwardly with Vitest
24-
// fake timers. A regression in the timeout wiring would be caught by the
25-
// #26322 regression test (oauth.concurrent-20-agents.test.ts) because a
26-
// stuck refresh would time out the whole suite.
27-
2824
describe("OAuth refresh call timeout (invariants)", () => {
2925
it("OAUTH_REFRESH_CALL_TIMEOUT_MS is strictly below OAUTH_REFRESH_LOCK_OPTIONS.stale", () => {
3026
// The whole point of the two constants: the refresh call must always

0 commit comments

Comments
 (0)