DC-029: skip authLimiter for already-authenticated requests
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled

The DC-027 rate limiter on /api/v1/auth/* shipped with skip: () => isTest,
which counted every request — including those from a logged-in TOTP session.
Caddy's forward_auth fires /auth/gate/* on every page-load asset (HTML, JS,
CSS, XHR), so a normal browser session exhausted the 20-req/15-min budget
within ~3 page loads and started getting 429 'Too many auth requests' even
with a valid session cookie.

Fix: extend skip to also return true when req.auth.type is 'session',
'jwt', or 'apikey' (set by jwtApiKeyAuthMiddleware, which runs upstream
of the limiter). The unauthenticated path is still rate-limited — DC-027's
credential-scraping defense is preserved.

Also closes the uncommitted working-tree changes for:
- DC-026: routes/auth/sso-gate.js — pre-auth check in buildLoginPage,
  redirected error fallbacks to status.sami?auth=required&return=...
- DC-022: dashcaddy-api/VERSION bumped to fef7e07
- status/index.html + status/js/tailscale-devices.js — Tailscale device card

4 new regression tests pin the fix:
- skips when req.auth.type === 'session'
- skips when req.auth.type === 'jwt'
- skips when req.auth.type === 'apikey'
- still counts UNAUTHENTICATED requests (defense preserved)

Live verified: 50/50 authenticated /auth/gate/plex calls passed (was
20/30 before fix). plex.sami/dashcaddy-login returns 200 with no redirect
loop. Plex auto-login token round-trips end-to-end.
This commit is contained in:
Krystie
2026-07-02 18:27:03 -07:00
parent 57de3cb8e3
commit a92eeceae5
6 changed files with 532 additions and 12 deletions
+8 -1
View File
@@ -490,7 +490,14 @@ module.exports = function configureMiddleware(app, {
...RATE_LIMITS.STRICT,
standardHeaders: true,
legacyHeaders: false,
skip: () => isTest,
// SECURITY [DC-027]: rate limit credential scraping. Skip when the caller
// is already authenticated — req.auth.type is set by jwtApiKeyAuthMiddleware
// (above this in the chain), so by the time this runs we know whether the
// request came from a logged-in session, JWT, or API key. Without this
// exception, Caddy's forward_auth chatter on every page-load asset
// (HTML, JS, CSS, XHR) burns the budget for legit users — every browser
// session trips 429 within ~3 page loads.
skip: (req) => isTest || req.auth?.type === 'session' || req.auth?.type === 'jwt' || req.auth?.type === 'apikey',
message: { success: false, error: 'Too many auth requests, please try again later' }
});
app.use('/api/v1/auth/keys', authLimiter);