{"record":{"id":"0f245289386a5766","repo":"BeyondDimension/SteamTools","slug":"authenticator-does-not-contain-secret","errorCode":null,"errorMessage":"Authenticator does not contain secret","messagePattern":"Authenticator does not contain secret","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"warning","filePath":"src/BD.WTTS.Client.Plugins.Authenticator/UI/ViewModels/AuthenticatorImportPageViewModel.cs","lineNumber":294,"sourceCode":"                string issuer = string.Empty;\n                string label = string.IsNullOrEmpty(uri.LocalPath) == false\n                    ? uri.LocalPath[1..]\n                    : string.Empty; // skip past initial /\n                int p = label.IndexOf(\":\", StringComparison.Ordinal);\n                if (p != -1)\n                {\n                    issuer = label.Substring(0, p);\n                    label = label[(p + 1)..];\n                }\n\n                // + aren't decoded\n                label = label.Replace(\"+\", \" \");\n\n                var query = HttpUtility.ParseQueryString(uri.Query);\n                string? secret = query[\"secret\"];\n                if (string.IsNullOrEmpty(secret))\n                {\n                    throw new ApplicationException(\"Authenticator does not contain secret\");\n                }\n\n                string? counter = query[\"counter\"];\n                if (uri.Host == \"hotp\" && string.IsNullOrEmpty(counter))\n                {\n                    throw new ApplicationException(\"HOTP authenticator should have a counter\");\n                }\n\n                AuthenticatorDTO authenticatorDto = new();\n\n                AuthenticatorValueDTO auth;\n                if (string.Compare(issuer, \"BattleNet\", StringComparison.OrdinalIgnoreCase) == 0)\n                {\n                    string? serial = query[\"serial\"];\n                    if (string.IsNullOrEmpty(serial))\n                    {\n                        throw new ApplicationException(\"Battle.net Authenticator does not have a serial\");\n                    }","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/BeyondDimension/SteamTools/blob/c16ffa08e03b192d23ada290c4969e77f9201f3d/src/BD.WTTS.Client.Plugins.Authenticator/UI/ViewModels/AuthenticatorImportPageViewModel.cs#L276-L312","documentation":"Thrown when an otpauth URI has no 'secret' query parameter (or it is empty). The secret is the base32-encoded shared key required to generate codes, so without it the entry is unusable and import aborts at the secret check.","triggerScenarios":"Parsing otpauth://totp/... or ...://hotp/... where the query string has no 'secret' key, or its value is empty — e.g. a truncated URI, a malformed QR, or an export that omitted the secret.","commonSituations":"QR code was cropped/truncated losing the query string; export tool redacted the secret for safety; URI was hand-typed and the secret forgotten; encoding bug stripped the '?' or query.","solutions":["Re-export/ re-scan the entry ensuring the full otpauth URI including ?secret=... is captured.","Verify the QR/URI contains a non-empty secret parameter before attempting import.","If the source redacted the secret, obtain the unredacted original from the authenticator.","Show a field-level validation error in the UI naming the missing parameter."],"exampleFix":"// before\nstring? secret = query[\"secret\"];\nif (string.IsNullOrEmpty(secret))\n    throw new ApplicationException(\"Authenticator does not contain secret\");\n\n// after: validate length too (base32 secret should be >= 16 chars)\nstring? secret = query[\"secret\"];\nif (string.IsNullOrWhiteSpace(secret) || secret.Length < 16)\n    throw new ArgumentException(Strings.Import_MissingOrShortSecret);","handlingStrategy":"validation","validationCode":"var query = HttpUtility.ParseQueryString(uri.Query);\nvar secret = query[\"secret\"];\nif (string.IsNullOrWhiteSpace(secret))\n    errors.Add(($\"Line {n}: missing 'secret' parameter.\", line));\nelse if (secret.Length < 16)\n    errors.Add(($\"Line {n}: 'secret' too short (base32 expected >= 16 chars).\", line));","typeGuard":"bool HasValidSecret(Uri u) => !string.IsNullOrWhiteSpace(HttpUtility.ParseQueryString(u.Query)[\"secret\"]);","tryCatchPattern":"try { ParseAuthenticator(line); }\ncatch (ApplicationException ex) when (ex.Message == \"Authenticator does not contain secret\")\n{\n    importErrors.Add(($\"Missing secret (line {n}).\", line));\n    continue;\n}","preventionTips":["Validate presence AND non-empty length of 'secret' before importing.","Pre-flight check that the URI includes a '?' and a 'secret=' key.","Warn when a QR scan returns a suspiciously short string (likely truncated).","Collect all missing-field errors per entry and report them together."],"tags":["authenticator","import","validation","secret"],"backgroundTag":null,"analyzedSha":"c16ffa08e03b192d23ada290c4969e77f9201f3d","analyzedAt":"2026-08-13T11:52:20.410Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}