{"record":{"id":"049a2b4d63a57567","repo":"BeyondDimension/SteamTools","slug":"battle-net-authenticator-does-not-have-a-serial","errorCode":null,"errorMessage":"Battle.net Authenticator does not have a serial","messagePattern":"Battle\\.net Authenticator does not have a serial","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"warning","filePath":"src/BD.WTTS.Client.Plugins.Authenticator/UI/ViewModels/AuthenticatorImportPageViewModel.cs","lineNumber":311,"sourceCode":"                {\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                    }\n\n                    serial = serial.ToUpper();\n                    if (Regex.IsMatch(serial, @\"^[A-Z]{2}-?[\\d]{4}-?[\\d]{4}-?[\\d]{4}$\") == false)\n                    {\n                        throw new ApplicationException(\"Invalid serial for Battle.net Authenticator\");\n                    }\n\n                    auth = new BattleNetAuthenticator();\n                    //char[] decoded = Base32.getInstance().Decode(secret).Select(c => Convert.ToChar(c)).ToArray(); // this is hex string values\n                    //string hex = new string(decoded);\n                    //((BattleNetAuthenticator)auth).SecretKey = Authenticator.StringToByteArray(hex);\n\n                    ((BattleNetAuthenticator)auth).SecretKey = Base32.GetInstance().Decode(secret);\n\n                    ((BattleNetAuthenticator)auth).Serial = serial;\n\n                    issuer = string.Empty;","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/BeyondDimension/SteamTools/blob/c16ffa08e03b192d23ada290c4969e77f9201f3d/src/BD.WTTS.Client.Plugins.Authenticator/UI/ViewModels/AuthenticatorImportPageViewModel.cs#L293-L329","documentation":"Thrown when importing a Battle.net (issuer == 'BattleNet') authenticator whose otpauth URI has no 'serial' query parameter. Battle.net authenticators require a serial alongside the secret, so a missing serial makes the entry unusable.","triggerScenarios":"Parsing an otpauth entry whose issuer matches BattleNet (case-insensitive) but the query string has no 'serial' key or it is empty.","commonSituations":"Battle.net export that omitted the serial; URI truncated; entry reconstructed manually without the serial; conversion from a format that didn't store the serial.","solutions":["Re-export the Battle.net authenticator including both secret and serial.","Obtain the serial from the Battle.net account/agent and append &serial=... to the URI.","Reject the entry in the UI with a message naming the missing 'serial' field.","If serial is truly unavailable, import the secret under a generic TOTP issuer instead."],"exampleFix":"// before\nif (string.IsNullOrEmpty(serial))\n    throw new ApplicationException(\"Battle.net Authenticator does not have a serial\");\n\n// after: clearer, parameterised message\nif (string.IsNullOrEmpty(serial))\n    throw new ArgumentException(Strings.Import_BattleNetMissingSerial);","handlingStrategy":"validation","validationCode":"if (issuer.Equals(\"BattleNet\", StringComparison.OrdinalIgnoreCase))\n{\n    var serial = query[\"serial\"];\n    if (string.IsNullOrWhiteSpace(serial))\n        errors.Add(($\"Line {n}: Battle.net entry missing 'serial'.\", line));\n}","typeGuard":"bool BattleNetHasSerial(Uri u, string issuer) =>\n    !issuer.Equals(\"BattleNet\", StringComparison.OrdinalIgnoreCase)\n    || !string.IsNullOrWhiteSpace(HttpUtility.ParseQueryString(u.Query)[\"serial\"]);","tryCatchPattern":"try { ParseAuthenticator(line); }\ncatch (ApplicationException ex) when (ex.Message == \"Battle.net Authenticator does not have a serial\")\n{\n    importErrors.Add(($\"Battle.net entry missing serial (line {n}).\", line));\n    continue;\n}","preventionTips":["Detect the BattleNet issuer early and require both 'secret' and 'serial' before parsing.","Provide a dedicated Battle.net import template showing required fields.","Validate all required fields per issuer and report missing ones together.","Document the expected otpauth layout for Battle.net in the import UI."],"tags":["authenticator","import","validation","battlenet"],"backgroundTag":null,"analyzedSha":"c16ffa08e03b192d23ada290c4969e77f9201f3d","analyzedAt":"2026-08-13T11:52:20.410Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}