feat(sts): answer GetCallerIdentity with multistore 0.8.0 - #248
Merged
Merged
Conversation
multistore 0.8.0 answers GetCallerIdentity, which aws-actions/configure-aws-credentials calls after the exchange to check the credentials it exports, so the step source.coop hands out now works. The Role sets the two fields 0.8.0 adds or makes fail closed: subject_conditions ["*"], since an empty list now accepts no subject, and allow_missing_exp_from empty, so every issuer must set exp. mint_temporary_credentials now returns a Result and BucketConfig::backend_type is an enum, parsed from the provider's backend string. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Claude finished @alukach's task in 14s —— View job ✅ No blocking issues — safe to merge. I read the diff for
Simplify (ponytail)
💰 Estimated review cost: $0.11 · 0m13s · 5 turns |
|
🚀 Latest commit deployed to https://source-data-proxy-pr-248.source-coop.workers.dev
|
alukach
added a commit
to source-cooperative/source.coop
that referenced
this pull request
Oct 2, 2026
…workflow's example (#616) "Example usage" on a trusted GitHub workflow handed out a fragment of a job: `env:` and `steps:` at column zero, with `permissions: { id-token: write }` left in a comment. It didn't drop into a workflow: at the top level `steps` is invalid, and pasted under `jobs:` the keys turn into jobs named `env` and `steps`, leaving the real job with nothing to run. That's what happened on the first hand-written workflow against staging. It also ignored which subject the trust names, so a trust pinned to an environment got an example whose token would carry the ref instead, and would be refused. Part of #491. ## What The dialog now has a switch between two forms, both naming the proxy once in `AWS_ENDPOINT_URL_S3` and reading it back in the sign-in step's `with:` as `${{ env.AWS_ENDPOINT_URL_S3 }}` (for `audience`, and with `/.sts` appended for `sts-endpoint`): - **Full Workflow** (shown first): `githubWorkflow(proxyOrigin, accountId, subject)` returns a complete workflow to save under `.github/workflows/`. `AWS_ENDPOINT_URL_S3` is set in a workflow-level `env:`, so every step's S3 client reaches the proxy. One job, `data`, has `runs-on`, `permissions` (`id-token: write`, `contents: read`), the `configure-aws-credentials@v6` sign-in step, and a first `aws s3 ls s3://{owner}/` to show the credentials working. - **Step**: `githubWorkflowStep(proxyOrigin, accountId, subject)` returns the sign-in step alone, for a workflow that already exists, with `AWS_ENDPOINT_URL_S3` set in the step's own `env:`. A step's `env` reaches only that step, so two comments above it say what the job around it needs: `id-token: write` (plus the environment, for a trust pinned to one), and `AWS_ENDPOINT_URL_S3` on the job for later steps to reach the proxy. Both forms are built from one shared `signInStep`. Either way: - **A trust pinned to an environment** puts `environment:` on the job (JSON-quoted, since an environment name may hold a space). Without it GitHub puts the ref in the token's subject, and the trust doesn't match. - **A trust pinned to a ref** says on the `on: workflow_dispatch` line which ref to run it from (full workflow only). `ExampleUsage` takes `code` as before, or a map of labelled forms, in which case a `SegmentedControl` above the code chooses between them and the copy button takes whichever is showing. `ServiceAccountDetail` passes both forms for each trust, and the intro now reads "In the repository `{subject}` names, save the full workflow under `.github/workflows/`, or add the step to a job of your own:". ## Stories On this branch's deploy: - `ExampleUsage` › **GithubWorkflow** (opens on Full Workflow): https://source-coop-ui-git-fix-github-workflow-example-radiantearth.vercel.app/?path=/story/features-service-accounts-exampleusage--github-workflow - `ExampleUsage` › **GithubWorkflowStep** (new, opens on Step): https://source-coop-ui-git-fix-github-workflow-example-radiantearth.vercel.app/?path=/story/features-service-accounts-exampleusage--github-workflow-step - `ExampleUsage` › **GithubWorkflowInAnEnvironment** (new): https://source-coop-ui-git-fix-github-workflow-example-radiantearth.vercel.app/?path=/story/features-service-accounts-exampleusage--github-workflow-in-an-environment - `ExampleUsage` › **Mobile**: https://source-coop-ui-git-fix-github-workflow-example-radiantearth.vercel.app/?path=/story/features-service-accounts-exampleusage--mobile   ## Testing - `service-account-usage.test.ts`: the workflow sets `AWS_ENDPOINT_URL_S3` in a top-level `env` and has one job holding `runs-on`, `permissions` with `id-token: write` and the sign-in step, nested where GitHub reads them (asserted on the exact indented lines, since nesting is the bug); `with:` reads `${{ env.AWS_ENDPOINT_URL_S3 }}`; a ref trust adds no `environment`, while an environment trust puts it on the job. The step stands alone with `env` beside `with`, and names the environment in its comment when the trust is pinned to one. 6 pass. - Once, outside the suite: both forms for an environment named `prod west` parsed with `js-yaml`: the workflow's top-level `env`, the job's `environment` and the step's `with`, and the step's own `env` and `with`. - `src/stories.smoke.test.tsx` 195 pass; `npm run type-check` is clean. - Checked on the branch deploy: both forms render, and the switch changes the code shown (screenshots above). - Not run: either form in a real repository. On staging it still needs source-cooperative/data.source.coop#248 deployed, since `configure-aws-credentials` checks the credentials with `GetCallerIdentity`. That `with:` can read a step's own `env` comes from GitHub's documented context availability (`env` is available in `jobs.<job_id>.steps.with`), not from a run. ## Docs and ADRs Checked ADR-014 (source-cooperative/data.source.coop): the step and the `RoleArn` it names are unchanged, so it still holds. docs.source.coop: the automated-access guide (source-cooperative/docs.source.coop#37) has its GitHub Actions section marked "coming soon", and should use this file when it's written. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What I'm changing
Bumps multistore from 0.7.2 to 0.8.0 (developmentseed/multistore#153), which answers
GetCallerIdentity.aws-actions/configure-aws-credentials, the step source.coop's settings page hands out, makes that call after the exchange to check the credentials it exports, so until now the step failed after a successful exchange. The call needs no new routing here: it is answered by the STS handler this Worker already mounts withwith_sts("/.sts", …), which in 0.8.0 serves/.sts/too and verifies SigV4 over the path the client signed, so the existing/.sts/→/.stsrewrite doesn't break the signature.What 0.8.0 asked of this repo:
RoleConfig.subject_conditionsis["*"]. In 0.8.0 an empty list accepts no subject (feat(sts)!: fail closed on empty trust fields, check token type, require exp, log successes developmentseed/multistore#146), which would refuse every person. Any subject is right here: a person's token names the person, and a platform token acts only as an account that trusts its subject (ADR-014).RoleConfig.allow_missing_exp_fromis empty, so every issuer must setexp. Ory always does; API keys never reachverify_token.mint_temporary_credentialsreturns aResult, andBucketConfig::backend_typeis an enum (refactor(core): make BucketConfig::backend_type a typed BackendType enum developmentseed/multistore#155), parsed from the backend stringbackend_optionsalready produces (s3/az/gcs, which itsFromStraccepts).The README's paragraph saying the action fails now says it works.
Decisions to flag
expcheck inplatform::subjectstays. feat(sts): let platform IdP tokens act as accounts that trust them #237 said to drop it with this bump, since multistore now requiresexpfor every issuer this Role trusts. It is redundant but harmless, and removing a security check plus its test is better done as its own reviewed change than inside a dependency bump.GetCallerIdentity'sAccountis multistore's fixed synthetic id, not the service account that Trust GitHub Actions alongside the Source issuer #223's 2026-09-23 comment asked for.ArnandUserIddo carry the Role and the account (the credentials' source identity). The action only needs the call to succeed, so this doesn't block the workflow; makingAccountthe service account is an upstream change in multistore if we want the action'saws-account-idoutput to mean something.Testing
cargo fmt --check,cargo clippy --target wasm32-unknown-unknown -- -D warnings,cargo check --target wasm32-unknown-unknownandcargo testpass locally (the pre-push hook).configure-aws-credentialsrun. CI's integration job runs the former on this PR's preview; the latter is the functional test for Trust GitHub Actions alongside the Source issuer #223 once this deploys.Docs and ADRs
ADR-014 says the proxy answers
GetCallerIdentity"once developmentseed/multistore#126 lands"; this PR is that landing and implements the decision, so the ADR still holds. ADR-004 already listsconfigure-aws-credentialsas a supported client. docs.source.coop: the GitHub Actions section of the automated-access guide (source-cooperative/docs.source.coop#37) is waiting on this.Part of #223: it is done once a workflow in an unrelated repository writes with only its ambient token, against a deployment. Upstream: developmentseed/multistore#126, developmentseed/multistore#146, developmentseed/multistore#153. Epic: source-cooperative/source.coop#491.
🤖 Generated with Claude Code