fix: consistently handle string-valued boolean fields from non-compliant OIDC providers
Background
👋 @muhlemmer - hope all is well with you and the team!
AWS Cognito (and potentially other providers) return email_verified and phone_number_verified as strings ("true"/"false") instead of proper JSON booleans, violating the OIDC specification.
AWS Documentation confirms this:
Currently, Amazon Cognito returns the values for email_verified and phone_number_verified as strings.
Source: https://docs.aws.amazon.com/cognito/latest/developerguide/userinfo-endpoint.html#get-userinfo-response-sample
The Problem
The zitadel/oidc library currently handles this inconsistently:
- ✅
EmailVerifieduses the customBooltype (added in #139) - ❌
PhoneNumberVerifieduses Go's standardbool
This forces developers to handle semantically identical fields differently:
// Currently inconsistent code path
userInfo.EmailVerified = oidc.Bool(emailValue) // Cast
userInfo.PhoneNumberVerified = phoneValue // No cast
Additionally, the existing Bool.UnmarshalJSON implementation meant that false values couldn't overwrite true.
Solution
Applied Bool type consistently to both fields and simplified Bool.UnmarshalJSON using a direct switch statement to:
- Handle standard JSON booleans (true/false)
- Handle AWS Cognito string format ("true"/"false")
- Return errors on invalid input instead of silently failing
- Allow false to overwrite true
Updated tests to match codebase conventions, as well.
Impact
PhoneNumberVerified changes from bool to Bool (type alias of bool). Most consumer code should work as-is since Bool is just a type alias. Direct type assertions would need updating.
Definition of Ready
- [X] I am happy with the code
- [X] Short description of the feature/issue is added in the pr description
- [ ] PR is linked to the corresponding user story
- [X] Acceptance criteria are met
- [ ] All open todos and follow ups are defined in a new ticket and justified
- [ ] Deviations from the acceptance criteria and design are agreed with the PO and documented.
- [X] No debug or dead code
- [X] My code has no repetitions
- [X] Critical parts are tested automatically
- [x] Where possible E2E tests are implemented
- [X] Documentation/examples are up-to-date
- [ ] All non-functional requirements are met
- [x] Functionality of the acceptance criteria is checked manually on the dev system.
Breaking Changes
Please don't use this string in your PR comment. It get's converted to the commit message later and it will trigger the semrel tool to create a new major release.
Heyo @bashhack checking if you'll have some time to come back to this PR?
@elinashoko - yeah! Been swamped on my end, but should have time by end of this weekend to wrap this up - thanks for being so patient all 🤝
@muhlemmer + @elinashoko - should be all set here, I think the end approach is a nice balance, thanks again! PR feedback implemented, and branch updated accordingly with main 🤝
Congrats on the 1.25+ support, too! 🎉
Thank you kindly @bashhack @muhlemmer please have another look when you have a sec.
@bashhack pls have a look when you have a chance, let's get this over the line!
Thanks @muhlemmer + @elinashoko - totally reasonable change, thanks for the diff, merged and updated! Should be good to go 🤝