Skip to content

Refresh expiring oauth_token credentials at serving time - #686

Merged
TheGreatAxios merged 5 commits into
mainfrom
cl-7505-serve-refreshed-oauth_token-credentials-at-serving-time
Sep 8, 2026
Merged

TheGreatAxios merged 5 commits into
mainfrom
cl-7505-serve-refreshed-oauth_token-credentials-at-serving-time

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Summary

  • Expiring oauth_token credentials refresh at serving time as a credential class: a tenant-wide pass runs ahead of every chat sendMail dial, refreshing any active credential whose expiresAt falls inside a skew lead, with concurrent refreshes coalesced into exactly one grant per credential.
  • @corbits/oauth-core is vendored hand-copied under vendor/intx/oauth-core with its VENDORED.md ledger row and 2026-10-07 kill date (the package is unpublished); the credential-row token session in packages/connections composes its createTokenSession onto the credential table's secret/refreshSecret/expiresAt columns with a pluggable refresh grant generalizing the MCP precedent.
  • The vendored buildSource material-resolution seam gains an optional serving-refresh hook (recorded local delta, db/hub-api/hub-sessions) that skips an offering as credential_needs_reauth when the hook cannot save it — threaded and tested, reserved pending launch-resolution wiring; the module header states the live wiring honestly.
  • A failed grant marks the credential status: "error" (settings Connections card shows needs-attention), pushes credentials-updated frames so instances re-resolve, and never serves a dead secret; api_key rows take an unchanged pass-through path at every layer.

Verification

  • bun run check exits 0 before each commit (typecheck, lint, full test matrix, structural checks); the push-time pre-push gate ran the full suite green (935 pass / 0 fail on the final web shard).
  • bun run scripts/checks/killdates.ts exits 0 (17 ledger hashes match their trees).
  • Targeted suites: token-session 8 pass / 0 fail (two-sequential-expiries regression proves the second grant sees rotated secrets), credential-material-refresh 6 pass / 0 fail (lost-race suppression, NULL-expiry non-due, failure marking), model-source-resolution seam tests green.
  • Independent two-round review of the full branch diff returned clean on the final tree.

Closes CL-7505

The serving-time refresh work needs a provider-agnostic OAuth refresh
layer (skew-aware expiry, coalesced concurrent refreshes, pluggable
refresh grant), and oauth-core already implements it as
createTokenSession, but the package was never published. Hand-copy the
initial-release sources with the VENDORED.md ledger row and its
2026-10-07 kill date in the same change.
getValidToken adapts oauth-core's createTokenSession onto the credential
row's secret/refreshSecret/expiresAt columns: skew-aware expiry lead,
concurrent refreshes coalesced into exactly one refresh grant per
credential, and a pluggable refresh grant the hub supplies per
credential class. Failure reports re-auth-required instead of throwing,
and an api_key row takes its exact pass-through path untouched.
Wire the token session into the material-resolution path. The vendored
db/hub-api/hub-sessions packages take a recorded local delta:
buildSource gains an optional servingRefresh hook, threaded through
resolveModelSources, resolveInstanceModelSources,
resolveDefinitionSources, and the hub-sessions push path, which offers
each served oauth_token credential to the hook and skips the offering
as credential_needs_reauth when the hook cannot save it — an
unauthenticated dial is never served, and an api_key credential never
reaches the hook.

The hub composes the hook in credential-material-refresh.ts: the
connections token session refreshes the row in place (coalesced,
skew-aware) through a pluggable grant (defaulting to the generalized MCP
auth() refresh), the refreshed material reaches running deployments via
the existing credentials-updated push, and a failed grant marks the
credential status "error" so the settings Connections card shows
needs-attention — the expiry sweep's mail remains the human nudge. The
chat platform runs the tenant-wide refresh ahead of every sendMail, so
serving time is the only trigger.

Update the vendored delta notes and tree hashes in the same change.
The token session reloaded only the credential id for a refresh grant,
so the grant's exchange inputs came from the row captured when the
session was created — a second refresh sent the pre-rotation refresh
secret, which rotating authorization servers reject as invalid_grant.
Every grant call now receives the current persisted row, and the
vendored session's rotated tokens are threaded through to the writer.

applyRefreshedTokens' UPDATE now carries the status = active predicate
its doc claims, and a false return is honored as a lost race (no
error, no redundant push).

A grant that states no expires_in used to store now + 120s, putting
the row back in the due set for refresh-plus-push on every dial.
Missing lifetime information now persists a NULL expiry — non-due,
matching the vendored oauth-core stance.

The serving-refresh module header now describes the live wiring: the
chat sendMail choke point is the trigger, the MCP sweep remains the
background backstop, and the buildSource seam is threaded but reserved
pending launch-resolution wiring.
@linear-code

linear-code Bot commented Sep 8, 2026

Copy link
Copy Markdown

CL-7505

@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Self-review (code-review skill) returned clean. Reviewer-of-record checks (commit subject/body style audits, diff-stat binary scan, commit-scope check) ran in-session on the final history; the deeper behavioral read ran as two independent review rounds — round 1 returned one blocking and three should-fix findings, all fixed with a red-then-green regression test, and round 2 returned clean.

@TheGreatAxios
TheGreatAxios merged commit 0525f22 into main Sep 8, 2026
24 checks passed
@TheGreatAxios
TheGreatAxios deleted the cl-7505-serve-refreshed-oauth_token-credentials-at-serving-time branch September 9, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant