feat(sts): accept AssumeRoleWithWebIdentity params from form-encoded POST bodies#112
feat(sts): accept AssumeRoleWithWebIdentity params from form-encoded POST bodies#112alukach wants to merge 4 commits into
Conversation
…POST bodies AWS STS accepts parameters in the query string or an application/x-www-form-urlencoded POST body; SDKs send the latter, so the query-only parse made /.sts unreachable from unmodified AWS SDK STS clients and the SDKs' built-in web-identity credential providers. - core: RequestInfo gains an optional form_body field runtimes populate for form-encoded POSTs, plus an is_form_urlencoded_post() predicate so every runtime shares one definition of when to collect it. Form POSTs are not part of the S3 protocol, so collecting them never steals a payload the data path needs. - sts: try_handle_sts checks the query first, then form_body; params are read from exactly one source, never merged (matching AWS). - cf-workers: RequestParts::absorb_form_body() collects such bodies before dispatch and hands back an empty JsBody. - examples: all three runtimes (cf-workers, server, lambda) wire the form body up before building RequestInfo. Refs source-cooperative/data.source.coop#184
|
Claude finished @alukach's task in 4m 8s —— View job Review complete
✅ No blocking issues — safe to merge. Went through the full diff ( Things I specifically checked and found sound:
Simplify (ponytail): nothing worth cutting — the per-runtime "collect + rebuild body" blocks look duplicated but can't be meaningfully unified (each runtime has a different body type: Note: I wasn't able to execute 💰 Estimated review cost: $1.10 · 4m08s · 27 turns |
|
📖 Docs preview deployed to https://multistore-docs-pr-112.development-seed.workers.dev
|
|
🚀 Latest commit deployed to https://multistore-proxy-pr-112.development-seed.workers.dev
|
absorb_form_body materialized any form-encoded POST body into WASM memory with no size cap, running before the gateway's max_request_body_size guard. Require a parseable Content-Length at or under 64 KiB before collecting — SDK STS clients always send one, and a chunked body with no declared length could be arbitrarily large. Also build RequestInfo once per request in the server and lambda examples instead of twice (review nit). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed the review in c094faa:
Verified: |
Content-Type is client-controlled, so a POST carrying application/x-www-form-urlencoded is not necessarily STS — a mislabeled S3 CompleteMultipartUpload/DeleteObjects would previously have its XML body drained into form_body and destroyed, failing the operation with no way to recover the payload. All three runtimes now hand the same bytes downstream: cf-workers rebuilds the stream via JsBody::from_bytes, axum rebuilds Body::from(bytes), and lambda copies without clearing the already-buffered body. The collection gate moves into core as RequestInfo::should_collect_form_body — form-urlencoded POST with a declared Content-Length within the shared FORM_BODY_MAX_BYTES (64 KiB). Oversized or length-less bodies are passed through untouched instead of rejected (they cannot be legitimate SDK STS requests), which also gives lambda the size bound it was missing and replaces the three independent copies of the cap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed the second round in d16727b:
Verified: |
What I'm changing
AWS STS accepts
AssumeRoleWithWebIdentityparameters in the query string or as anapplication/x-www-form-urlencodedPOSTbody — and AWS SDKs exclusively send the latter. The STS handler parses the query string only, so an unmodified SDK STS client pointed at the proxy (viaAWS_ENDPOINT_URL_STS), the SDKs' built-in web-identity credential providers (AWS_WEB_IDENTITY_TOKEN_FILE/AWS_ROLE_ARN), andaws-actions/configure-aws-credentialsall fall through the router unhandled. This closes that gap so the STS endpoint is a drop-inAssumeRoleWithWebIdentitytarget for standard AWS tooling, with zero custom exchange code on the client.Downstream motivation: source-cooperative/data.source.coop#184 (its
/.stsis this handler).How I did it
multistore::route_handler—RequestInfogains an optionalform_bodyfield plus awith_form_bodybuilder, and anis_form_urlencoded_post()predicate so every runtime shares one definition of when to collect a body. Form-encodedPOSTs are not part of the S3 protocol, so collecting them never steals a payload the forwarding path needs.Router::dispatchthreads the field through itsRequestInforeconstruction.multistore-sts—try_handle_ststakesform_bodyalongsidequeryand triestry_parse_sts_requeston the query first, then the body. Parameters are read from exactly one source, never merged across the two (matching AWS). The parser itself is untouched — it already parses a form-urlencoded string.multistore-cf-workers—RequestPartsgainsform_bodyand anabsorb_form_body(JsBody) -> Result<JsBody>helper: a no-op passthrough for every other request shape, so integrators call it unconditionally betweenfrom_web_sysand dispatch.as_request_info()wires the field through./.sts, so all three now collect form bodies before buildingRequestInfo(the axum server bounds collection at 64 KiB; lambda bodies are already buffered).auth/proxy-auth.mdandreference/operations.mdnow state both parameter sources and the SDK-compatibility rationale.Test plan
cargo test— new route-handler tests: a form-bodyPOSTwith no query string is intercepted; a non-STS form body falls through; newis_form_urlencoded_posttests cover charset/casing, wrong method, wrong content type (S3 batch deletePOST+ XML), and no content type.cargo check --all-targetscargo check -p multistore-cf-workers --target wasm32-unknown-unknowncargo fmt/cargo clippy --fix(pre-commit hook)🤖 Generated with Claude Code
https://claude.ai/code/session_01Hk6VMBuKNcTK3boS8HrdQs