TechnitiumSoftware/DnsServer · warning · InvalidOperationException

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

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.

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)

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.