Skip to content

COG-1159: W1 · Add GET /api/csrf token-bootstrap endpoint (204) - #334

Open
patrickbradley-cog wants to merge 1 commit into
masterfrom
devin/cog-1159-w1-add-get-apicsrf-token-bootstrap-endpoint-204
Open

patrickbradley-cog wants to merge 1 commit into
masterfrom
devin/cog-1159-w1-add-get-apicsrf-token-bootstrap-endpoint-204

Conversation

@patrickbradley-cog

@patrickbradley-cog patrickbradley-cog commented Sep 30, 2026 •

Copy link
Copy Markdown

Linear: COG-1159 · Project P-COG-46 (Spring Boot 1.5.4 → 4.1 Migration · UserFront) · Wave W1

Summary

Adds a safe, unauthenticated GET /api/csrf that returns 204 No Content, so AdminPortal can obtain the XSRF-TOKEN cookie before its first POST /index once W2 turns CSRF on with CookieCsrfTokenRepository. Forward-compatible on Boot 1.5.4; no Boot/Java bump.

// resource/CsrfResource.java
@GetMapping("/api/csrf")
ResponseEntity<Void> csrf(CsrfToken token) {
    if (token != null) token.getToken();   // null while CSRF is disabled
    return ResponseEntity.noContent().build();
}
// SecurityConfig.PUBLIC_MATCHERS: + "/api/csrf"   (single added entry)

Note on token.getToken(): the ticket says injecting CsrfToken is enough to materialise the token. That holds on Spring Security 4.2 (CsrfFilter saves the token eagerly). On Spring Security 6 (the Boot 4 target) tokens are deferred, and the cookie is only written once the token value is read. So the handler reads it explicitly, and the endpoint keeps working after the pivot without another change.

CsrfResourceTest (@WebMvcTest + the real SecurityConfig) asserts only the 204 for an anonymous client. Its Javadoc explains that the cookie shows up only once CSRF is enabled, so the test stays green through W2.

Merge order: merge this after the W0 smoke suite (#106, #108, #109). The only overlap with COG-1152 (SecurityConfig hygiene) is the one-line PUBLIC_MATCHERS addition. Whichever of the two lands second should rebase on top of the first rather than revert it.

Verification

JDK: Temurin 8 (1.8), MySQL 5.7 in docker on :3306 (onlinebanking, credentials from application.properties).

The first mvn verify run failed in the existing contextLoads with an SSLHandshakeException. This is an environment issue: modern JDK 8 disables TLSv1/1.1, and connector 5.1.42 tries them against MySQL 5.7. It is not caused by this change. I added useSSL=false through an env override only, with no repo change:

$ SPRING_DATASOURCE_URL='jdbc:mysql://localhost:3306/onlinebanking?useSSL=false' mvn -B -f UserFront/pom.xml verify
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 - in com.userFront.resource.CsrfResourceTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 - in com.userFront.UserFrontApplicationTests
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

Running the app jar against MySQL, anonymous client:

Mapped "{[/api/csrf],methods=[GET]}" onto ... CsrfResource.csrf(CsrfToken)
$ curl -si http://localhost:8080/api/csrf
HTTP/1.1 204            (no Set-Cookie: expected while CSRF is disabled)
$ curl -si http://localhost:8080/api/appointment/all
HTTP/1.1 302            (protected endpoint still redirects to login)

W2 preview. This was a throwaway local build with .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) and was not committed. It shows the bootstrap flow this endpoint enables:

GET  /api/csrf                                  -> 204, Set-Cookie: XSRF-TOKEN=e4aa2b60-...; Path=/
POST /index (no token)                          -> 403
POST /index (XSRF-TOKEN cookie + X-XSRF-TOKEN)  -> 302 Location: /index?error   (reached authentication; bad creds)

The repo has no CI workflows. AdminPortal is untouched: making the client call /api/csrf first is outside this ticket's scope.

Link to Devin session: https://app.devin.ai/sessions/dcc3c806c39c49b68b3e23cd9aababc7
Open in Devin Desktop: https://app.devin.ai/desktop/session/dcc3c806c39c49b68b3e23cd9aababc7?variant=devin
Requested by: @patrickbradley-cog


Devin Review

Status Commit
⚪ Not started —

Run Devin Review

Devin Review (Staging)


Devin Review

Co-Authored-By: patrick.bradley <patrick.bradley@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Devin Review

Comment on lines +35 to +36
mockMvc.perform(get("/api/csrf"))
.andExpect(status().isNoContent());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Bootstrap cookie remains untested

The status-only test passes without issuing a token. When CSRF protection is enabled, a separate test must verify that an anonymous request receives the XSRF-TOKEN cookie.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional, per COG-1159. CSRF is still disabled on Boot 1.5.4, so the ticket scopes this test to the 204 only. A cookie assertion would fail today. Asserting that XSRF-TOKEN is set belongs with W2, where SecurityConfig switches to CookieCsrfTokenRepository. I checked that flow manually on a throwaway local build: GET /api/csrf returned 204 with Set-Cookie: XSRF-TOKEN=…. The details are in the PR description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant