Goal
Let standard AWS tooling exchange OIDC tokens for proxy credentials directly against /.sts, with no custom exchange code:
- AWS SDK built-in web-identity providers: set
AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, and AWS_ENDPOINT_URL_STS, and boto3 / aws-cli / SDK-JS auto-exchange and auto-refresh credentials.
aws-actions/configure-aws-credentials in GitHub Actions: mint the runner's OIDC token and exchange it at the proxy in two lines of YAML (the audience: input maps onto AUTH_AUDIENCE), replacing hand-rolled curl/exchange plumbing like the one in tests/test_writes.py.
/.sts already speaks AssumeRoleWithWebIdentity semantics and returns AWS-shaped XML that SDKs can parse, so this is close to working already.
Blockers
Exactly two mismatches stand in the way:
-
Transport (fix in multistore) — AWS SDK STS clients send AssumeRoleWithWebIdentity as a form-encoded POST body; the multistore-sts handler parses the query string only (try_parse_sts_request reads req.query). Real AWS STS accepts both, so accepting body params is protocol-faithful.
-
Role naming (fix in this repo) — the proxy serves a single role with the literal id _default (src/sts.rs), but AWS clients validate RoleArn client-side before sending (botocore enforces ARN shape and a 20-char minimum), so RoleArn=_default never reaches the server. Accepting an ARN-shaped alias (e.g. arn:aws:iam::000000000000:role/_default) unblocks SDK clients without changing the trust model — it's the same role under a longer name. This also dovetails with the existing TODO in src/sts.rs about per-product roles, which would want ARN-shaped ids anyway.
Acceptance
With both changes deployed (and the multistore dependency bumped here):
export AWS_WEB_IDENTITY_TOKEN_FILE=/path/to/oidc-token
export AWS_ROLE_ARN=arn:aws:iam::000000000000:role/_default
export AWS_ENDPOINT_URL_STS=https://data.source.coop/.sts
aws s3 ls s3://cholmes/ --endpoint-url https://data.source.coop
acquires and refreshes proxy credentials with no custom code, and aws-actions/configure-aws-credentials pointed at the proxy works in CI.
🤖 Generated with Claude Code
Compatibility bar
/.sts must be a drop-in replacement for AWS STS for AssumeRoleWithWebIdentity: an unmodified AWS SDK or the configure-aws-credentials action pointed at it via AWS_ENDPOINT_URL_STS must work without proxy-specific code paths. That means accepting both query-string and form-encoded body parameters, honoring Action/Version/DurationSeconds as STS does, and returning STS-shaped success and error XML.
Goal
Let standard AWS tooling exchange OIDC tokens for proxy credentials directly against
/.sts, with no custom exchange code:AWS_WEB_IDENTITY_TOKEN_FILE,AWS_ROLE_ARN, andAWS_ENDPOINT_URL_STS, and boto3 / aws-cli / SDK-JS auto-exchange and auto-refresh credentials.aws-actions/configure-aws-credentialsin GitHub Actions: mint the runner's OIDC token and exchange it at the proxy in two lines of YAML (theaudience:input maps ontoAUTH_AUDIENCE), replacing hand-rolled curl/exchange plumbing like the one intests/test_writes.py./.stsalready speaksAssumeRoleWithWebIdentitysemantics and returns AWS-shaped XML that SDKs can parse, so this is close to working already.Blockers
Exactly two mismatches stand in the way:
Transport (fix in multistore) — AWS SDK STS clients send
AssumeRoleWithWebIdentityas a form-encoded POST body; the multistore-sts handler parses the query string only (try_parse_sts_requestreadsreq.query). Real AWS STS accepts both, so accepting body params is protocol-faithful.Role naming (fix in this repo) — the proxy serves a single role with the literal id
_default(src/sts.rs), but AWS clients validateRoleArnclient-side before sending (botocore enforces ARN shape and a 20-char minimum), soRoleArn=_defaultnever reaches the server. Accepting an ARN-shaped alias (e.g.arn:aws:iam::000000000000:role/_default) unblocks SDK clients without changing the trust model — it's the same role under a longer name. This also dovetails with the existing TODO insrc/sts.rsabout per-product roles, which would want ARN-shaped ids anyway.Acceptance
With both changes deployed (and the multistore dependency bumped here):
acquires and refreshes proxy credentials with no custom code, and
aws-actions/configure-aws-credentialspointed at the proxy works in CI.🤖 Generated with Claude Code
Compatibility bar
/.stsmust be a drop-in replacement for AWS STS forAssumeRoleWithWebIdentity: an unmodified AWS SDK or theconfigure-aws-credentialsaction pointed at it viaAWS_ENDPOINT_URL_STSmust work without proxy-specific code paths. That means accepting both query-string and form-encoded body parameters, honoringAction/Version/DurationSecondsas STS does, and returning STS-shaped success and error XML.