{"record":{"id":"746f15380f8b2e81","repo":"TechnitiumSoftware/DnsServer","slug":"time-based-one-time-password-totp-was-not-initia","errorCode":null,"errorMessage":"Time-based one-time password (TOTP) was not initialized for user: ","messagePattern":"Time-based one-time password \\(TOTP\\) was not initialized for user: ","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Auth/User.cs","lineNumber":300,"sourceCode":"        {\n            if (_isSsoUser)\n                throw new InvalidOperationException(\"Time-based one-time password (TOTP) feature is not available for SSO users.\");\n\n            if (_totpEnabled)\n                throw new InvalidOperationException(\"Time-based one-time password (TOTP) is already enabled for user: \" + _username);\n\n            _totpKeyUri = AuthenticatorKeyUri.Generate(issuer, _username);\n\n            return _totpKeyUri;\n        }\n\n        public void EnableTOTP(string totp)\n        {\n            if (_isSsoUser)\n                throw new InvalidOperationException(\"Time-based one-time password (TOTP) feature is not available for SSO users.\");\n\n            if (_totpKeyUri is null)\n                throw new InvalidOperationException(\"Time-based one-time password (TOTP) was not initialized for user: \" + _username);\n\n            if (_totpEnabled)\n                throw new InvalidOperationException(\"Time-based one-time password (TOTP) is already enabled for user: \" + _username);\n\n            Authenticator authenticator = new Authenticator(_totpKeyUri);\n\n            if (!authenticator.IsTOTPValid(totp))\n                throw new Exception(\"Invalid time-based one-time password (TOTP) was attempted for user: \" + _username);\n\n            _totpEnabled = true;\n        }\n\n        public void DisableTOTP()\n        {\n            if (_isSsoUser)\n                throw new InvalidOperationException(\"Time-based one-time password (TOTP) feature is not available for SSO users.\");\n\n            if (!_totpEnabled)","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Auth/User.cs#L282-L318","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nuser.EnableTOTP(totpCode);\n\n// after\nvar keyUri = user.InitializedTOTP(issuer); // store keyUri in session/UI\nuser.EnableTOTP(totpCode);                  // user enters code from app","handlingStrategy":"validation","validationCode":"if (!user.HasPendingTotpKey)\n    return BadRequest(\"Initialize TOTP before confirming a code.\");\nuser.EnableTOTP(totpCode);","typeGuard":"static bool TotpReadyToEnable(User user) => !user.IsSsoUser && user.HasPendingTotpKey && !user.TotpEnabled;","tryCatchPattern":null,"preventionTips":["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."],"tags":["auth","totp","mfa","state","workflow","technitium-dns-server"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}