TechnitiumSoftware/DnsServer · error · InvalidOperationException
Time-based one-time password (TOTP) was not initialized for
Error message
Time-based one-time password (TOTP) was not initialized for user:
What it means
Thrown by User.EnableTOTP(totp) when _totpKeyUri is null. EnableTOTP expects the user to have first called InitializedTOTP(issuer), which generates and stores the shared secret; without that secret there is nothing to validate the supplied TOTP code against. The null check precedes authenticator construction so no NullReferenceException leaks.
Source
Thrown at DnsServerCore/Auth/User.cs:300
{
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);
if (!authenticator.IsTOTPValid(totp))
throw new Exception("Invalid time-based one-time password (TOTP) was attempted for user: " + _username);
_totpEnabled = true;
}
public void DisableTOTP()
{
if (_isSsoUser)
throw new InvalidOperationException("Time-based one-time password (TOTP) feature is not available for SSO users.");
if (!_totpEnabled)View on GitHub (pinned to d0484b6c1e)
Solutions
- Always call InitializedTOTP(issuer) first and only call EnableTOTP with the code from the authenticator app afterward.
- Track a server-side 'totp pending initialization' flag so the confirm endpoint rejects requests that skipped initialize.
- On the confirm endpoint, if the user has no pending key URI, redirect back to the initialize step.
Example fix
// before user.EnableTOTP(totpCode); // after var keyUri = user.InitializedTOTP(issuer); // store keyUri in session/UI user.EnableTOTP(totpCode); // user enters code from app
Defensive patterns
Strategy: validation
Validate before calling
if (!user.HasPendingTotpKey)
return BadRequest("Initialize TOTP before confirming a code.");
user.EnableTOTP(totpCode); Type guard
static bool TotpReadyToEnable(User user) => !user.IsSsoUser && user.HasPendingTotpKey && !user.TotpEnabled;
Prevention
- Enforce the initialize-before-confirm ordering server-side.
- Bind the confirm step to the session that ran initialize.
- On lost state, redirect the user back to the initialize step.
When it happens
Trigger: Calling EnableTOTP before InitializedTOTP on the same user — e.g. the browser refreshes or the user manually POSTs the confirm step without having completed (or after losing) the initialize step.
Common situations: Multi-step wizard state lost between page loads; direct API calls that skip the initialize step; the user opened a second tab and completed setup there, leaving _totpKeyUri null on reload.
Related errors
- Time-based one-time password (TOTP) is already enabled for u
- Time-based one-time password (TOTP) is already disabled for
- Time-based one-time password (TOTP) feature is not available
- Invalid time-based one-time password (TOTP) was attempted fo
- Already logged in. Please create a new object to use a diffe
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/746f15380f8b2e81.
Report an issue: GitHub.