Start learning free

LESSON 95 OF 173 · BACKEND: USERS AND SECURITY

Too Many Tries: Rate Limits in TypeScript

Practice

Right now /login answers every try. A script could send thousands of passwords for one email until one works. A rate limit stops it: the shop counts the failed logins of each email, and after 3 it answers 429 Too Many Requests without even checking the password.

Counting with an object: an object of type { [key: string]: number } can map each key to a count. A key that isn't there yet gives undefined (the type promises a number, but the object only has the keys you put in), and || 0 turns that into 0. Here it counts the likes of each product:

const LIKES: { [productId: string]: number } = {};

app.post("/products/:productId/like", (req: Request, res: Response) => {
  const productId = req.params.productId;
  LIKES[productId] = (LIKES[productId] || 0) + 1;
  res.json({ likes: LIKES[productId] });
});

Three details make the limit work. The check comes first, before the password: once the count is 3, even the right password gets 429, so a guesser can't tell a right guess from a wrong one. Emails with no account are counted too, so the answers look the same whether the account exists or not. And a right password sets the count back to 0, so three typos spread over a year don't lock anyone out.

Ana's password is secret123. Your code must give these answers, in this order:

POST /login  {"email": "ana@example.com", "password": "guess1"}     →  401  {"error": "Wrong email or password"}
POST /login  {"email": "ana@example.com", "password": "guess2"}     →  401  {"error": "Wrong email or password"}
POST /login  {"email": "ana@example.com", "password": "secret123"}  →  200  {"token": "<token>"}
POST /login  "guess3", then "guess4", then "guess5"                  →  401  each time
POST /login  {"email": "ana@example.com", "password": "secret123"}  →  429  {"error": "Too many failed logins, try again later"}
POST /login  {"email": "admin@example.com", "password": "admin-pass-123"}  →  200  {"token": "<admin>"}
GET /me  (Bearer <token>)  →  200  {"email": "ana@example.com", "id": 2, "role": "customer"}

Your task: create the object FAILED_LOGINS, of type { [email: string]: number }. In the /login route, answer { error: "Too many failed logins, try again later" } with 429 when the email already has 3 or more failed tries. Add 1 to the email's count on every failed login, and set it back to 0 on a good one.


Assignment
Follow the Coding Tutorial to add a rate limit to the /login route: the counter, the 429 check, counting failures and the reset.


Hint
Read a count with (FAILED_LOGINS[data.email] || 0), parentheses included: an email that never failed isn't a key yet, so the lookup gives undefined. The 429 check goes right after the data line, before the user is even looked up.


STEP 1 OF 5
    Ctrl + Enter to check

    Sec Rate Limit in TypeScript

    Keep going with TypeScript1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free