- "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]
# 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)
-`...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**