# A-3 Auth Middleware Fix Implementation Report **Date:** 2026-03-29 **Branch:** culurien **Audit Reference:** A-3 Auth Middleware Audit --- ## Summary This document covers fixes for 9 auth middleware findings (F-A3-2 through F-A3-14). --- ## Files Changed ### Fixes | File | Change | |------|--------| | `aggregator-server/internal/api/handlers/auth.go` | Removed JWT secret from log (F-A3-11), added `log` import, set issuer=redflag-web on web tokens (F-A3-12), issuer validation in WebAuthMiddleware, set user_role in context for RequireAdmin | | `aggregator-server/internal/api/middleware/auth.go` | Added JWT issuer constants, issuer validation in AuthMiddleware (F-A3-12), backward compat grace period for missing issuer, added `log` import | | `aggregator-server/internal/api/middleware/require_admin.go` | NEW: RequireAdmin() middleware (F-A3-13) — checks user_role from context | | `aggregator-server/internal/api/middleware/cors.go` | CORS origin from REDFLAG_CORS_ORIGIN env var (F-A3-14), added PATCH method and agent headers | | `aggregator-server/internal/api/handlers/security_settings.go` | Renamed from .broken, fixed API mismatches with service (F-A3-13) | | `aggregator-server/cmd/server/main.go` | Protected config download with WebAuthMiddleware (F-A3-7), protected update download with AuthMiddleware (F-A3-6), scheduler stats changed to WebAuthMiddleware (F-A3-10), /auth/verify gets WebAuthMiddleware (F-A3-2), agent unregister gets rate limiter (F-A3-9), security settings routes uncommented (F-A3-13), securitySettingsHandler initialized | | `config/.env.bootstrap.example` | Added REDFLAG_CORS_ORIGIN documentation | | `aggregator-server/.env.example` | Added REDFLAG_CORS_ORIGIN documentation | ### Tests Updated | File | Change | |------|--------| | `handlers/auth_middleware_leak_test.go` | Tests now PASS (secret removed from log) | | `handlers/downloads_auth_test.go` | Updated to test with auth middleware, added agent JWT helper | | `handlers/auth_verify_test.go` | No changes needed (already passes) | | `handlers/agent_unregister_test.go` | Updated route registration strings | | `middleware/scheduler_auth_test.go` | Updated to use WebAuthMiddleware, agent JWT now includes issuer | | `middleware/token_confusion_test.go` | JWTs now include issuer claims | | `middleware/require_admin_test.go` | No changes needed (AST scan passes) | | `middleware/require_admin_behavior_test.go` | Removed //go:build ignore tag, tests active | --- ## JWT Issuer Claims (F-A3-12) ### New Issuer Values - Agent tokens: `Issuer = "redflag-agent"` (set in `GenerateAgentToken`) - Web tokens: `Issuer = "redflag-web"` (set in `Login` handler) ### Validation - `AuthMiddleware` rejects tokens with `Issuer != "redflag-agent"` (if issuer is present) - `WebAuthMiddleware` rejects tokens with `Issuer != "redflag-web"` (if issuer is present) ### Backward Compat Grace Period - Tokens with empty/absent issuer are allowed through with a logged warning - This preserves compatibility with deployed agents that have existing JWTs - TODO: Remove issuer-absent grace period after 30 days from deployment --- ## RequireAdmin Implementation (F-A3-13) - Located in `middleware/require_admin.go` - Runs AFTER WebAuthMiddleware (depends on `user_role` in context) - WebAuthMiddleware updated to set `user_role` from `UserClaims.Role` - If role != "admin": returns 403 with `[WARNING] [server] [auth] non_admin_access_attempt` ### Security Settings Routes Re-enabled (7 routes) 1. `GET /api/v1/security/settings` — GetAllSecuritySettings 2. `GET /api/v1/security/settings/audit` — GetSecurityAuditTrail 3. `GET /api/v1/security/settings/overview` — GetSecurityOverview 4. `GET /api/v1/security/settings/:category` — GetSecuritySettingsByCategory 5. `PUT /api/v1/security/settings/:category/:key` — UpdateSecuritySetting 6. `POST /api/v1/security/settings/validate` — ValidateSecuritySettings 7. `POST /api/v1/security/settings/apply` — ApplySecuritySettings All protected by: WebAuthMiddleware + RequireAdmin --- ## Test Results ### Server Tests (all passing) ``` --- PASS: TestAgentAuthMiddlewareDoesNotLogSecret --- PASS: TestAgentAuthMiddlewareLogHasNoEmoji --- PASS: TestRequireAdminBlocksNonAdminUsers --- PASS: TestRequireAdminMiddlewareExists --- PASS: TestSchedulerStatsRequiresAdminAuth --- PASS: TestSchedulerStatsCurrentlyAcceptsAgentJWT --- PASS: TestWebTokenRejectedByAgentAuthMiddleware --- PASS: TestAgentTokenRejectedByWebAuthMiddleware ok middleware --- PASS: TestWebAuthMiddlewareDoesNotLogSecret --- PASS: TestWebAuthMiddlewareLogFormatHasNoEmoji --- PASS: TestWebAuthMiddlewareLogFormatCompliant --- PASS: TestConfigDownloadRequiresAuth --- PASS: TestConfigDownloadCurrentlyUnauthenticated --- PASS: TestUpdatePackageDownloadRequiresAuth --- PASS: TestUpdatePackageDownloadCurrentlyUnauthenticated --- PASS: TestAuthVerifyAlwaysReturns401WithoutMiddleware --- PASS: TestAuthVerifyWorksWithMiddleware --- PASS: TestAgentSelfUnregisterHasNoRateLimit --- PASS: TestAgentSelfUnregisterShouldHaveRateLimit --- PASS: TestRetryCommandEndpointProducesUnsignedCommand --- PASS: TestRetryCommandEndpointMustProduceSignedCommand --- SKIP: TestRetryCommandHTTPHandlerProducesUnsignedCommand_Integration ok handlers --- PASS: TestRetryCommandIsUnsigned --- PASS: TestRetryCommandMustBeSigned --- PASS: TestSignedCommandNotBoundToAgent --- PASS: TestOldFormatCommandHasNoExpiry ok services --- PASS: TestGetPendingCommandsHasNoTTLFilter --- PASS: TestGetPendingCommandsMustHaveTTLFilter --- PASS: TestRetryCommandQueryDoesNotCopySignature ok queries ``` ### Agent Tests (14/14 passing, no regressions) ``` All 14 crypto tests pass. No changes to agent code in A-3. ``` ### Build ``` go build ./... — BUILD OK ```