fix(auth): explicit Validation::new(Algorithm::HS256) + mint iss/aud/nbf claims #406

Merged
YousifShkara merged 2 commits from fix/MAPPS-334-explicit-validation-secret-floor-v2 into main 2026-06-30 07:56:33 +02:00
Owner

MAPPS-334 (step 1 of 2): close the alg-confusion door + prepare for strict iss/aud validation.

decode_token previously used Validation::default(). That defaults to HS256 today, but the default is set by jsonwebtoken and can drift on a future dep bump; pin Algorithm::HS256 explicitly so the protocol contract is greppable and stable. Set validate_exp = true (already implicit) and add a 30s leeway to match the Bunyip RS verifier.

Mint side now stamps iss = "mokosh-server", aud = "mokosh-server", and nbf = iat on every access + refresh token. The new fields land on JwtClaims with #[serde(default)] so any in-flight legacy token that lacks them still deserializes - no rolling-deploy 401 storm.

iss / aud validation is NOT pinned in this PR. Flipping it on today would 401 every legacy access + refresh token in flight (they were minted without the claims) and force a re-login storm. The follow-up ticket flips strict validation after the rolling refresh-TTL window (~30 days post-merge) has rotated every live token through the new mint path.

JWT_SECRET length floor (32 bytes outside dev) already exists in src/main.rs (PMS-499); no change needed.

#MAPPS-334

MAPPS-334 (step 1 of 2): close the alg-confusion door + prepare for strict iss/aud validation. `decode_token` previously used `Validation::default()`. That defaults to HS256 today, but the default is set by jsonwebtoken and can drift on a future dep bump; pin `Algorithm::HS256` explicitly so the protocol contract is greppable and stable. Set `validate_exp = true` (already implicit) and add a 30s `leeway` to match the Bunyip RS verifier. Mint side now stamps `iss = "mokosh-server"`, `aud = "mokosh-server"`, and `nbf = iat` on every access + refresh token. The new fields land on `JwtClaims` with `#[serde(default)]` so any in-flight legacy token that lacks them still deserializes - no rolling-deploy 401 storm. iss / aud validation is NOT pinned in this PR. Flipping it on today would 401 every legacy access + refresh token in flight (they were minted without the claims) and force a re-login storm. The follow-up ticket flips strict validation after the rolling refresh-TTL window (~30 days post-merge) has rotated every live token through the new mint path. JWT_SECRET length floor (32 bytes outside dev) already exists in src/main.rs (PMS-499); no change needed. #MAPPS-334
fix(auth): explicit Validation::new(Algorithm::HS256) + mint iss/aud/nbf claims
Some checks failed
E2E / Playwright against staging (pull_request) Successful in 37s
Check / fmt + clippy + build + tests (pull_request) Successful in 4m57s
Integration / integration tests (pull_request) Failing after 1m53s
ab59925556
MAPPS-334 (step 1 of 2): close the alg-confusion door + prepare for strict iss/aud validation.

`decode_token` previously used `Validation::default()`. That defaults to HS256 today, but the default is set by jsonwebtoken and can drift on a future dep bump; pin `Algorithm::HS256` explicitly so the protocol contract is greppable and stable. Set `validate_exp = true` (already implicit) and add a 30s `leeway` to match the Bunyip RS verifier.

Mint side now stamps `iss = "mokosh-server"`, `aud = "mokosh-server"`, and `nbf = iat` on every access + refresh token. The new fields land on `JwtClaims` with `#[serde(default)]` so any in-flight legacy token that lacks them still deserializes - no rolling-deploy 401 storm.

iss / aud validation is NOT pinned in this PR. Flipping it on today would 401 every legacy access + refresh token in flight (they were minted without the claims) and force a re-login storm. The follow-up ticket flips strict validation after the rolling refresh-TTL window (~30 days post-merge) has rotated every live token through the new mint path.

JWT_SECRET length floor (32 bytes outside dev) already exists in src/main.rs (PMS-499); no change needed.

#MAPPS-334
fix(auth): disable validate_aud until strict iss/aud flip lands
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 3m17s
Integration / integration tests (pull_request) Successful in 10m26s
E2E / Playwright against staging (pull_request) Successful in 52s
Create release / Gate (release-branch merges only) (pull_request) Successful in 1s
Create release / Create release from merged PR (pull_request) Has been skipped
92ed8c1d40
`Validation::new(Algorithm::HS256)` defaults `validate_aud = true` with no allowed-aud set. Newly minted tokens carry `aud = "mokosh-server"` (per the previous commit on this PR), so decode rejects them with `InvalidAudience` before the deferred strict-flip ticket ever lands. The integration suite caught it: every `tests/approvals_polymorphic.rs::*` round-trip 401s at the auth boundary instead of hitting the handler.

Set `validation.validate_aud = false` explicitly so the deferred-flip posture documented on the previous commit (iss / aud minted now, validated strictly in a follow-up ticket after the rolling refresh-TTL window) actually holds. The follow-up ticket flips it back on and pins `aud` + `iss` to `MOKOSH_JWT_AUDIENCE` / `MOKOSH_JWT_ISSUER`.

#MAPPS-334
YousifShkara deleted branch fix/MAPPS-334-explicit-validation-secret-floor-v2 2026-06-30 07:56:33 +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!406
No description provided.