Closed FusionHS closed 1 month ago
I'm not sure I have a POST
request using application/x-www-form-urlencoded
. Do you have a reproducing sample somewhere?
It should just be a simple html form post. A quick standalone example in HTML rather than like the curl example above would be the following:
<!DOCTYPE html>
<html data-theme="dark" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Please Log In</title>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="initial-scale=1.0" name="viewport"/>
<script crossorigin="anonymous" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0"
src="https://unpkg.com/htmx.org@1.9.11"></script>
<script>
window.addEventListener("DOMContentLoaded", (event) => {
document.body.addEventListener('htmx:configRequest', function (evt) {
evt.detail.headers ['X-XSRF-TOKEN'] = getCookie('XSRF-TOKEN');
});
});
function getCookie(name) {
let nameEQ = name + "=";
let ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
</script>
</head>
<body>
<div class="btn btn-primary" hx-post="/logout">logout</div>
<form hx-post="/app/foo" hx-swap="outerHTML">
<p>hello, <span></span></p>
<label for="name">Foo Name:</label><br>
<input id="name" name="name" type="text"><br>
<label for="type">Foo Type:</label><br>
<input id="type" name="type" type="text"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
For extra context on the reason for this structure:
From the BFF tutorial, it's putting the resource server behind the BFF gateway under the security matchers that would then also require CSRF, as opposed to the BFF's own resource server. From this example, the specific focus is on the /api/**
endpoints
spring:
cloud:
gateway:
routes:
- id: bff
uri: ${scheme}://${hostname}:${resource-server-port}
predicates:
- Path=/api/**
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
- TokenRelay=
- SaveSession
- StripPrefix=1
com:
c4-soft:
springaddons:
oidc:
# Trusted OpenID Providers configuration (with authorities mapping)
ops:
- iss: ${issuer}
authorities:
- path: ${authorities-json-path}
aud: ${audience}
# SecurityFilterChain with oauth2Login() (sessions and CSRF protection enabled)
client:
client-uri: ${reverse-proxy-uri}${bff-prefix}
security-matchers:
- /api/**
- /login/**
- /oauth2/**
- /logout
permit-all:
- /api/**
- /login/**
- /oauth2/**
csrf: cookie-accessible-from-js
oauth2-redirections:
rp-initiated-logout: ACCEPTED
# SecurityFilterChain with oauth2ResourceServer() (sessions and CSRF protection disabled)
resourceserver:
permit-all:
- /login-options
- /error
- /actuator/health/readiness
- /actuator/health/liveness
Describe the bug Downstream services times out reading request body of POST application/x-www-form-urlencoded request in BFF pattern when
csrf: cookie-accessible-from-js
It works just fine if csrf is disabled, with exactly the same request going through the BFF.
I've just started using this project with version
7.6.11
, so it might be the case that I'm miss configuring something here or using it in a way that is not best practise. Or maybe I just missed a part in the documentationCode sample
Example request
Expected behavior Downstream service should be able to read the incoming request body
Additional context The only difference I can see in implementation and execution when the csrf is enabled and required, is that
ServerCsrfTokenRequestHandler.resolveCsrfTokenValue()
is not invoked through theSpaCsrfTokenRequestHandler()
described inReactiveConfigurationSupport
when csrf is set toCOOKIE_ACCESSIBLE_FROM_JS
I think the core problem might be that this method of resolving the csrf first attempts to read it from the form data (even though the token is available in the header) in
resolveCsrfTokenValue()
whenexchange.getFormData()
is invoked. As I understand, this body can only be read once, but it is cached. I thought this would mean that it would safely propagate to the downstream service, but this does not seem to be the case. As when this line is executed, the downstream service times out when attempting to read the request body.