Skip to content

fix(auth): expired OAuth credentials survive per-provider credential discovery and silently break background operations#110678

Merged
steipete merged 10 commits into
openclaw:mainfrom
YangManBOBO:fix/oauth-expired-credential-discovery
Jul 19, 2026
Merged

fix(auth): expired OAuth credentials survive per-provider credential discovery and silently break background operations#110678
steipete merged 10 commits into
openclaw:mainfrom
YangManBOBO:fix/oauth-expired-credential-discovery

Conversation

@YangManBOBO

@YangManBOBO YangManBOBO commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Related: #110488

What Problem This Solves

Fixes an issue where the per-provider credential discovery map (resolveAgentCredentialMapFromStore) would pick the first credential in raw store order for each provider, even when a later profile was non-expired. An expired OAuth profile with a stale access token could take the one-per-provider slot ahead of a valid profile, silently breaking background operations that resolve credentials through this map (compaction safeguards, model discovery, context-engine maintenance) while identical manual operations succeeded through profile-aware resolution.

Why This Change Was Made

The credential map builder now prefers a non-expired OAuth credential over an expired one when multiple profiles exist for the same provider. An expired OAuth credential is kept as a fallback — the auth resolution path can still attempt to refresh it, so dropping it entirely at the map level would leave a sole-profile provider with no credential at all. The conversion layer (convertAuthProfileCredentialToAgent) is unchanged for OAuth: an expired-but-refreshable credential continues to pass through, matching the existing contract where the downstream refresh path owns the refresh decision.

User Impact

Background operations that use the per-provider credential map now receive the freshest available OAuth profile instead of blindly picking the first one in store order. A sole expired profile still appears in the map so the refresh path can attempt recovery.

Evidence

Real runtime proof (production credential map builder)

Before (current main): the map takes the first credential in store order unconditionally. An expired OAuth ahead of a valid one wins the slot and every background operation on that provider fails.

After (this patch): the map still takes the first credential, but when a non-expired OAuth profile for the same provider appears later in the store order, it replaces the expired one:

expired first (access=expired-access, expires=-1h)  →  map slot = expired-access
valid second  (access=valid-access,   expires=+1h)  →  map slot = valid-access  [preferred]

A sole expired OAuth profile is preserved so the refresh path has a credential to work with:

sole expired OAuth  →  map slot = sole-access  [kept for refresh]

Regression coverage

  • agent-model-discovery.auth.test.ts: 8 tests total (7 pass, 1 pre-existing keyRef fixture failure unchanged from base main).
    • New: expired OAuth kept when it is the sole profile for a provider.
    • New: map prefers a non-expired OAuth over an expired one for the same provider.
    • Pre-existing credential-conversion tests pass unchanged.
  • pnpm tsgo:core:test — passed.
  • Scoped oxfmt, oxlint, git diff --check pass.

AI-assisted.

@YangManBOBO
YangManBOBO requested a review from a team as a code owner July 18, 2026 12:00
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Jul 18, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 18, 2026
@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 18, 2026, 11:08 PM ET / July 19, 2026, 03:08 UTC.

Summary
The branch makes provider credential discovery use canonical auth-profile ordering, prefers live OAuth profiles while retaining expired OAuth fallback, forwards discovery context, and adds auth-order tests and documentation.

PR surface: Source +50, Tests +164, Docs +3. Total +217 across 12 files.

Reproducibility: no. live current-main reproduction was run in this review, but the report provides a concrete multi-profile background-discovery scenario and the branch adds focused source-level regression coverage for it.

Review metrics: 1 noteworthy metric.

  • Credential-selection policy: 1 provider-level selection path changed from raw store order to canonical profile order. This changes which credential existing background operations select for multi-profile providers.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #110488
Summary: This PR is a concrete candidate fix for the linked report about provider-level discovery ignoring canonical profile order and OAuth expiry.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Complete exact-head compatibility and required-check validation before merging.

Risk before merge

  • [P2] This changes provider-wide credential selection for existing multi-profile installations; merge should confirm that explicit auth.order, live OAuth preference, and sole expired OAuth refresh fallback retain their intended upgrade behavior on the exact final head.

Maintainer options:

  1. Confirm profile-selection compatibility (recommended)
    Before merge, verify the final head against an existing multi-profile configuration with explicit auth.order, live OAuth, and a sole expired OAuth fallback.
  2. Accept the ordering policy
    Land the canonical ordering change with the documented behavior that live OAuth wins by default while configured order remains authoritative.

Next step before merge

  • [P2] An assigned recent area contributor is actively maintaining this auth-provider behavior change, so the remaining action is normal compatibility confirmation and exact-head merge review rather than an autonomous repair.

Security
Cleared: The diff changes internal credential selection only; it adds no dependency, workflow, permission, secret-output, or external code-execution surface.

Review details

Best possible solution:

Land the canonical-ordering approach once final-head validation confirms configured profile order remains authoritative and existing sole expired OAuth profiles still reach the refresh-capable downstream path.

Do we have a high-confidence way to reproduce the issue?

No live current-main reproduction was run in this review, but the report provides a concrete multi-profile background-discovery scenario and the branch adds focused source-level regression coverage for it.

Is this the best way to solve the issue?

Yes. Routing provider-level discovery through the existing canonical auth-profile ordering is narrower and more maintainable than adding a separate credential-preference implementation for background consumers.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against c2a9579c5919.

Label changes

Label justifications:

  • P1: The linked report describes background operations failing for users with affected provider credential combinations.
  • merge-risk: 🚨 auth-provider: The PR changes how OAuth, token, and API-key profiles are selected for provider-level background discovery.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body provides concrete before/after production credential-map behavior and verifies the sole expired-OAuth fallback; focused regression tests supplement that runtime-oriented evidence.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body provides concrete before/after production credential-map behavior and verifies the sole expired-OAuth fallback; focused regression tests supplement that runtime-oriented evidence.
Evidence reviewed

PR surface:

Source +50, Tests +164, Docs +3. Total +217 across 12 files.

View PR surface stats
Area Files Added Removed Net
Source 8 69 19 +50
Tests 3 169 5 +164
Docs 1 4 1 +3
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 12 242 25 +217

What I checked:

  • Canonical discovery selection: The branch replaces raw profile insertion-order selection with canonical profile ordering before choosing one credential per normalized provider, addressing the linked background-discovery mismatch. (src/agents/agent-auth-credentials.ts:91, bcaf63cc010a)
  • OAuth fallback invariant: The ordering change ranks expired OAuth after live OAuth but keeps expired OAuth eligible, preserving the downstream refresh path when it is the only available profile. (src/agents/auth-profiles/order.ts:488, bcaf63cc010a)
  • Explicit-order coverage: The added ordering tests cover both insertion orders for live versus expired OAuth and retain explicit auth-order authority across expiry state. (src/agents/auth-profiles/order.test.ts:254, 7b38ae89e2ef)
  • Discovery caller propagation: The final branch forwards config and workspace context into auth discovery from model-resolution and auxiliary discovery callers, so canonical ordering can observe configured auth order consistently. (src/agents/model-catalog.ts:791, 7b38ae89e2ef)
  • Active owner-maintained branch: The feature direction appears to have been taken over and refined by the assigned maintainer through the canonical-discovery commit and subsequent test, docs, and final-head updates. (src/agents/agent-auth-credentials.ts:91, bcaf63cc010a)

Likely related people:

  • steipete: Auth-discovery behavior was revised in the canonical-ordering commit, followed by tests and documentation updates; the PR timeline also records assignment and later head updates by this contributor. (role: recent area contributor and assigned reviewer; confidence: high; commits: bcaf63cc010a, aff5509668ad, bdd49819a591; files: src/agents/agent-auth-credentials.ts, src/agents/auth-profiles/order.ts, src/agents/auth-profiles/order.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (7 earlier review cycles)
  • reviewed 2026-07-18T12:03:56.290Z sha 05fb24e :: needs maintainer review before merge. :: none
  • reviewed 2026-07-18T12:29:15.812Z sha dd9b587 :: found issues before merge. :: [P1] Preserve refreshable OAuth credentials in background discovery
  • reviewed 2026-07-18T13:10:27.621Z sha dd9b587 :: found issues before merge. :: [P1] Preserve refreshable OAuth credentials in background discovery
  • reviewed 2026-07-18T15:16:07.338Z sha 8eff96b :: needs maintainer review before merge. :: none
  • reviewed 2026-07-19T02:20:35.709Z sha 1be607f :: needs maintainer review before merge. :: none
  • reviewed 2026-07-19T02:32:44.263Z sha 3999a9d :: needs maintainer review before merge. :: none
  • reviewed 2026-07-19T02:59:50.349Z sha 7b38ae8 :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 18, 2026
@YangManBOBO
YangManBOBO force-pushed the fix/oauth-expired-credential-discovery branch from dd9b587 to 8eff96b Compare July 18, 2026 15:11
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 18, 2026
@steipete steipete self-assigned this Jul 19, 2026
@steipete
steipete force-pushed the fix/oauth-expired-credential-discovery branch from 8eff96b to 1be607f Compare July 19, 2026 02:16
@steipete
steipete force-pushed the fix/oauth-expired-credential-discovery branch from 1be607f to 3999a9d Compare July 19, 2026 02:28
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Jul 19, 2026
@steipete
steipete force-pushed the fix/oauth-expired-credential-discovery branch from 3999a9d to 7b38ae8 Compare July 19, 2026 02:56
@openclaw-barnacle openclaw-barnacle Bot added the cli CLI command changes label Jul 19, 2026
@steipete
steipete force-pushed the fix/oauth-expired-credential-discovery branch from 7b38ae8 to bdd4981 Compare July 19, 2026 03:04
@steipete

Copy link
Copy Markdown
Contributor

Maintainer proof on exact head bdd49819a591207716baebf01e5c6c3eca49412d:

  • Reworked discovery credential selection to use the canonical auth-profile ordering path instead of a local expired-OAuth swap. Default selection now prefers usable OAuth credentials before expired OAuth credentials, while preserving explicit auth.order as authoritative and retaining a sole expired OAuth profile for the existing refresh path.
  • Threaded the existing config/workspace context through every affected model-discovery caller and aligned the model-failover documentation.
  • Added regression coverage for insertion order in both directions, lastUsed inversion, explicit ordering, sole-expired refresh eligibility, API-key versus OAuth ordering, and SQLite-backed configured ordering.
  • Hardened the unrelated ACP process integration test that repeatedly timed out under the exact-head compact shard, while retaining its response assertions and bounded deadlines.

Proof:

@steipete
steipete merged commit ed546bd into openclaw:main Jul 19, 2026
116 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
…discovery and silently break background operations (openclaw#110678)

* fix(auth): reject expired OAuth credentials in provider credential discovery

* test(auth): verify expired first profile is skipped for same-provider validation

* fix(auth): prefer non-expired OAuth profile in per-provider credential map

* fix(auth): use canonical profile order in discovery

* docs(auth): document expired OAuth ordering

* test(auth): use synthetic credential fixtures

* test(auth): clarify resolved profile fixtures

* test(auth): keep profile result names consistent

* test(cli): relax ACP process deadlines under load

* style(cli): format ACP process timeout

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling cli CLI command changes docs Improvements or additions to documentation merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants