TechnitiumSoftware/DnsServer · warning · InvalidOperationException

Time-based one-time password (TOTP) is already enabled for…

Error message

Time-based one-time password (TOTP) is already enabled for user: 

What it means

Thrown by User.InitializedTOTP(issuer) when _totpEnabled is already true, meaning TOTP was previously initialized and confirmed via EnableTOTP. Re-initializing would generate a new AuthenticatorKeyUri and orphan the old secret, breaking already-configured authenticator apps, so the library refuses the call. The state check comes after the SSO guard but before any key generation.

Solutions

  1. Call DisableTOTP() before calling InitializedTOTP() to rotate the TOTP secret on an already-enabled account.
  2. Check the user's TOTP state via the session/API before offering the 'initialize' action.
  3. Build the UI so re-configuration goes through a single 'reset TOTP' flow that disables then re-initializes.

Example fix

// before
var keyUri = user.InitializedTOTP(issuer);

// after
if (user.TotpEnabled)
    user.DisableTOTP();
var keyUri = user.InitializedTOTP(issuer);
Defensive patterns

Strategy: validation

Validate before calling

if (user.TotpEnabled)
    return Conflict("TOTP is already enabled; disable it first to reconfigure.");
var keyUri = user.InitializedTOTP(issuer);

Type guard

static bool CanInitializeTotp(User user) => !user.IsSsoUser && !user.TotpEnabled;

Prevention

When it happens

Trigger: Calling InitializedTOTP a second time on the same user after EnableTOTP has already completed (i.e. _totpEnabled flipped to true). Common from a UI that lets the user click 'set up TOTP' again, or from a retry loop that does not check current state.

Common situations: User clicks 'reconfigure TOTP' instead of using the dedicated disable-then-re-enable path; a provisioning script runs twice; the authenticator app was lost but the correct recovery flow (DisableTOTP first) was not followed.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/f980c36de1e568f7. Report an issue: GitHub.

Appendix: source

Thrown at DnsServerCore/Auth/User.cs:287

            _passwordHash = GetPasswordHashFor(newPassword);
        }

        public void LoadOldSchemeCredentials(string passwordHash)
        {
            if (_isSsoUser)
                throw new InvalidOperationException();

            _passwordHashType = UserPasswordHashType.OldScheme;
            _passwordHash = passwordHash;
        }

        public AuthenticatorKeyUri InitializedTOTP(string issuer)
        {
            if (_isSsoUser)
                throw new InvalidOperationException("Time-based one-time password (TOTP) feature is not available for SSO users.");

            if (_totpEnabled)
                throw new InvalidOperationException("Time-based one-time password (TOTP) is already enabled for user: " + _username);

            _totpKeyUri = AuthenticatorKeyUri.Generate(issuer, _username);

            return _totpKeyUri;
        }

        public void EnableTOTP(string totp)
        {
            if (_isSsoUser)
                throw new InvalidOperationException("Time-based one-time password (TOTP) feature is not available for SSO users.");

            if (_totpKeyUri is null)
                throw new InvalidOperationException("Time-based one-time password (TOTP) was not initialized for user: " + _username);

            if (_totpEnabled)
                throw new InvalidOperationException("Time-based one-time password (TOTP) is already enabled for user: " + _username);

            Authenticator authenticator = new Authenticator(_totpKeyUri);

View on GitHub (pinned to d0484b6c1e)