From 3384510f3cc7b60a7e4102a89fdc792f9d37d719 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:45:30 +0200 Subject: [PATCH] fix: passkeys work independently of MFA mode When MFA mode is off but passkeys are enabled, WebAuthn + BackupCode factors are still synced to Logto. Previously, MFA off cleared all factors including WebAuthn, so passkey enrollment was never offered. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../cameleer/saas/config/LogtoStartupConfig.java | 13 ++++++++----- .../saas/vendor/VendorAuthPolicyController.java | 15 +++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/siegeln/cameleer/saas/config/LogtoStartupConfig.java b/src/main/java/net/siegeln/cameleer/saas/config/LogtoStartupConfig.java index 5c6b221..88a04b3 100644 --- a/src/main/java/net/siegeln/cameleer/saas/config/LogtoStartupConfig.java +++ b/src/main/java/net/siegeln/cameleer/saas/config/LogtoStartupConfig.java @@ -40,14 +40,17 @@ public class LogtoStartupConfig { var policy = authPolicyRepository.getPolicy(); String mfaMode = policy.getMfaMode(); boolean mfaEnabled = !"off".equals(mfaMode); + boolean passkeyEnabled = policy.isPasskeyEnabled(); - if (!mfaEnabled) { - logtoClient.updateSignInExperience(Map.of( - "mfa", Map.of("factors", List.of(), "policy", "UserControlled"))); - return; + List factors = new ArrayList<>(); + if (mfaEnabled) { + factors.add("Totp"); + } + if (mfaEnabled || passkeyEnabled) { + factors.add("WebAuthn"); + factors.add("BackupCode"); } - List factors = new ArrayList<>(List.of("Totp", "WebAuthn", "BackupCode")); String logtoPolicy = "required".equals(mfaMode) ? "Mandatory" : "UserControlled"; logtoClient.updateSignInExperience(Map.of( diff --git a/src/main/java/net/siegeln/cameleer/saas/vendor/VendorAuthPolicyController.java b/src/main/java/net/siegeln/cameleer/saas/vendor/VendorAuthPolicyController.java index 965c358..751c3dd 100644 --- a/src/main/java/net/siegeln/cameleer/saas/vendor/VendorAuthPolicyController.java +++ b/src/main/java/net/siegeln/cameleer/saas/vendor/VendorAuthPolicyController.java @@ -73,14 +73,17 @@ public class VendorAuthPolicyController { try { String mfaMode = policy.getMfaMode(); boolean mfaEnabled = !"off".equals(mfaMode); + boolean passkeyEnabled = policy.isPasskeyEnabled(); - if (!mfaEnabled) { - logtoClient.updateSignInExperience(Map.of( - "mfa", Map.of("factors", List.of(), "policy", "UserControlled"))); - return; + List factors = new ArrayList<>(); + if (mfaEnabled) { + factors.add("Totp"); + } + // Passkeys are always available when enabled, regardless of MFA mode + if (mfaEnabled || passkeyEnabled) { + factors.add("WebAuthn"); + factors.add("BackupCode"); } - - List factors = new ArrayList<>(List.of("Totp", "WebAuthn", "BackupCode")); String logtoPolicy = "required".equals(mfaMode) ? "Mandatory" : "UserControlled";