Skip to content

feat(identity): the Principal adapter, read from identity's contract - #2

Merged
ExtraToast merged 2 commits into
mainfrom
feat/identity-adapter
Oct 20, 2026
Merged

ExtraToast merged 2 commits into
mainfrom
feat/identity-adapter

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

rust-commons gets its first module, jorisjonkers_commons::identity: the Rust adapter for identity's Principal contract (JorisJonkers-dev/identity#111, spec JorisJonkers-dev/identity#105, Epic JorisJonkers-dev/identity#114). The repository itself was bootstrapped from repo-template. It has the workspace-libs topic and the Main ruleset requiring Pipeline Complete, and release-please is set up as a rust release with the version in the release PR title. It is one crate, jorisjonkers-commons, which consumers take by git URL and tag.

PrincipalReader::read(&HeaderMap) is the one entry point. It returns a Principal, or ReadError::Refused(Refusal) with the contract's reason:

  • The Gate's signed header. X-Identity-Principal, typ identity-principal+jwt, is verified against the Issuer's JWKS. The plain X-Auth-Request-* headers are never read.
  • A JWT access token. It is verified the same way, with typ JWT and a client_id required (NotAnAccessToken otherwise).
  • An opaque token. It is introspected with the Client's secret, and each answer is kept for 30 seconds.

Unknown key ids refetch the JWKS at most once a minute. ReadError::Unavailable means identity could not be asked, which is not a refusal.

The middleware and the policy helpers are tower layers, so they wrap an axum router or one of its routes:

  • PrincipalLayer puts the Principal in the request's extensions. It answers 401 to a refused request and 503 when identity is down.
  • require_role, refuse_tools, require_user and require_machine each answer 403 for the case they refuse.
  • require_recent_two_factor(minutes) sends a stale session 303 to /.auth/step-up?minutes=…&rd=…. It answers 403 to a Personal Token, a tool or a machine.

HTTP goes through a Fetch trait. The default ReqwestFetch uses reqwest with rustls and sits behind the default reqwest feature. Signatures are verified with ring, with no JWT crate. The Principal type is generated from the pinned schema by a golden test, tests/principal_type.rs (PRINCIPAL_UPDATE=true rewrites it).

Checks

  • Conformance. Every vector of the pinned contract reads as it says: 15 signed-header, 11 access-token and 5 introspection. An access token sent as the signed header, and a signed header sent as a bearer token, are both refused as wrong-typ.
  • Red checks. I removed each check in turn and watched a test fail: every policy helper's condition, the 401 and 403 branches, the step-up branch and the two-factor window, plus the reader's typ, issuer, audience, expiry, major and active checks, the refetch limit and the answer lifetime. One mutation survived: accepting an empty-string client_id. No vector has one, and a missing client_id is still refused.
  • Revocation. A token revoked at t₀ still reads at t₀+29s and is refused as inactive at t₀+30s. Five reads ask identity once.
  • Gates. task check runs cargo fmt --check and clippy -D warnings (pedantic, with and without default features), plus rustdoc with warnings denied, actionlint, cargo llvm-cov with a 95% line gate (99% now) and gitleaks. The vendored vectors and the tests' signing key are allowlisted in .gitleaks.toml.

Contract

contract/ is pinned at contract/principal/v1.1.0, released by JorisJonkers-dev/identity#132.

Closes JorisJonkers-dev/identity#111

@ExtraToast ExtraToast self-assigned this Oct 20, 2026
@ExtraToast ExtraToast added type: feature New user-facing or operator-facing capability. area: shared-libs Shared libraries, commons packages, and reusable SDK code. area: auth Authentication, authorization, sessions, or identity. labels Oct 20, 2026
@ExtraToast
ExtraToast marked this pull request as ready for review October 20, 2026 20:05
@ExtraToast
ExtraToast merged commit c574b7c into main Oct 20, 2026
2 checks passed
@ExtraToast
ExtraToast deleted the feat/identity-adapter branch October 20, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth Authentication, authorization, sessions, or identity. area: shared-libs Shared libraries, commons packages, and reusable SDK code. type: feature New user-facing or operator-facing capability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rust-commons, with the Rust adapter

1 participant