#283: Secure DLQ list and deploy status endpoints with admin authentication to prevent unauthorized access to sensitive operations and webhook replay capabilities.
This PR adds a comprehensive admin authentication guard (adminAuthGuard) middleware that secures all admin-only endpoints with two independent authentication methods: JWT with admin role verification and API key with admin-level scope validation. All DLQ management and blue-green deployment operations now require proven admin credentials before execution.
src/middleware/adminAuthGuard.ts— Admin auth guard middleware supporting JWT and API key authentication with scope/role validationsrc/middleware/adminAuthGuard.test.ts— 19 test cases covering JWT validation, API key auth, scope checks, demo tokens, and error handlingsrc/routes/deploy.routes.ts— Deploy status, switch-green, and rollback endpoints protected by adminAuthGuardsrc/routes/deploy.routes.test.ts— Deploy route tests with auth validation, credential redaction, and deployment state verification
-
src/index.ts- Protect
GET /api/v1/jobs/dlqwithadminAuthGuard - Protect
POST /api/v1/jobs/dlq/reprocesswithadminAuthGuard - Import adminAuthGuard middleware
- Protect
-
src/app.ts— Setup admin auth middleware integration -
src/config/env.schema.ts— AddJWT_SECRETvalidation for admin auth -
docs/api-keys.md— Document admin scopes (deploy:*,jobs:admin,jobs:*,*) and JWT role requirements
Authorization: Bearer <jwt>- Validated with
jsonwebtokenusingJWT_SECRET(HS256) - Requires JWT
roleclaim to be one of:admin,superadmin - Rejects expired tokens and invalid signatures
- Demo token for tests:
demo-admin-token
X-API-Key: <key>- Verified against stored API key hashes using
crypto.timingSafeEqual - Requires scope in
['deploy:*', 'jobs:admin', 'jobs:*', '*'] - Checks expiration and activation status
- Returns 403 if key lacks admin scope
All responses follow RFC 7231 and RFC 6585 standards:
- 401 Unauthorized — Missing credentials or invalid token/key
- 403 Forbidden — Valid credentials but insufficient permissions
- No sensitive diagnostics — Error messages never leak role/scope details
Example:
{
"error": {
"code": "unauthorized",
"message": "Authentication required. Provide Bearer JWT or X-API-Key.",
"requestId": "abc-123"
}
}GET /api/v1/jobs/dlq?type=<job-type>&limit=<n>&offset=<n>POST /api/v1/jobs/dlq/reprocess(with audit logging)
GET /api/v1/admin/deploy/statusPOST /api/v1/admin/deploy/switch-greenPOST /api/v1/admin/deploy/rollback
- Timing-safe comparison for API key validation prevents timing attacks
- Credential redaction in logs via
redactSecret()utility - Demo token bypass for test environments only (
demo-admin-token) - Audit logging for all admin operations (DLQ reprocess, deployment state changes)
- No partial grants — single failed auth attempt rejects immediately
19 tests validate:
- ✅ Missing credentials (401)
- ✅ Invalid JWT signature (401)
- ✅ Expired JWT (401)
- ✅ Non-admin JWT role (401 or 403)
- ✅ Valid admin JWT allows access
- ✅ Demo tokens work in test environments
- ✅ Invalid API key (401)
- ✅ API key with insufficient scope (403)
- ✅ API key with admin scopes (
deploy:*,jobs:admin,jobs:*,*) - ✅ Expired API key (401)
- ✅ Credential redaction in audit logs
- ✅ Deployment routes reject unauthenticated access
- ✅ Deployment routes accept valid admin JWT
JWT_SECRET=<your-secret-key> # Used to sign/verify admin JWTs# Full access
scope: ['*']
# Deploy operations only
scope: ['deploy:*']
# DLQ operations only
scope: ['jobs:admin'] or ['jobs:*']None — existing endpoints remain unchanged. Only adds auth enforcement to previously unprotected admin operations.
docs/api-keys.md— Admin scope definitions and JWT role requirements- Inline comments in
adminAuthGuard.tsand route files
- Ensure
JWT_SECRETenvironment variable is set before deploying - Generate admin API keys with appropriate scopes for service accounts
- Audit logs now record all admin operations with actor ID and resource details
- RFC 7231: HTTP Semantics (401, 403)
- RFC 6585: HTTP Status Codes (429 per issue #259)
- OWASP: Authentication Cheat Sheet