---
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:
    - cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java
    - cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityConfig.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java
  modified:
    - cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java
    - cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java
    - cameleer3-server-app/src/test/java/com/cameleer3/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*
