My2FAService.ktMy2FAService is a @Service that orchestrates multi-channel 2FA verification. It wires UserAuthenticationsService (token storage), SmsSenderConfig (SMS availability check), My2FABruteForceProtection (rate limiting), My2FARequestConfiguration (group-based disable), and WebAuthnSupport (WebAuthn availability).
validateOTP(code, *expectedToken) — Primary entry point. Accepts a 6-digit code and one or more expected tokens (from SMS or e-mail).preCheck(code) — Returns BLOCKED if brute force protection is active, FAILED if code is too short (<4 chars).expectedToken (SMS/e-mail tokens). On match: calls bruteForceProtection.registerOTPSuccess() and updates lastSuccessful2FA timestamp._validateAuthenticatorOTP(code) which validates against the stored TOTP token via TimeBased2FA.standard.validate().bruteForceProtection.registerOTPFailure() increments the failure counter.Success registers reset the counter. Failures increment it. getBlockedResult() returns null or a BLOCKED OTPCheckResult with a localized message. The pre-check gates with minimum code length (4) to avoid unnecessary brute-force counting on obviously invalid inputs.
userConfigured2FA — True if the user has at least one 2FA method configured (authenticator app token, mobile phone for SMS, or WebAuthn credentials).smsConfigured — True if SMS sender is configured or system is in development mode.checklastSuccessful2FA(timePeriod, unit, user) — Checks if the last successful 2FA was within a given time period (supports MINUTES, HOURS, DAYS).isMail2FADisabledForUser(userContext) — Checks if the user belongs to any group listed in My2FARequestConfiguration.disableEmail2FAForGroups.setDisabled(groupNames) parses a semicolon/comma/colon-separated string of group names from configuration, resolves them against GroupService.allGroups, and populates mail2FADisabledGroupIds. Users in these groups skip e-mail-based 2FA. This is configured on @PostConstruct from my2FARequestConfiguration.disableEmail2FAForGroups.
Multi-channel OTP validation (SMS, e-mail, authenticator app) is implemented as a fallback chain: external tokens are checked first, then TOTP. This allows a single validateOTP call to work regardless of delivery method. Brute force protection is applied at the OTP level (not login level) so each 2FA attempt is rate-limited independently. The group-based disable feature supports organizational policies where certain users (e.g. administrators with hardware tokens) don't need e-mail 2FA. The lastSuccessful2FA timestamp enables session-level 2FA caching (don't re-prompt if recently verified).
868d6abb7 2025 -> 2026 600951ee0 2025 -> 2026 63081666f Source file headers: 2024-> 2025. 199c26801 Source file headers: 2024-> 2025. 67805f2fc ThreadLocalUserContext.user -> loggedInUser 58ffda82b ThreadLocalUserContext.user -> loggedInUser 4c04cfd65 MAJOR-CHANGE! Migration of integer id's to Long id's. fd13c259a MAJOR-CHANGE! Migration of integer id's to Long id's. 4f5a458c9 Migration stuff in progress... 635197c0b Migration stuff in progress... 77bade6df javax.* -> jakarta.* 3afa03fb1 javax.* -> jakarta.* b6092df09 Copyright 2023 -> 2024 9a0609dd1 Copyright 2023 -> 2024 ab45d51fa Copyright 2001-2022 -> 2001-2023. 29815da21 Copyright 2001-2022 -> 2001-2023. 38bec971a ThreadLocal -> Kotlin 19bb46d57 ThreadLocal -> Kotlin cb605ebde 2FA: WebAuthn not experimental; at least one 2FA required. f988a9b1b 2FA: WebAuthn not experimental; at least one 2FA required.