oidc icon indicating copy to clipboard operation
oidc copied to clipboard

fix: consistently handle string-valued boolean fields from non-compliant OIDC providers

Open bashhack opened this issue 4 months ago • 7 comments

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:

  • EmailVerified uses the custom Bool type (added in #139)
  • PhoneNumberVerified uses Go's standard bool

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.

bashhack avatar Aug 31 '25 05:08 bashhack

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.

muhlemmer avatar Sep 01 '25 11:09 muhlemmer

Heyo @bashhack checking if you'll have some time to come back to this PR?

elinashoko avatar Sep 25 '25 08:09 elinashoko

@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 🤝

bashhack avatar Oct 02 '25 02:10 bashhack

@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! 🎉

bashhack avatar Oct 04 '25 04:10 bashhack

Thank you kindly @bashhack @muhlemmer please have another look when you have a sec.

elinashoko avatar Oct 08 '25 11:10 elinashoko

@bashhack pls have a look when you have a chance, let's get this over the line!

elinashoko avatar Oct 23 '25 11:10 elinashoko

Thanks @muhlemmer + @elinashoko - totally reasonable change, thanks for the diff, merged and updated! Should be good to go 🤝

bashhack avatar Oct 26 '25 16:10 bashhack