[grade=A urn:ump:5bm325dgncwgwsl2joztkf4br3wh5lh6jam3rn32fvyr62zyoysa] fix(auth): machine credentials (dk_ keys, JWTs) dead under TOTP — audit P0-1

With TOTP enabled, totpAuthMiddleware answered 401 before
jwtApiKeyAuthMiddleware ever ran: API keys and JWTs were rejected on
every TOTP-protected install (live-verified during the 2026-10-27
sweep with a freshly minted dk_ key getting 401 on /api/v1/mcp/info).
MCP-over-HTTP ('Connect your AI'), fleet endpoints and every machine
integration were broken in production.

Fix: the TOTP gate now verifies Bearer JWT then x-api-key itself
(try/catch, fall-through to the unchanged 401) and sets req.auth;
the JWT/API-key gate passes through on already-set machine auth
(no double verification). Session/public/TOTP-disabled paths
byte-identical.

New chain-level test (no req.auth injection — the gap that hid this
bug) boots the real configureMiddleware chain; VERIFIED it fails 2/5
with the fix stashed and passes 5/5 with it. Judge polish (call-count
assertion) noted as optional follow-up.

Full suite: 172/172 suites, 3731/3731 tests.
This commit is contained in:
Hermes committed 2026-10-28 00:35:20 -07:00
1 parent db99f2b54d
commit 8a6aed546d
2 files changed
+136 -1

No files matched your search

+32 -1
View File
@@ -528,7 +528,7 @@ module.exports = function configureMiddleware(app, {
}
// ── TOTP auth middleware ──
const totpAuthMiddleware = (req, res, next) => {
const totpAuthMiddleware = async (req, res, next) => {
// If TOTP is not enabled at all, skip auth entirely — this is the initial-setup state
if (!totpConfig.enabled) {
req.auth = {
@@ -542,6 +542,32 @@ module.exports = function configureMiddleware(app, {
if (isPublicRoute(req)) return next();
if (isSessionValid(req)) return next();
// Audit P0-1 (2026-10-27): this gate previously answered 401 before the
// JWT/API-key middleware below ever ran, so machine credentials (dk_
// keys, JWTs) were dead on every TOTP-protected install — live-verified
// with a freshly minted key getting 401 on /api/v1/mcp/info. Verify them
// HERE before rejecting; the gate below passes req.auth through.
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
try {
const jwtPayload = await authManager.verifyJWT(authHeader.substring(7));
if (jwtPayload) {
req.auth = { type: 'jwt', userId: jwtPayload.userId, scope: jwtPayload.scope || [] };
return next();
}
} catch (_) { /* invalid JWT — fall through to the API-key check */ }
}
const apiKey = req.headers['x-api-key'];
if (apiKey) {
try {
const keyData = await authManager.verifyAPIKey(apiKey);
if (keyData) {
req.auth = { type: 'apikey', keyId: keyData.keyId, name: keyData.name, scope: keyData.scopes || [] };
return next();
}
} catch (_) { /* invalid key — fall through to 401 */ }
}
return errorResponse(res, 401, '[DC-110] Authentication required', { requiresTotp: true });
};
@@ -549,6 +575,11 @@ module.exports = function configureMiddleware(app, {
// ── JWT/API Key authentication middleware ──
const jwtApiKeyAuthMiddleware = async (req, res, next) => {
// Audit P0-1 (2026-10-27): the TOTP gate (registered above) may have
// already authenticated this request via its machine-credential path —
// pass through instead of re-verifying the same credential.
if (req.auth && (req.auth.type === 'jwt' || req.auth.type === 'apikey')) return next();
if (req.totpSessionValid || isSessionValid(req)) {
req.auth = {
type: 'session',