--- phase: 04-security plan: 02 subsystem: auth tags: [spring-security, jwt-filter, security-filter-chain, bootstrap-token, refresh-token, stateless-auth] # Dependency graph requires: - phase: 04-security provides: "JwtService, Ed25519SigningService, BootstrapTokenValidator, SecurityProperties beans" - phase: 03-agent-registry provides: "AgentRegistryService, AgentRegistrationController, SseConnectionManager, SSE endpoints" provides: - "SecurityFilterChain with stateless JWT authentication and public/protected endpoint split" - "JwtAuthenticationFilter extracting JWT from Authorization header or query param" - "Registration endpoint with bootstrap token validation, JWT + refresh token + public key issuance" - "Refresh endpoint issuing new access JWT from valid refresh token" - "TestSecurityHelper for JWT-authenticated integration tests" affects: [04-03] # Tech tracking tech-stack: added: [] patterns: [OncePerRequestFilter for JWT extraction, SecurityFilterChain with permitAll/authenticated split, error path permit for proper Spring Boot error forwarding] key-files: created: - cameleer-server-app/src/main/java/com/cameleer/server/app/security/JwtAuthenticationFilter.java - cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java - cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/SecurityFilterIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/BootstrapTokenIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/RegistrationSecurityIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/JwtRefreshIT.java modified: - cameleer-server-app/src/main/java/com/cameleer/server/app/controller/AgentRegistrationController.java - cameleer-server-app/src/main/java/com/cameleer/server/app/config/WebConfig.java - cameleer-server-app/src/test/java/com/cameleer/server/app/security/TestSecurityConfig.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentRegistrationControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ExecutionControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/MetricsControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/BackpressureIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DetailControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/SearchControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentCommandControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/AgentSseControllerIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/storage/IngestionSchemaIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/interceptor/ProtocolVersionIT.java - cameleer-server-app/src/test/java/com/cameleer/server/app/controller/ForwardCompatIT.java key-decisions: - "Added /error to SecurityConfig permitAll to allow Spring Boot error page forwarding through security" - "Excluded register and refresh paths from ProtocolVersionInterceptor (auth endpoints, not data endpoints)" - "SSE authentication via ?token= query parameter handled transparently by JwtAuthenticationFilter" - "Refresh endpoint in permitAll (uses refresh token for self-authentication, not JWT access token)" patterns-established: - "TestSecurityHelper @Component for registering test agents and creating auth headers in ITs" - "Bootstrap token in Authorization: Bearer header for registration (same header format as JWT)" - "SecurityFilterChain permits /error for proper error page rendering in authenticated context" requirements-completed: [SECU-01, SECU-02, SECU-05] # Metrics duration: 26min completed: 2026-03-11 --- # Phase 4 Plan 02: Security Filter Chain and Endpoint Protection Summary **Spring Security filter chain with JWT authentication on all protected endpoints, bootstrap token validation on registration, refresh token flow, and 91 passing tests including 18 new security ITs** ## Performance - **Duration:** 26 min - **Started:** 2026-03-11T19:11:48Z - **Completed:** 2026-03-11T19:38:07Z - **Tasks:** 2 - **Files modified:** 25 ## Accomplishments - SecurityFilterChain enforces JWT authentication on all endpoints except health, register, refresh, and docs - JwtAuthenticationFilter extracts JWT from Authorization header or ?token= query param (SSE support) - Registration endpoint requires bootstrap token, returns accessToken + refreshToken + serverPublicKey (Ed25519) - Refresh endpoint issues new access JWT from valid refresh token with agent ID verification - All 15 existing ITs adapted to use JWT authentication via TestSecurityHelper - 4 new security ITs (SecurityFilterIT, BootstrapTokenIT, RegistrationSecurityIT, JwtRefreshIT) with 18 tests ## Task Commits Each task was committed atomically: 1. **Task 1: SecurityFilterChain + JwtAuthenticationFilter + registration/refresh integration** - `387e2e6` (feat) 2. **Task 2: Security integration tests + existing test adaptation** - `539b85f` (test) ## Files Created/Modified - `...security/JwtAuthenticationFilter.java` - OncePerRequestFilter extracting JWT from header or query param - `...security/SecurityConfig.java` - SecurityFilterChain with public/protected endpoint split - `...controller/AgentRegistrationController.java` - Updated with bootstrap token validation, JWT issuance, refresh endpoint - `...config/WebConfig.java` - Excluded register/refresh from ProtocolVersionInterceptor - `...TestSecurityHelper.java` - Test utility for JWT-authenticated requests - `...security/SecurityFilterIT.java` - 6 tests for protected/public endpoint access control - `...security/BootstrapTokenIT.java` - 4 tests for bootstrap token validation on registration - `...security/RegistrationSecurityIT.java` - 3 tests for registration security response - `...security/JwtRefreshIT.java` - 5 tests for refresh token flow - 15 existing IT files updated with JWT authentication headers ## Decisions Made - **Added /error to permitAll:** Spring Boot forwards exceptions to /error endpoint; without permitting it, controllers returning 404 via ResponseStatusException would result in 403 to the client. - **Excluded register/refresh from ProtocolVersionInterceptor:** These are auth/token-renewal endpoints that agents call without full protocol handshake context. Protocol version enforcement is for data/management endpoints. - **Refresh endpoint uses permitAll + self-authentication:** The refresh endpoint validates the refresh token directly rather than requiring a separate JWT access token, simplifying the agent token renewal flow. - **SSE query param authentication transparent:** JwtAuthenticationFilter checks both Authorization header and ?token= query param, so no SSE controller changes needed. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 3 - Blocking] Added /error to SecurityConfig permitAll** - **Found during:** Task 2 (test execution) - **Issue:** Controllers using ResponseStatusException(NOT_FOUND) forward to /error endpoint, which was blocked by Spring Security, resulting in 403 instead of 404 - **Fix:** Added "/error" to the permitAll requestMatchers list - **Files modified:** SecurityConfig.java - **Verification:** All 91 tests pass, 404 responses correctly returned **2. [Rule 3 - Blocking] Excluded register/refresh from ProtocolVersionInterceptor** - **Found during:** Task 2 (JwtRefreshIT tests returning 400) - **Issue:** Refresh endpoint matched /api/v1/agents/** interceptor pattern, rejecting requests without X-Cameleer-Protocol-Version header with 400 - **Fix:** Added /api/v1/agents/register and /api/v1/agents/*/refresh to interceptor excludePathPatterns - **Files modified:** WebConfig.java - **Verification:** All JwtRefreshIT and BootstrapTokenIT tests pass --- **Total deviations:** 2 auto-fixed (2 blocking) **Impact on plan:** Both fixes necessary for correct Spring Security + Spring MVC interceptor integration. No scope creep. ## Issues Encountered None beyond the auto-fixed blocking issues above. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Full Spring Security filter chain active with JWT auth on all protected endpoints - TestSecurityHelper available for all future integration tests - Ready for Plan 03: Ed25519 signing of SSE command payloads - Registration flow complete: bootstrap token -> register -> receive JWT + public key -> use JWT for all API calls -> refresh when expired ## Self-Check: PASSED - All 7 created files verified present on disk - Both commits (387e2e6, 539b85f) verified in git log - Full `mvn clean verify` passed: 91 tests, 0 failures --- *Phase: 04-security* *Completed: 2026-03-11*