Multi-service authorization extension: token primitives, control plane, outbound tokens, gateway, and topology (#12–#15)#21
Open
JedimEmO wants to merge 10 commits into
Open
Conversation
Foundation crate for the service-coms extension (#12-#15): one claims model (RasClaims) for web sessions, internal service tokens, and gateway-derived tokens, distinguished by typ; ES256/EdDSA/HS256 signing with kid-based KeyRing rotation and JWKS publication (asymmetric only); strict TokenValidator with algorithm allowlist (asymmetric by default), key/header algorithm cross-check, issuer/audience/token-type pinning, and clock-skew-aware exp/nbf handling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements the #12 core: TokenSource abstraction with token families, TokenManager with fail-closed scope/audience bounds, family+subject+ audience+scopes+config-version cache keys, early refresh with skew, per-key concurrent-refresh dedup, and subject invalidation; GrantStore trait with in-memory impl; SecretString redaction throughout; and capability-scoped AuthorizedHttpClient over ras-transport-core that validates outbound hosts before minting tokens and never auto-replays requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t flow OAuth2/OIDC TokenSource for #12: refresh-token flow with grant-scope subset checks and transactional rotation persistence, client-credentials flow with audience forwarding, typed ConsentRequired on missing/revoked grants, and a PKCE S256 ConsentFlow with opaque single-use expiring state bound to user/integration/redirect/scopes. All provider traffic flows through HttpTransport so tests run a fake provider in-process. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed mode) Implements #13's embedded scope: Principal model with audience-scoped grants (permission P at audience A), in-memory AuthorizationStore with roles/bindings/direct grants, manifest import with unknown-permission rejection (explicit grant_custom escape hatch), pluggable ServiceIdentityVerifier with a constant-time static-secret dev verifier, and a fail-closed TokenIssuer minting short-lived single-audience internal JWTs stamped with authz_version. Supports topology ServiceGraphPolicy edge constraints, JWKS publication, key rotation with emergency removal, append-only audit events (no secrets/tokens), an embedded axum authority router (POST /auth/token, GET /auth/jwks.json), and RasTokenAuthProvider so existing generated services validate RAS tokens through ras-auth-core unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RasInternalTokenSource (#12/#13 bridge): obtains internal service tokens from the RAS authority, never minting locally. EmbeddedAuthority calls the issuer in-process; HttpAuthority posts to a central authority's /auth/token. v1 is service-as-service only; other principal modes fail closed before any authority call. End-to-end test proves a capability-scoped client attaches an authority-issued single-audience JWT that validates via the authority's JWKS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements #14: an axum-based reverse proxy that validates ras_web_session tokens locally via JWKS, matches routes with deterministic longest-prefix segment-aligned rules (duplicates fail validation, unmatched fail closed), narrows session permissions to the route's audience, and mints short-lived single-audience ras_gateway_access tokens that never outlive the session. The original session token is never forwarded; inbound Authorization/ Cookie/Host/hop-by-hop headers are stripped; bodies stream unbuffered. Derived-token cache keyed by session/subject/audience/authz-version. Missing audience permissions fail closed unless a route is explicitly authenticated-only; WebSocket upgrades fail closed (501) in v1. Consumes generated topology gateway profiles with startup upstream validation; a backend_validation_options helper pins issuer/audience/token type for backends. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements #15: a deployment-agnostic topology model with deterministic build-time validation (unique service ids/audiences, per-gateway route conflicts, undeclared references, public-gateway exposure of private services requiring explicit expose_private, edge permissions checked against target manifests with an explicit custom escape hatch) and byte-deterministic artifacts with content-derived ids: authorization policy JSON consumed by ras-authorization-core's ServiceGraphPolicy, gateway profile TOML consumed by ras-authorization-gateway, and Mermaid diagrams. The ras_topology! macro generates the validated builder from a declarative graph with typed references to manifest functions and generated permission constants; duplicate ids and undeclared references fail at compile time with spanned errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wires the whole extension together with real generated services: ras_topology! declares the graph; the generated policy artifact constrains the embedded authority and the generated gateway profile configures the gateway (deployment-provided upstream bindings). Billing serves /summary by acquiring a RAS internal token (TokenManager + RasInternalTokenSource, embedded mode) and calling the generated InvoiceServiceClient; the gateway narrows alice's web session to single-audience tokens per route; the invoice service accepts internal and gateway tokens via composed RasTokenAuthProviders while generated WITH_PERMISSIONS enforcement still applies per operation. e2e tests cover the happy paths plus fail-closed cases: missing audience permissions (403), missing session (401), unknown route (404), direct backend access with a session token (401), per-operation permission rejection, and undeclared topology edges denied by the authority. Topology builder now accepts generated ServicePermissions directly via IntoManifest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four new mdBook chapters (Service-To-Service Auth, Outbound Integrations, The Auth Gateway, Topology) covering the deployment presets, token model, fail-closed semantics, gateway deployment shape (behind existing ingress, WebSocket limitation), and topology artifact flow; CHANGELOG entry for the #12-#15 extension crates; README feature bullet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The documentation-hygiene CI check requires every crate's declared readme target to exist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Implements the service-coms/gateway extension from #12, #13, #14, and #15, in the layering agreed during the issue review. #16 (binary data-plane transport) is deliberately not included.
Eight new crates across three families, plus an end-to-end example and book documentation:
crates/authorization/RasClaimsmodel for all RAS token families (ras_web_sessionmulti-audience,ras_internal_access/ras_gateway_accesssingle-audience), ES256/EdDSA signing (HS256 for embedded shared-secret mode only),kid-basedKeyRingrotation with emergency removal, JWKS publication, and a strictTokenValidator(asymmetric-only algorithm allowlist by default, key/header algorithm cross-check against key-type confusion, issuer/audience/token-type pinning, clock-skew-aware exp/nbf).grant_customescape hatch), pluggableServiceIdentityVerifier(constant-time static-secret dev verifier shipped; production workload-identity adapters plug into the same trait), fail-closedTokenIssuerwith topology-policy edge enforcement, append-only audit events that never contain secrets, embedded axum authority routes (POST /auth/token,GET /auth/jwks.json), andRasTokenAuthProviderso existing generated services validate RAS tokens throughras-auth-coreunchanged.Authorization/Cookie/Host/hop-by-hop stripping, streaming bodies, derived-token cache keyed by session/subject/audience/authz-version, generated-profile consumption with startup upstream validation. WebSocket upgrades fail closed (501) in v1 — documented limitation.crates/integration/(#12)TokenSourceabstraction with token families, bounds-checked cachingTokenManager(family+integration+subject+audience+canonical-scopes+config-version cache keys, early refresh, per-key concurrent-refresh dedup),GrantStoretrait with in-memory impl,SecretStringredaction throughout, and capability-scopedAuthorizedHttpClients overras-transport-corethat validate outbound hosts before minting tokens and never auto-replay requests.ConsentRequiredon missing/revoked grants, and a PKCE S256ConsentFlowwith opaque single-use expiring state bound to user/integration/redirect/scopes/verifier.RasInternalTokenSource: obtains internal tokens from the authority (in-processEmbeddedAuthorityor HTTPHttpAuthority), holds no keys, never mints locally; v1 is service-as-service only with other principal modes failing closed.crates/topology/(#15)ras_topology!declares the logical service graph with typed references to manifest functions and generated permission constants. Compile-time: duplicate ids, undeclared references. Build-time: audience uniqueness, per-gateway route conflicts, public-gateway-exposes-private-service (explicitexpose_privaterequired), edge permissions checked against target manifests. Emits byte-deterministic artifacts with content-derived ids: authorization policy JSON (loaded by the authority to refuse issuance outside the declared graph), gateway profile TOML (loaded by the gateway with deployment-provided upstream bindings), and Mermaid diagrams.Example + docs
rest_service!services wired end-to-end: topology artifacts constrain the authority and configure the gateway; billing calls invoice with an authority-issued internal token through the generated client; the gateway narrows a web session per route; generatedWITH_PERMISSIONSenforcement still applies per operation.Testing
alg:none/algorithm-confusion rejection, cache-key collision resistance across token families and principal modes, concurrent-refresh dedup, refresh-rotation persistence-failure surfacing, consent-state single-use/expiry/binding, all fail-closed issuance paths, narrowing invariants, exposure validation, deterministic artifacts, and cross-crate artifact consumption.cargo fmt --checkclean,cargo denypassing, mdBook builds.Deliberate v1 scope cuts (from the issue review)
ras-identity-sessionis not yet migrated ontoras-authorization-tokensigning — that is the planned breaking follow-up, named in the Service-To-Service Auth chapter. Existing session behavior is untouched by this PR.New crates are all
0.1.0; no existing crate APIs changed (the only touch outside new code is workspace manifest membership/deps, docs, and the topology builder consuming generatedServicePermissions).Closes #12. Closes #13. Closes #14. Closes #15.
🤖 Generated with Claude Code