diff --git a/scripts/iam/zitadel-provision.py b/scripts/iam/zitadel-provision.py index b0f7b6e..d39861e 100755 --- a/scripts/iam/zitadel-provision.py +++ b/scripts/iam/zitadel-provision.py @@ -420,15 +420,20 @@ def _ensure_smtp(token: str) -> str: def _ensure_login_policy(token: str) -> None: """Require password + a second factor, and demand enrolment at login. - forceMFA does not persist through the v1 policy endpoint (it reads back - absent), so the effective lever is mfaInitSkipLifetime: 0s, which makes the - login flow require enrolment instead of letting a user skip it for 30 days. + Enforcement needs forceMfa. The v1 endpoint ignores the spelling "forceMFA" + as an unknown field, which is why earlier runs looked successful while + projections.login_policies5 kept force_mfa = false. Second factors stay OTP and U2F: an authenticator app or a hardware key. Multi-factor is left configured for U2F with verification so a later step can move 2FA to 2MFA without another policy change. """ code, d = _api("PUT", "/admin/v1/policies/login", token, { + # Case matters: protojson accepts "forceMfa", the lowerCamel form of + # force_mfa. Sending "forceMFA" is silently ignored as an unknown field, + # so the policy looks unchanged and MFA is never enforced. + "forceMfa": True, + "forceMfaLocalOnly": True, "allowUsernamePassword": True, "allowRegister": False, "allowExternalIDP": True,