Difficulty: Advanced
Problem
1. POST /api/queues — unauthenticated queue creation
backend/src/routes/queues.ts — router.post('/') creates a new queue with no authentication check. Any caller with network access to the API can create queues, polluting the queue registry with arbitrary data. The only validation is the Zod schema (name, slug, maxPositions). There is no operator credential, API key, or JWT check.
2. POST /api/queues/:id/advance and POST /api/queues/:id/close — unauthenticated state mutations
These two routes mutate queue lifecycle state (advancing positions, closing queues) with no authentication. An attacker can prematurely close any queue or trigger advancement before the operator intends it. The writeRateLimiter (20 req/min) is applied, but this is not an authentication control.
3. No authentication middleware anywhere in the backend
The entire backend has no authentication layer. backend/src/index.ts applies helmet, cors, rate limiting, and request logging — but no JWT verification, no API key middleware, and no session management. The admin-only routes (advance, close, escrow release/refund) are indistinguishable from participant routes (enroll, lookup) from an auth perspective.
Impact: Any external actor can create queues, advance active queues early, close queues prematurely, and release escrow funds — all without any credential. This is a critical security gap for any production deployment.
Proposed Solution
- Create
backend/src/middleware/auth.ts with an API key authentication middleware: reads X-API-Key header, validates against an OPERATOR_API_KEY environment variable (hashed with bcrypt or compared via crypto.timingSafeEqual).
- Define two auth tiers:
requireAuth (operator only, for create/advance/close/escrow-release) and no auth required (read-only routes, participant enrollment).
- Apply
requireAuth to: POST /api/queues, POST /api/queues/:id/advance, POST /api/queues/:id/close, POST /api/escrow/release, POST /api/escrow/refund.
- Add
OPERATOR_API_KEY to backend/.env.example with a note about hashing.
- Add auth tests: valid key passes, invalid key returns 401, missing key returns 401, timing-safe comparison (no timing oracle).
Acceptance Criteria
Contributor Note
If assigned, your PR must explain the crypto.timingSafeEqual requirement (why naive === comparison is vulnerable to timing attacks), describe how OPERATOR_API_KEY should be generated and stored in production (length, entropy), show the 401 response format, and discuss whether JWT with short expiry is preferable to static API keys for this use case.
Difficulty: Advanced
Problem
1.
POST /api/queues— unauthenticated queue creationbackend/src/routes/queues.ts—router.post('/')creates a new queue with no authentication check. Any caller with network access to the API can create queues, polluting the queue registry with arbitrary data. The only validation is the Zod schema (name,slug,maxPositions). There is no operator credential, API key, or JWT check.2.
POST /api/queues/:id/advanceandPOST /api/queues/:id/close— unauthenticated state mutationsThese two routes mutate queue lifecycle state (advancing positions, closing queues) with no authentication. An attacker can prematurely close any queue or trigger advancement before the operator intends it. The
writeRateLimiter(20 req/min) is applied, but this is not an authentication control.3. No authentication middleware anywhere in the backend
The entire backend has no authentication layer.
backend/src/index.tsapplieshelmet,cors, rate limiting, and request logging — but no JWT verification, no API key middleware, and no session management. The admin-only routes (advance, close, escrow release/refund) are indistinguishable from participant routes (enroll, lookup) from an auth perspective.Impact: Any external actor can create queues, advance active queues early, close queues prematurely, and release escrow funds — all without any credential. This is a critical security gap for any production deployment.
Proposed Solution
backend/src/middleware/auth.tswith an API key authentication middleware: readsX-API-Keyheader, validates against anOPERATOR_API_KEYenvironment variable (hashed with bcrypt or compared viacrypto.timingSafeEqual).requireAuth(operator only, for create/advance/close/escrow-release) and no auth required (read-only routes, participant enrollment).requireAuthto:POST /api/queues,POST /api/queues/:id/advance,POST /api/queues/:id/close,POST /api/escrow/release,POST /api/escrow/refund.OPERATOR_API_KEYtobackend/.env.examplewith a note about hashing.Acceptance Criteria
backend/src/middleware/auth.tscreated withrequireAuthmiddlewarerequireAuthreadsX-API-Keyheader and validates againstOPERATOR_API_KEYenv varcrypto.timingSafeEqualto prevent timing oracle attacksPOST /api/queues(create) requires authenticationPOST /api/queues/:id/advancerequires authenticationPOST /api/queues/:id/closerequires authenticationPOST /api/escrow/releaseandPOST /api/escrow/refundrequire authenticationX-API-Keyreturns{ error: { message: "Unauthorized", status: 401 } }OPERATOR_API_KEYdocumented inbackend/.env.exampleContributor Note
If assigned, your PR must explain the
crypto.timingSafeEqualrequirement (why naive===comparison is vulnerable to timing attacks), describe howOPERATOR_API_KEYshould be generated and stored in production (length, entropy), show the 401 response format, and discuss whether JWT with short expiry is preferable to static API keys for this use case.