feat(oidc): mirror bunyip_role into the ID token (PMS-158) #86

Merged
longjacksonle merged 1 commit from fix/bunyip-emit-role-claim into main 2026-06-10 00:41:11 +02:00

What

Mirror the bunyip_role claim into the OIDC ID token. BUNYIP-66 added it to the at+jwt access token (for resource servers to translate); this adds it to the ID token so a relying party that reads only the ID token (e.g. a SPA rendering the signed-in user's role) can display / translate the role without an extra userinfo call.

Changes

  • Add bunyip_role: Option<String> to IdTokenClaims (skip_serializing_if = "Option::is_none", so an ID token from an older build still deserializes).
  • Extract a pure IdTokenClaims::build(...) (mirroring AtClaims::build from BUNYIP-66) so the claim mapping is unit-testable; mint_id_token now computes at_hash and delegates.
  • bunyip_role is emitted on every mint regardless of scope (identity-level, not gated on email).

Tests

id_token_carries_bunyip_role, id_token_bunyip_role_emitted_without_email_scope, id_token_bunyip_role_serializes_under_expected_key, id_token_deserializes_without_bunyip_role. All 9 bunyip-oidc unit tests pass; clippy --all-targets -- -D warnings and cargo fmt --check clean.

Context

The bunyip half of mokosh PMS-158 (the signed-in user's role always shows as technician). Bunyip never emitted its role in tokens, so mokosh-server's PMS-172 translation always saw bunyip_role = None and kept the JIT technician default. The access-token half shipped in BUNYIP-66; this completes the ID-token side.

🤖 Generated with Claude Code

## What Mirror the `bunyip_role` claim into the OIDC **ID token**. BUNYIP-66 added it to the at+jwt **access token** (for resource servers to translate); this adds it to the ID token so a relying party that reads only the ID token (e.g. a SPA rendering the signed-in user's role) can display / translate the role without an extra userinfo call. ## Changes - Add `bunyip_role: Option<String>` to `IdTokenClaims` (`skip_serializing_if = "Option::is_none"`, so an ID token from an older build still deserializes). - Extract a pure `IdTokenClaims::build(...)` (mirroring `AtClaims::build` from BUNYIP-66) so the claim mapping is unit-testable; `mint_id_token` now computes `at_hash` and delegates. - `bunyip_role` is emitted on every mint regardless of scope (identity-level, not gated on `email`). ## Tests `id_token_carries_bunyip_role`, `id_token_bunyip_role_emitted_without_email_scope`, `id_token_bunyip_role_serializes_under_expected_key`, `id_token_deserializes_without_bunyip_role`. All 9 `bunyip-oidc` unit tests pass; `clippy --all-targets -- -D warnings` and `cargo fmt --check` clean. ## Context The bunyip half of mokosh **PMS-158** (the signed-in user's role always shows as `technician`). Bunyip never emitted its role in tokens, so mokosh-server's PMS-172 translation always saw `bunyip_role = None` and kept the JIT `technician` default. The access-token half shipped in BUNYIP-66; this completes the ID-token side. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(oidc): mirror bunyip_role into the ID token (PMS-158)
All checks were successful
Check / fmt / clippy / build / test (pull_request) Successful in 1m11s
Create release / Create release from merged PR (pull_request) Has been skipped
0e3e9ae425
BUNYIP-66 added the user's Bunyip system role as the bunyip_role claim on the at+jwt access token so resource servers can translate it to their own authorization model. This mirrors the same claim into the ID token so a relying party that reads only the ID token (e.g. a SPA showing the signed-in user's role in its header) can display or translate the role without an extra userinfo round-trip.

To keep the mapping unit-testable, the ID-token claim assembly is extracted into a pure IdTokenClaims::build (mirroring AtClaims::build): mint_id_token now computes at_hash and delegates claim construction to it. bunyip_role is emitted on every mint regardless of scope (identity-level, not gated on the email scope), as an Option<String> with skip_serializing_if so an ID token minted by an older build still deserializes.

Tests: id_token_carries_bunyip_role, id_token_bunyip_role_emitted_without_email_scope, id_token_bunyip_role_serializes_under_expected_key, id_token_deserializes_without_bunyip_role. All 9 bunyip-oidc unit tests pass; clippy --all-targets and fmt clean.

Context: this is the bunyip half of mokosh PMS-158 (the signed-in user's role always displayed as technician). Bunyip never emitted the role in its tokens, so mokosh-server's PMS-172 translation always saw bunyip_role = None and kept the JIT technician default. The access-token half landed in BUNYIP-66; this completes the ID-token side.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch fix/bunyip-emit-role-claim 2026-06-10 00:41:11 +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/bunyip!86
No description provided.