feat/bunyip-op-cutover #52

Merged
YousifShkara merged 3 commits from feat/bunyip-op-cutover into main 2026-06-02 23:57:32 +02:00
Owner
No description provided.
feat(auth): scaffold Resource-Server OIDC verifier (bunyip-as-OP cutover)
All checks were successful
Build OCI container / Build and push mokosh-api image (push) Successful in 5m46s
81314387ec
Stage-1 scaffold for the bunyip-as-OP cutover (docs/new-auth/mokosh in the docs repo). Lands the boundary for the RS-side at+jwt verifier without wiring it in, so reviewers can iterate on the JWKS-cache shape, env contract, and error mapping incrementally:

- VerifierConfig::from_env reads OIDC_ISSUER + OIDC_AUDIENCE with fail-loud errors so a misconfigured RS never silently accepts wrong-issuer tokens.
- AtClaims mirrors bunyip's RFC 9068 at+jwt shape (crates/bunyip-oidc/src/services/oidc_provider.rs:31), narrowed to the fields mokosh-server actually consumes (sub, scope, exp, aud, iss).
- Verifier::verify_at_jwt is intentionally a hard 401 (VerifyError::NotImplemented) so any accidental wiring fails closed, never open. The signature + JWKS-cache implementation lands in a follow-up on this branch (rusty-links/src/auth/oidc_rs.rs is the reference port).
- Module is gated behind cfg(feature = "server") to match the rest of auth/.

No existing code path changes: AuthMiddleware still routes through the legacy HS256 + mokosh-auth at+jwt verifier. The IdP code stays alive for the transitional dual-issuer state per dev-sso-three-repo-runbook.md \xc2\xa73.8.
Stage-1 follow-up on feat/bunyip-op-cutover. Fills in the scaffold from 8131438 with the actual JWKS-cache + signature path ported from rusty-links/src/auth/oidc_rs.rs:

- Discovery: fetches `{issuer}/.well-known/openid-configuration` once per cache TTL, pins the doc's iss against OIDC_ISSUER so a hijacked discovery host cannot point us at a different JWKS.
- JWKS: parses every OKP/Ed25519 sig-use JWK, reconstructs the SubjectPublicKeyInfo PEM from the JWK `x` value (no jwk-crate dep), caches by kid.
- Validation: EdDSA + iss + aud + exp via jsonwebtoken's Validation, with OIDC_LEEWAY_SECONDS (default 30) for clock skew. Unknown-kid forces one JWKS refresh and one retry, mirroring the rusty-links pattern (key rotation).
- AtClaims is narrowed to (iss, sub, aud, client_id, scope, exp, iat); UserInfo carries (sub, email, email_verified) for JIT-provisioning the local users row (the at+jwt itself does not carry email per RFC 9068 and bunyip's AtClaims shape).
- VerifyError maps cleanly to 401 with the standard categories (Malformed / InvalidSignature / InvalidIssuer / InvalidAudience / Expired / JwksFetch / DiscoveryFetch).

Still NOT wired into AuthMiddleware; the next commit on this branch adds the wiring + JIT user row.

Unit tests cover the PEM round-trip and the env-config error path.
feat(auth): wire bunyip RS verifier into AuthMiddleware
Some checks failed
Build OCI container / Build and push mokosh-api image (push) Successful in 2m50s
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt + clippy + compile + tests (pull_request) Failing after 8s
cae15d2c20
Stage-1 wiring on feat/bunyip-op-cutover. Inserts the bunyip-as-OP path at the TOP of the verification ladder so PSA /api/v1/* accepts tokens minted by bunyip-api as soon as OIDC_ISSUER + OIDC_AUDIENCE are configured. The two legacy paths stay underneath so existing sessions don't break during the cutover:

1. (new) Bunyip RS: at+jwt verified against bunyip's JWKS; on success JIT-mirror (sub, email) into public.users via AuthService::upsert_user_from_oidc.
2. (existing) Legacy mokosh-auth at+jwt (the IdP code path being retired).
3. (existing) Legacy HS256 cookie (customer portal + transitional dashboard).

Per docs/new-auth/mokosh/03-mokosh-server-rs-cutover.md §3.3:
- email comes from /oauth2/userinfo (the at+jwt has no email claim per RFC 9068); on userinfo failure we still insert with a placeholder ("<sub>@unresolved.invalid") so the request can proceed and a later request refreshes.
- tenant_id falls back to OIDC_DEFAULT_TENANT_ID, defaulting to Uuid::from_u128(1) (matching auth::bootstrap default_tenant_id). v1 has no real multi-tenant claim plumbing.
- role defaults to UserRole::default() (Technician), matching the safest least-privilege default for a fresh PSA login. Promotion is admin-driven, same as before.

create_api_router gets a new bunyip_verifier: Option<oidc_rs::Verifier> parameter; main.rs builds it from env. When the env isn't set the verifier is None and the middleware skips path 1 entirely, so this change is a no-op on any deployment that hasn't flipped to bunyip yet.
YousifShkara deleted branch feat/bunyip-op-cutover 2026-06-02 23:57:32 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!52
No description provided.