Context
PR #132 adds the /.sts token-exchange endpoint. Role trust policies come from multistore::types::RoleConfig, which currently models trust as:
pub trusted_oidc_issuers: Vec<String>, // N issuers
pub required_audience: Option<String>, // but ONE audience, shared across all of them
pub subject_conditions: Vec<String>, // and ONE set of sub conditions, shared too
A role can trust multiple OIDC issuers, but the aud and sub constraints apply uniformly across all of them — and required_audience only holds a single value.
Problem
We will definitely want multiple OIDC providers to be able to assume the same role, each with its own audience and subject conditions. In AWS IAM this is the norm: a role's trust policy can contain multiple statements, one per federated provider, each with its own aud/sub conditions. For example, a single product role trusted by both:
- source.coop web flow (Ory):
aud = the web app's OAuth client_id, sub = the Ory user id
- GitHub Actions OIDC (
token.actions.githubusercontent.com): aud = a custom audience, sub matching repo:<org>/<repo>:ref:refs/heads/main
With the current shape this is unrepresentable: an audience valid for one issuer is wrong for the other, and subject patterns for Ory user ids vs GitHub repo: subjects can't be scoped to their issuer.
Proposal
Restructure role trust as a list of per-provider entries, e.g.:
pub struct TrustedProvider {
pub issuer: String,
pub audiences: Vec<String>, // also fixes the single-client_id limitation
pub subject_conditions: Vec<String>, // glob patterns, scoped to this issuer
}
pub struct RoleConfig {
// ...
pub trusted_providers: Vec<TrustedProvider>,
}
Token exchange validates the subject token against the entry matching its iss, then enforces that entry's aud/sub conditions.
Dependencies
- The type and its enforcement live upstream in the
multistore / multistore-sts crates (RoleConfig, JWKS validation), so this needs a crate release first, then adoption here (src/sts.rs builds the _default role; AUTH_AUDIENCE would become that role's first trusted-provider audience).
- Related: the role registry is currently a single hardcoded
_default role (see TODO in src/sts.rs); per-role trust config becomes most useful once roles are served from the Source Cooperative API.
Spun out of review feedback on #132 (finding 1: audience restriction).
Context
PR #132 adds the
/.ststoken-exchange endpoint. Role trust policies come frommultistore::types::RoleConfig, which currently models trust as:A role can trust multiple OIDC issuers, but the
audandsubconstraints apply uniformly across all of them — andrequired_audienceonly holds a single value.Problem
We will definitely want multiple OIDC providers to be able to assume the same role, each with its own audience and subject conditions. In AWS IAM this is the norm: a role's trust policy can contain multiple statements, one per federated provider, each with its own
aud/subconditions. For example, a single product role trusted by both:aud= the web app's OAuth client_id,sub= the Ory user idtoken.actions.githubusercontent.com):aud= a custom audience,submatchingrepo:<org>/<repo>:ref:refs/heads/mainWith the current shape this is unrepresentable: an audience valid for one issuer is wrong for the other, and subject patterns for Ory user ids vs GitHub
repo:subjects can't be scoped to their issuer.Proposal
Restructure role trust as a list of per-provider entries, e.g.:
Token exchange validates the subject token against the entry matching its
iss, then enforces that entry'saud/subconditions.Dependencies
multistore/multistore-stscrates (RoleConfig, JWKS validation), so this needs a crate release first, then adoption here (src/sts.rsbuilds the_defaultrole;AUTH_AUDIENCEwould become that role's first trusted-provider audience)._defaultrole (see TODO insrc/sts.rs); per-role trust config becomes most useful once roles are served from the Source Cooperative API.Spun out of review feedback on #132 (finding 1: audience restriction).