fix(auth): bind userinfo.sub == claims.sub; truthful email_verified_at #404
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
psa-systems/mokosh-server!404
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/MAPPS-335-sub-binding-and-email-verified-truth"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
MAPPS-335: two related Bunyip-RS hardening items the 2026-06-30 audit named medium-severity.
Sub-binding:
ensure_user_from_bunyippreviously readinfo.email,info.email_verified,info.given_name,info.family_namedirectly off the/oauth2/userinforesponse without asserting the response'ssubmatched the at+jwt's verifiedsub. A misbehaving / compromised OP (load-balancer bug, attacker-influenced response) injected ANOTHER user's identity into the JIT row keyed onclaims.sub. The at+jwt signature was already validated; the userinfo response is only trusted to belong to the same subject. Filter theUserInfoto keep only the response wheninfo.sub == claims.sub; on mismatch, log warn and treat as "no userinfo claims" so the JIT path falls back to the placeholder email + synthetic name. The request still succeeds (no 401 storm on a transient OP glitch), but no wrong-identity row is ever written.Truthful email_verified_at:
upsert_user_from_oidcstampedemail_verified_at = NOW()unconditionally, including the placeholdersub@unresolved.invalidbranch reached when the IdP reportedemail_verified=false. Downstream gates that reademail_verified_at IS NOT NULL(invite consumption, certain admin paths) got a false positive. Plumb the actualemail_verifiedflag through; the column lands as NULL when the IdP says false, NOW() only when true. TheON CONFLICT DO UPDATEclause was extended with aCOALESCEonemail_verified_atso an existing legitimate verification timestamp is preserved even when a later JIT call passes false.No migration; the column already exists and the change is purely a write-side truthfulness fix.
#MAPPS-335