diff --git a/backend/src/main/java/org/raddatz/familienarchiv/auth/JdbcSessionRevocationAdapter.java b/backend/src/main/java/org/raddatz/familienarchiv/auth/JdbcSessionRevocationAdapter.java index ba24a23a..e3c39a58 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/auth/JdbcSessionRevocationAdapter.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/auth/JdbcSessionRevocationAdapter.java @@ -1,12 +1,8 @@ package org.raddatz.familienarchiv.auth; import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.session.jdbc.JdbcIndexedSessionRepository; -import org.springframework.stereotype.Service; -@Service -@ConditionalOnBean(JdbcIndexedSessionRepository.class) @RequiredArgsConstructor class JdbcSessionRevocationAdapter implements SessionRevocationPort { diff --git a/backend/src/main/java/org/raddatz/familienarchiv/auth/NoOpSessionRevocationAdapter.java b/backend/src/main/java/org/raddatz/familienarchiv/auth/NoOpSessionRevocationAdapter.java index bb3d3ede..cdb5ec63 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/auth/NoOpSessionRevocationAdapter.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/auth/NoOpSessionRevocationAdapter.java @@ -1,10 +1,5 @@ package org.raddatz.familienarchiv.auth; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.stereotype.Service; - -@Service -@ConditionalOnMissingBean(SessionRevocationPort.class) class NoOpSessionRevocationAdapter implements SessionRevocationPort { @Override diff --git a/backend/src/main/java/org/raddatz/familienarchiv/auth/SessionRevocationConfig.java b/backend/src/main/java/org/raddatz/familienarchiv/auth/SessionRevocationConfig.java new file mode 100644 index 00000000..81dc96ea --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/auth/SessionRevocationConfig.java @@ -0,0 +1,19 @@ +package org.raddatz.familienarchiv.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.jdbc.JdbcIndexedSessionRepository; + +@Configuration +class SessionRevocationConfig { + + @Bean + SessionRevocationPort sessionRevocationPort( + @Autowired(required = false) JdbcIndexedSessionRepository sessionRepository) { + if (sessionRepository != null) { + return new JdbcSessionRevocationAdapter(sessionRepository); + } + return new NoOpSessionRevocationAdapter(); + } +} diff --git a/backend/src/test/java/org/raddatz/familienarchiv/auth/AuthSessionIntegrationTest.java b/backend/src/test/java/org/raddatz/familienarchiv/auth/AuthSessionIntegrationTest.java index 1a8786a4..99a4dcb2 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/auth/AuthSessionIntegrationTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/auth/AuthSessionIntegrationTest.java @@ -119,6 +119,21 @@ class AuthSessionIntegrationTest { assertThat(me.getStatusCode().value()).isEqualTo(401); } + // ─── Task: CSRF rejection at integration layer ──────────────────────────── + + @Test + void post_without_csrf_token_returns_403_CSRF_TOKEN_MISSING() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + // Deliberately omit XSRF-TOKEN cookie and X-XSRF-TOKEN header + ResponseEntity response = http.postForEntity( + baseUrl + "/api/auth/logout", + new HttpEntity<>("{}", headers), String.class); + + assertThat(response.getStatusCode().value()).isEqualTo(403); + assertThat(response.getBody()).contains("CSRF_TOKEN_MISSING"); + } + // ─── helpers ───────────────────────────────────────────────────────────── /**