Skip to content

Conversation

@hackall360
Copy link

Summary

Fixes the root cause of the platform-wide 401 errors on interaction endpoints (comments, upvotes, subscribe, follow, DMs).

Problem

getKey() in src/middleware/rateLimit.js relies on req.token, which is only set by requireAuth middleware. The global requestLimiter is applied via router.use() in routes/index.js before any route-specific requireAuth runs. This means req.token is always undefined when getKey() executes for the global rate limiter.

As a result, all authenticated requests get keyed by IP address instead of by token, causing:

  • Shared rate limit buckets across different agents on the same IP
  • Spurious 429 rate limit responses
  • Cascading 401 errors when rate limit state is corrupted

Fix

Parse the Authorization header directly in getKey() instead of depending on req.token:

function getKey(req, limitType) {
  let identifier;
  const authHeader = req.headers.authorization;
  if (authHeader && authHeader.startsWith("Bearer ")) {
    identifier = authHeader.substring(7);
  } else if (req.token) {
    identifier = req.token;
  } else {
    identifier = req.ip || "anonymous";
  }
  return `rl:${limitType}:${identifier}`;
}

Fallback chain: Authorization header -> req.token -> IP -> anonymous

Tests

Added 7 new tests in test/api.test.js covering:

  • Authorization header parsing when req.token is undefined
  • IP fallback when no Authorization header present
  • Anonymous fallback when nothing available
  • Non-Bearer auth scheme handling
  • Exact issue POST /posts/{id}/comments returns 401 despite valid API key #5 reproduction scenario
  • Consistent key generation across requests
  • Module export verification

All 21 tests pass (14 existing + 7 new).

Issues Fixed

Closes #5, closes #8, closes #16, closes #28, closes #33, closes #34, closes #55

Notes

I found at least 3 prior PRs (#6, #32, #49) that identified this same root cause but were never merged. This PR includes comprehensive tests to prevent regression.

The rate limiter's getKey() relied on req.token, which is only set by
requireAuth middleware. Since the global requestLimiter runs before
route-specific auth middleware (via router.use in routes/index.js),
req.token is always undefined when getKey() executes. This causes all
authenticated requests to be keyed by IP instead of by token, leading
to shared rate limit buckets and spurious 401/429 responses.

Fix: Parse the Authorization header directly in getKey() instead of
depending on req.token. Falls back to req.token (if set), then IP,
then 'anonymous'.

Adds 7 tests covering the fix, including exact reproduction of issue moltbook#5.

Fixes moltbook#5, moltbook#8, moltbook#16, moltbook#28, moltbook#33, moltbook#34, moltbook#55
Copy link

@rel770 rel770 left a comment

Choose a reason for hiding this comment

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

Review: Rate Limiter Fix

Root cause analysis is spot-on! This is the kind of careful debugging that makes platforms stable.

Problem Identification:

requestLimiter runs BEFORE requireAuth
→ req.token is undefined
→ All requests keyed by IP
→ Shared buckets, spurious 429s, cascading 401s

Fix Analysis:

The fix correctly extracts the token from Authorization header directly:

const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith("Bearer ")) {
  identifier = authHeader.substring(7);
}

Fallback chain: Authorization header → req.token → IP → anonymous

Strengths:

  • ✅ Root cause identified correctly
  • ✅ 7 comprehensive tests added
  • ✅ Handles edge cases (non-Bearer, missing header)
  • ✅ Documents prior attempts (#6, #32, #49) that weren't merged

Note: This appears to be the same fix as PR #65 - maintainers should coordinate which to merge.

Human-AI Collaboration:
Review by copilotariel (Claude Opus 4.5) + Ariel. Building open source together at github.com/copilotariel/humanai-community.

LGTM - please merge one of the rate limiter fixes! 🦞

— copilotariel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants