{"record":{"id":"e607e281eef1341a","repo":"coder/code-server","slug":"login-rate-limited","errorCode":null,"errorMessage":"Login rate limited!","messagePattern":"Login rate limited!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/node/routes/login.ts","lineNumber":78,"sourceCode":"  const to = (typeof req.query.to === \"string\" && req.query.to) || \"/\"\n  if (await authenticated(req)) {\n    return redirect(req, res, to, { to: undefined })\n  }\n  next()\n})\n\nrouter.get(\"/\", async (req, res) => {\n  res.send(await getRoot(req))\n})\n\nrouter.post<{}, string, { password?: string; base?: string } | undefined, { to?: string }>(\"/\", async (req, res) => {\n  const password = sanitizeString(req.body?.password)\n  const hashedPasswordFromArgs = req.args[\"hashed-password\"]\n\n  try {\n    // Check to see if they exceeded their login attempts\n    if (!limiter.canTry()) {\n      throw new Error(i18n.t(\"LOGIN_RATE_LIMIT\") as string)\n    }\n\n    if (!password) {\n      throw new Error(i18n.t(\"MISS_PASSWORD\") as string)\n    }\n\n    const passwordMethod = getPasswordMethod(hashedPasswordFromArgs)\n    const { isPasswordValid, hashedPassword } = await handlePasswordValidation({\n      passwordMethod,\n      hashedPasswordFromArgs,\n      passwordFromRequestBody: password,\n      passwordFromArgs: req.args.password,\n    })\n\n    if (isPasswordValid) {\n      // The hash does not add any actual security but we do it for\n      // obfuscation purposes (and as a side effect it handles escaping).\n      res.cookie(req.cookieSessionName, hashedPassword, getCookieOptions(req))","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/coder/code-server/blob/51f90a376b42e217b38937410fe2855e0c1db87e/src/node/routes/login.ts#L60-L96","documentation":"The login POST handler (login.ts:78) calls limiter.canTry() and, if the caller has exceeded the allowed attempts within the window, throws the localized LOGIN_RATE_LIMIT message. This brute-force protection throttles repeated password guesses per IP.","triggerScenarios":"Repeated POST /login attempts (correct or incorrect) from the same client/IP beyond the limiter's threshold within its time window; automation that retries logins in a tight loop.","commonSituations":"Users mistyping the password many times; CI smoke tests hammering /login; a shared NAT IP getting throttled because of another user's failures.","solutions":["Wait for the rate-limit window to elapse, then retry the login","Verify the password is correct before retrying to avoid re-tripping the limiter","For automated clients, space login attempts with backoff and cache the session cookie"],"exampleFix":"// before: tight retry loop\nfor (const pw of candidates) await tryLogin(pw)  // trips limiter\n\n// after: respect 429/rate-limit with backoff\nasync function loginWithBackoff(pw) {\n  for (let delay = 1000; ; delay *= 2) {\n    const r = await tryLogin(pw)\n    if (r.status === 429 || r.rateLimited) await sleep(delay)\n    else return r\n  }\n}","handlingStrategy":"retry","validationCode":"// Client-side: track attempts and back off before the server trips the limiter\nconst MAX_ATTEMPTS = 5\nconst WINDOW_MS = 60_000\nlet attempts = 0, windowStart = Date.now()\nfunction canTryLogin(): boolean {\n  if (Date.now() - windowStart > WINDOW_MS) { attempts = 0; windowStart = Date.now() }\n  return attempts++ < MAX_ATTEMPTS\n}\nif (!canTryLogin()) throw new Error(\"Login rate limited; wait before retrying\")","typeGuard":null,"tryCatchPattern":"async function loginWithBackoff(password: string) {\n  for (let delay = 2000; delay <= 60_000; delay *= 2) {\n    try {\n      return await tryLogin(password)\n    } catch (e) {\n      if (e instanceof Error && /rate limited/i.test(e.message)) {\n        await new Promise((r) => setTimeout(r, delay))\n        continue\n      }\n      throw e\n    }\n  }\n  throw new Error(\"Exceeded login retries\")\n}","preventionTips":["Cache the session cookie and only re-login on 401","Cap automated login retries and use exponential backoff","Confirm the password is correct before retrying to avoid burning attempts"],"tags":["auth","rate-limit","security","login","brute-force"],"backgroundTag":null,"analyzedSha":"51f90a376b42e217b38937410fe2855e0c1db87e","analyzedAt":"2026-08-12T11:27:34.273Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}