feat(auth): add token-bound step-up elevations for second-factor management

Second-factor management has been cookie-only since v1.23. The gate is right:
a Bearer token is a credential whose provenance the server cannot see, and
tearing down a second factor must not be reachable from the weaker of an
interactive login and a value pasted into a script. What was missing was never
Bearer access to those routes — it was a way to say, over a transport with no
session row, that the human holding this device just re-proved a factor.

An elevation is that proof. It is minted only against a re-proved account
password or primary passkey, bound to the exact token that re-proved, single-use
via a conditional UPDATE, five-minute-lived to match the cookie path's step-up
window, and stored hashed. A stolen token mints nothing; a second token cannot
redeem another's elevation; a password change drops every one the account holds.

requireMfaManagementAuth is the one gate the management routes share. On a
cookie session it delegates to the original helpers unchanged, so the web flow
runs the code it always did. requireAdmin stays cookie-only and out of reach.
This commit is contained in:
Marc-André Bombeck committed 2026-07-19 12:42:00 +02:00
1 parent 571e415eaa
commit e557d9e9e6
19 files changed
+808 -51

No files matched your search

+4 -3
View File
@@ -83,12 +83,13 @@ export function redactSecrets(input: string): string {
// tiny safety margin against `sk-1`-style false-positives in code
// identifiers. Capture group 1 = the leading separator (preserved).
.replace(/(^|[^A-Za-z0-9])sk-(?:ant-)?[A-Za-z0-9_-]{8,}/g, "$1[REDACTED]")
// HealthLog native API tokens (`hlk_<64hex>` access, `hlr_<64hex>`
// refresh). Same boundary-aware shape as the `sk-` rule. The
// HealthLog hex-bodied credentials: `hlk_` access token, `hlr_` refresh
// token, `hls_` session cookie secret (v1.30.32), `hle_` step-up
// elevation (v1.30.34). Same boundary-aware shape as the `sk-` rule. The
// idempotency replay-cache already rejects bodies containing
// these prefixes (CLAUDE.md headless-client-API patterns); this
// is the matching egress guard for log/error surfaces.
.replace(/(^|[^A-Za-z0-9])hl[kr]_[A-Fa-f0-9]{32,}/g, "$1[REDACTED]")
.replace(/(^|[^A-Za-z0-9])hl[kres]_[A-Fa-f0-9]{32,}/g, "$1[REDACTED]")
// v1.22.0 — the MCP OAuth bridge artifacts: the authorization code
// (`hlac_`), refresh token (`hlrt_`), and DCR client id (`hlc_`). All
// are signed, base64url-bodied, and credential-bearing, so they are