Skip to content

refactor(federation): dissolve backend-federation into oidc-provider; hoist credential type to core#67

Merged
alukach merged 4 commits into
mainfrom
refactor/dedupe-federation-credentials
Jun 4, 2026
Merged

refactor(federation): dissolve backend-federation into oidc-provider; hoist credential type to core#67
alukach merged 4 commits into
mainfrom
refactor/dedupe-federation-credentials

Conversation

@alukach
Copy link
Copy Markdown
Member

@alukach alukach commented Jun 4, 2026

What & why

Outbound federation spanned two crates with a backwards-feeling dependency edge: multistore-oidc-provider (the IdP/orchestrator) imported from multistore-backend-federation (the AWS STS mechanism). That crate bundled two things of different altitude — a foundational credential value type and an AWS-specific exchange mechanism.

The only thing its separateness bought was a future "bring-your-own-token" consumer that spends a token the proxy didn't mint. That tenant will never arrive: the proxy is the only consumer of the credentials it obtains, multistore never spends a token it didn't mint at a cloud STS nor brokers cloud access to third parties, and its minted identity is reused only for the proxy authenticating itself (backend STS, the Source API, inbound STS). So this PR collapses the federation concern into one crate and puts the shared vocabulary where it belongs.

Changes

1. Hoist the credential type into multistore core

  • FederatedCredentials (fields + apply_to(&mut BucketConfig) + secret-redacting Debug) moves to crates/core/src/types.rs, beside its inbound sibling TemporaryCredentials (kept distinct: that one carries the proxy's authz model — allowed_scopes/assumed_role_id/source_identity — this one only what an object-store client needs to sign).

2. Dissolve backend-federation into oidc-provider

  • The AWS AssumeRoleWithWebIdentity mechanism + FederationError move into oidc-provider/src/exchange/aws.rs (no more cross-crate delegation); the StsError mapping is now intra-crate.
  • oidc-provider re-exports FederatedCredentials, so it stays the single front door — consumers (e.g. source.coop) import the type from oidc-provider and never name a federation crate or core's types module.
  • crates/backend-federation/ is deleted; workspace Cargo.toml, README, release-please config, the wrangler example, and the smoke-test docstring are updated.

Net: oidc-provider now depends only on multistore core. −208 lines.

Note: removed two dead helpers

The moved mechanism's body() (a second encoding path — the exchange uses form_pairs()) and endpoint() (a regional-URL helper never wired in) were dead in the internal-only context, so I removed them (and the now-unused url dep) rather than carry dead code + a clippy warning; their duration/policy coverage was rewritten against form_pairs(). Easy to restore if regional-endpoint support is anticipated.

Verification

  • cargo build — workspace compiles with the crate gone (Cargo.lock regenerated).
  • Tests — core 74 (incl. the 2 moved), oidc-provider 29 default / 37 with --features azure,gcp (incl. 6 AWS-mechanism tests).
  • cargo clippy --all-features — no new warnings (3 reported are pre-existing in auth/tests.rs, middleware.rs, jwks.rs).
  • cf-workers wasm32-unknown-unknown check — passes.

🤖 Generated with Claude Code

alukach and others added 2 commits June 4, 2026 13:14
…, drop the duplicate

oidc-provider and backend-federation each modeled temporary backend
credentials. Collapse to one source of truth in the mechanism crate:

- Delete oidc-provider's CloudCredentials (+ its redacting Debug and the
  From<FederatedCredentials> bridge); re-export and consume
  multistore_backend_federation::FederatedCredentials throughout the
  AWS/Azure/GCP exchanges, CredentialExchange, CredentialCache, and
  get_credentials.
- backend_auth.rs injects via FederatedCredentials::apply_to instead of
  re-inserting access_key_id/secret_access_key/token inline. This also
  clears skip_signature, fixing a latent bug where an anonymous bucket
  (source.coop's registry.rs starting state) stayed unsigned after
  federation.

OidcProviderError::StsError stays as the boundary translation of
FederationError::Sts (signing/key/HTTP failures + status mapping need
their own enum) — a bridge, not duplicated mechanism.

backend-federation owns the outbound exchange, the credential value type,
and BucketConfig injection; oidc-provider owns identity (JWT mint, JWKS,
discovery) + orchestration and delegates the rest. Single path for
source.coop's OIDC-IdP backend auth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dentials to core

Outbound federation lived in two crates with a backwards-feeling edge
(oidc-provider -> backend-federation). backend-federation bundled two things of
different altitude: the credential value type (foundational) and the AWS STS
exchange mechanism. With the proxy as the only consumer of the creds it obtains
-- multistore never spends a token it didn't mint nor brokers cloud access to
third parties -- the separate "spender" crate had no future tenant.

- Move FederatedCredentials (+ apply_to + redacting Debug) into multistore core
  (crates/core/src/types.rs), beside its inbound sibling TemporaryCredentials.
- Absorb the AWS AssumeRoleWithWebIdentity mechanism + FederationError into
  oidc-provider's exchange/aws.rs (no more delegation); the StsError mapping is
  now intra-crate. oidc-provider re-exports FederatedCredentials so it stays the
  single front door for consumers (source.coop never names a federation crate).
- Drop the unused body()/endpoint() helpers (body duplicated the form_pairs
  path; endpoint was never wired in) and the now-unused url dep.
- Delete crates/backend-federation; update workspace Cargo.toml, README,
  release-please config, the wrangler example, and the smoke-test docstring.

oidc-provider now depends only on multistore core. Tests: core 74, oidc-provider
29 default / 37 with azure,gcp; no new clippy warnings; cf-workers wasm passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 4, 2026

🚀 Latest commit deployed to https://multistore-proxy-pr-67.development-seed.workers.dev

  • Date: 2026-06-04T21:01:15Z
  • Commit: d87e39e

…ange in oidc-provider

The crate-layout page predates `backend-federation` and already describes
`oidc-provider` owning the AWS exchange and depending only on core, so this
refactor needs no removals — just two clarifications matching its outcome:

- Name `FederatedCredentials` among core's type definitions (it now lives in
  core, next to `TemporaryCredentials`).
- Note that `oidc-provider` owns the `AssumeRoleWithWebIdentity` request/parse
  mechanism itself, not only the `AwsBackendAuth` middleware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 4, 2026

📖 Docs preview deployed to https://multistore-docs-pr-67.development-seed.workers.dev

  • Date: 2026-06-04T21:01:15Z
  • Commit: d87e39e

Names the type by what it is for (signing backend object-store requests),
matching core's backend_* vocabulary (backend_type / backend_options /
backend_prefix) and the apply_to(&mut BucketConfig) -> backend_options flow.

"Federated" described how the creds are obtained, but that does not distinguish
them from the sibling TemporaryCredentials, which are also a product of
AssumeRoleWithWebIdentity (inbound, minted for callers); the axis that actually
differs is purpose -- backend-facing vs caller-facing -- which this name names.

Pure rename across multistore core + oidc-provider (which re-exports it) and the
crate-layout doc. Tests unchanged: core 74, oidc-provider 29 / 37 (azure,gcp).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alukach alukach merged commit 2924c27 into main Jun 4, 2026
14 of 15 checks passed
@alukach alukach deleted the refactor/dedupe-federation-credentials branch June 4, 2026 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant