Skip to content

fix(auth): hydrate primary authorization snapshots per request - #7462

Merged
bokelley merged 7 commits into
mainfrom
conductor/cold-cache-auth-snapshot
Sep 17, 2026
Merged

bokelley merged 7 commits into
mainfrom
conductor/cold-cache-auth-snapshot

Conversation

@bokelley

@bokelley bokelley commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

An identity binding could commit between cold-cache identity and epoch reads, stamping old attribution with a new epoch. Cookie and bearer caches now retain provider authentication only; every request hydrates a fresh immutable, server-only authorization snapshot from primary PostgreSQL.

The atomic statement reads the exact authenticated credential, canonical identity routing, explicit organization selection, active exact-credential grant, and persisted epoch. It also preserves the durable deletion/quarantine tombstones and exact-one-primary lifecycle checks now on main. Missing, deleted, quarantined, or invalidly routed credentials terminate with 401; unavailable primary state returns 503. No linked credential, membership cache, or implicit organization can substitute for the authenticated credential.

Organization selectors must agree with the provider/session organization. The gated organization resolver rejects stale/replayed snapshots, reads WorkOS membership for the exact credential and selected organization, then rechecks the full database snapshot. WorkOS remains an external authority read bracketed by DB checks. The authentication selectors are x-organization-id, query org/organization_id, body organization_id/organizationId, and path orgId/organizationId; legacy companyId and mutation-specific fields remain outside this slice.

One absolute two-second snapshot budget covers pool checkout and its single retry, BEGIN, transaction-local timeout configuration, the statement, COMMIT, and ROLLBACK cleanup. Late checkout results are released without queries; late settlements are consumed. Ambiguous operations, lost commit acknowledgement, and unconfirmed rollback destroy the uncertain client exactly once rather than returning unknown state to the pool. Retries never renew the deadline.

optionalAuth permits anonymous access only without a credential. Presented invalid/expired credentials return 401, and unavailable snapshot/JWKS/provider/shared-session recovery returns 503; neither becomes anonymous access. Main's exact-credential platform-admin lookup, no-cache authority checks, typed outages, independent break-glass authority, and reserved aao-admin group guard are preserved.

Exact replay and deliberate composition

  • Qualified base: 54fd5f3c0a3cd5ac295d2f295bb7dc7232803ab9; base tree: 74e47c4e36fa58fc5d87a0d120d91491523c2d8a. Merged fix(addie): authorize admin tools by authenticated credential #7450 (f928b36ff), fix(auth): authorize web admins by authenticated credential #7452 (c9a8c4f91), and fix(auth): contain destructive identity consolidation #7459 are verified ancestors.
  • Final head: 0c14d9fd9b49a90faec3f17899aa2e44f686d2a3; final reviewed tree: 8bdd72b68c7e0e8d01240d57b0c7542137db5edb.
  • Original published head/explicit publication lease: 3216b0d61dca45211f4a500a6508e3bdcd0b3113, based on dc69e0b2264b4c563f2175a48c515b7108dbb4f6.
  • Attempted the exact six historical commits first. The first conflicted in auth middleware and epoch/session tests; the other five preserve their original added/deleted edit sequences. This is a conflict-resolved replay, not a pure replay. All six retain their order, authors, author dates, and full messages. Range-diff has two ! entries followed by four = entries. The first is deliberate main composition; the second differs only in surrounding main test context, with byte-identical ordered additions/deletions across all eight paths. The last four full stable patch IDs are unchanged. The second full stable patch ID changes because its surrounding context changes.
  • Replay head a1d7537832d163ea95f8d53b89520334fc514f01, tree 6ed8985f78020532a57a2003fcc3fe19a38f2198. The first resolution retains main's admin logic and replaces overlapping legacy lifecycle hydration with the new snapshot mechanism.
  • A separate seventh commit, fix(auth): preserve credential lifecycle in primary snapshots, deliberately composes fix(auth): contain destructive identity consolidation #7459's durable deletion/quarantine markers and exact-one-primary check into the new atomic statement. Tests cover tombstones before/after commit, recreated user rows, independent replicas, cross-middleware cache rejection, exact-credential admin attribution, and lifecycle denial. The predecessor cross-replica test still demands immediate 401; its provider-call expectation changes from three to two because provider authentication remains cached.
  • Independent tree reconstruction used an aggregate three-way merge and separately rebuilt middleware from the historical feature plus main's admin functions and the historical dev hydration change. It matched the replay tree exactly, then applied the separately archived correction and matched the reviewed tree. Per-commit patch IDs, authored metadata, per-path blob IDs, range-diff, and the correction SHA-256 are archived under .context/landing-7462/.
  • Migration diff is empty. The complete base migration subtree be6336113c9b2db393b6c3a0550bc72f727c9955 is unchanged: all 576 migrations, zero duplicate numbers, and unchanged allocation records. No new migration is allocated.
  • All package manifests and lockfiles are unchanged. Fresh exact-lock installation reported 15 inherited advisories (4 moderate, 11 high); dependency delta is empty, not a claim of zero inherited vulnerabilities.

Qualification

  • Fresh npm ci in Docker (Node 24.21.0, npm 11.19.0), with an isolated PostgreSQL 16 container and no production access.
  • Implicated auth/identity/deadline/race unit tests: 92 files, 1,339 passed.
  • Snapshot, epoch, identity, predecessor deletion/quarantine, admin binding, membership, OAuth and cross-replica integrations: 13 files, 162 passed across the preserved final runs. Snapshot integration includes the bounded blocked-statement recovery case. Private-ledger migration integration: 36 passed.
  • Migration naming/uniqueness checks: 3 passed. Dynamic import guard: passed. Addie reference and runtime inventories: current (249 tools, 69 sets), with portability check passed. Changeset protocol-scope and git diff --check: passed.
  • Both TypeScript phases passed: server and fixed-trace rollout test-aware typecheck.
  • Normal full commit hook on the final deliberate source correction: the single user-authorized replacement passed in full (exit 0): root 71 files / 1,063 tests passed; server 636 files / 10,206 tests passed and 31 unchanged skips; all three loopback tests passed; both TypeScript phases passed. No bypass, test exclusion, or source/staging/tree change. The first attempt failed only the IPv6 loopback test (two failures and one unhandled error; 10,204 server tests passed, 31 skipped). Diagnosis reproduced the same failures on byte-verified untouched current main in the original container network namespace, where IPv6 was disabled and ::1 absent. The same main source/image/dependencies passed all three focused tests with IPv6 enabled. The user accepted this deterministic baseline container-configuration failure as invalid qualification and authorized exactly one replacement normal full hook in an equivalent Node 24.21.0 container with IPv6 enabled. Original failed log and controlled baseline evidence are preserved.
  • Independent read-only security/composition review on the final exact base/head/tree: zero Must Fix / zero Should Fix, independently bound to base 54fd5f3c0a3cd5ac295d2f295bb7dc7232803ab9, head 0c14d9fd9b49a90faec3f17899aa2e44f686d2a3, and reviewed tree 8bdd72b68c7e0e8d01240d57b0c7542137db5edb; source/blob/diff/correction hashes and successful full-hook log digest reverified. Zero Must Fix / Should Fix findings required for publication.
  • Initial test failures are preserved in evidence: lifecycle fixtures/expectations required adaptation to the new snapshot schema and provider-only caching; the new audit fixture needed consistent PostgreSQL parameter casts. The initial Docker static-check failure was Git's ownership trust check; configuring the exact worktree as safe inside the container allowed the normal check to pass.
  • Normal pre-push checks: passed during the explicit force-with-lease publication (exit 0): version synchronization and changeset policy passed; the hook found no protocol, storyboard, or docs scope requiring additional checks. No hook bypass or manual skip flags were used. Auto-merge remains off. Fresh exact-head main-targeted GitHub CI, scanner output/annotations, dependency graph delta, IPR, resolved review threads, exact-head aao-secretariat approval, CLEAN mergeability, unchanged main, and GitHub merge-tree equality remain mandatory before guarded squash merge.

Ordered dependency scope

#7461 remains the mandatory next child for the pre-existing required-auth API-key/JWT/cookie fallback and WorkOS API-key outage-classification issues. Those behaviors are not claimed fixed by #7462. No #7461, #7463, or later PR is mutated here. This contributes to #6827's snapshot/epoch requirements without closing the broader issue or claiming standalone closure of every Bearer boundary.

The user has authorized merging this ordered dependency after its current-head gates pass, and explicitly accepts exact-head APPROVED from aao-secretariat. Old approvals and old CI are historical. Never use admin bypass, auto-merge, deployment, production access, or explicit head-branch deletion.

@bokelley
bokelley requested a review from a team September 13, 2026 14:10
Comment thread server/src/middleware/auth.ts Resolved
aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Sep 13, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — per-request authorization-snapshot auth refactor.

Scope: server/** only, no protocol surface (static/schemas/source/, docs/reference/, dist/**) touched — correctly no changeset required. No high-risk paths, no gated paths, no author team gate.

Reviewed: single-row MVCC authorization snapshot with fail-closed behavior, replay/cross-org rejection, non-enumerable provenance, and exact-credential bans, all backed by unit + integration tests. Core logic is well-tested with no Critical/High findings.

Medium findings (2, non-blocking):

  • resolve-user-org-authorization / snapshot query bypasses the client.ts query() transient-retry wrapper and lacks a statement timeout on the now-universal auth path.
  • optionalAuth now hard-fails 503/401 for credential-bearing requests instead of degrading to anonymous.

Decision: No critical/high findings (rows 1 skipped). gated_paths is false (row 2 n/a). high_risk false (rows 3/5 n/a). No medium finding in data-loss/schema/infra category (row 4 n/a). No prior escalation (row 6 n/a). No no-auto-approve team (row 7 n/a). Only 2 medium findings — fewer than 3 (row 8 n/a). Falls through to row 9 → approve.

Worth a follow-up look at the retry-wrapper bypass and optionalAuth degradation behavior, but neither blocks per the decision table.

aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Sep 13, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — subsequent pass on PR #7462 (per-request auth snapshot hydration), no blocking or medium findings.

This delta hardens transient-vs-invalid credential handling (503 vs 401), adds a bounded snapshot query wrapper (2s budget, one transient-connection retry, fail-closed on late/replica/error results), tightens provider/selector org agreement to 403-on-mismatch, and makes optionalAuth fail-closed on presented-but-invalid credentials. All changes are server-only — no protocol/schema/dist surface touched, so no changeset is required. Operational readiness (statement/lock timeouts, bounded retry, 503 observability) and the optionalAuth control flow were verified.

Decision-table trace: no critical/high findings (row 1 n/a); gated_paths false (row 2 n/a); high_risk false, no deletions/modifications triggers (rows 3–5 n/a); prior decision was approve, so sticky escalation does not apply (row 6 n/a); no no-auto-approve team match (row 7 n/a); zero medium findings (row 8 n/a). Falls through to row 9 — approve. The prior clean approve holds.

aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Sep 13, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean pass, no blocking or medium findings.

The delta since the prior approve (head bb7be31 → 853b8a3) is confined to:

  • A single CodeQL suppression comment in optionalAuth (server/src/middleware/auth.ts:1860) that accurately documents the fail-closed credential-absence branch. No executable auth logic changed.
  • Nine new Express/Supertest HTTP-boundary tests asserting that present-but-invalid credentials never reach the handler (401), unavailable snapshots return 503, and anonymous access is permitted only when both credentials are absent.

No protocol-surface (static/schemas/source/**), docs, dist/**, or changeset files touched — so schema↔docs coherence, changeset scope, oneOf discriminator, and artifact-immutability gates do not apply. No 3.1.x patch-eligibility or breaking-class concerns (base is main). high_risk is false; gated_paths is false; no no-auto-approve team match.

Decision table: rows 1–8 do not fire (no critical/high/medium findings, no gated path, no high-risk deletion/modification, no sticky escalation, no team gate, fewer than three medium findings). Falls through to row 9 → approve.

aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Sep 13, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean delta pass, no blocking or medium findings.

The reviewer reports a behavior-preserving rewrite of the optionalAuth credential-absence guard into a single ternary early-return (identical 401-vs-next() outcomes) plus a test-harness swap that injects req.cookies from a fixture matching the network-health pattern without weakening presence assertions. No schema, changeset, dist, or migration surface is touched, and no prior blockers remain.

Gate checks: no no-auto-approve team match; gated_paths false; high_risk false. No critical/high/medium findings, so rows 1–8 do not fire — falls through to row 9 (approve). Prior decision was also approve with no findings; this remains a clean state.

@bokelley
bokelley force-pushed the conductor/cold-cache-auth-snapshot branch from 423f6d8 to b1789a6 Compare September 13, 2026 15:06
@bokelley
bokelley force-pushed the conductor/cold-cache-auth-snapshot branch from b1789a6 to 9b9010f Compare September 14, 2026 00:55
@bokelley
bokelley requested review from a team and removed request for a team September 14, 2026 01:39
@bokelley
bokelley removed the request for review from a team September 14, 2026 16:25
@bokelley
bokelley force-pushed the conductor/cold-cache-auth-snapshot branch from 7b9876d to 438f952 Compare September 14, 2026 16:37
@bokelley
bokelley force-pushed the conductor/cold-cache-auth-snapshot branch from 438f952 to 3216b0d Compare September 15, 2026 20:40
@bokelley
bokelley requested a review from a team September 15, 2026 21:06
@bokelley
bokelley marked this pull request as ready for review September 17, 2026 07:27
aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Sep 17, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean pass on the fail-closed WorkOS auth rewrite (per-request primary-DB authorization snapshot replacing epoch fingerprint caching).

No critical, high, or medium findings. Verification confirmed:

  • The snapshot SELECT joins are all single-row (unique active-grant index in migration 553; PK + one-primary index in migration 460).
  • db/client.ts deadline/uncertain-client accounting releases or destroys clients correctly.
  • Platform-only change (server/**); no protocol/schema/docs/changeset surface, so no versioning, oneOf, or governance findings apply.

Gate check: gated_paths=false, high_risk=false, no no-auto-approve team match, no blocking findings. Prior decision was also approve with no findings. None of decision rows 1–8 fire, so this falls through to row 9 → approve.

@bokelley
bokelley marked this pull request as draft September 17, 2026 13:04
@bokelley
bokelley force-pushed the conductor/cold-cache-auth-snapshot branch from 3216b0d to 0c14d9f Compare September 17, 2026 13:56
@bokelley
bokelley marked this pull request as ready for review September 17, 2026 14:06

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean pass, no blocking findings.

Reviewed the server-only per-request primary-DB authorization snapshot refactor across db/client.ts, user-authorization-snapshot-db.ts, middleware/auth.ts, and resolve-user-org-authorization.ts. Deadline-bounded pool checkout destroys uncertain clients without leaking slots; snapshot hydration fails closed (401 invalid / 503 unavailable / 403 conflicting-org); caches retain only provider auth; server-only authority is non-enumerable. No protocol/schema/changeset/migration surface touched, so no spec-drift, changeset-scope, oneOf, or immutability concerns apply.

Gate checks:

  • No no-auto-approve team match.
  • gated_paths is false — row 2 does not fire even though review_decision is REVIEW_REQUIRED.
  • high_risk is false — no deletion/modification escalation rows apply.
  • Prior decision was approve, so no sticky escalation.
  • Zero findings of any severity → rows 1, 4, 5, 8 do not fire.

None of rows 1–8 fire; the outcome is approve per row 9.

@bokelley
bokelley merged commit 32f25d9 into main Sep 17, 2026
34 checks passed
@bokelley
bokelley deleted the conductor/cold-cache-auth-snapshot branch September 17, 2026 14:17
bokelley added a commit that referenced this pull request Sep 24, 2026
…on (#7675)

* fix(auth): let web chat rebind the session to the selected organization

Since #7462 a cookie session is bound to the WorkOS org chosen at sign-in, and a differing explicit org selector is rejected. Add POST /auth/switch-organization, which refreshes the sealed session with the target organizationId (WorkOS verifies membership). Web chat calls it once on that 403 and resends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(auth): update integration auth mocks

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brian O'Kelley <bokelley@scope3.com>
bokelley added a commit that referenced this pull request Oct 1, 2026
…7825)

Since #7462, optionalAuth returns 401 for a present-but-unusable
wos-session cookie instead of treating the caller as anonymous. The
Agents page swallowed that error and rendered an empty registry.

Retry the public list once with credentials omitted on 401, and show an
error instead of an empty grid for any other failure.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

2 participants