Start learning free

LESSON 99 OF 173 · BACKEND: USERS AND SECURITY

Don't Let Clients Set Everything

Practice

A sign-up sends a JSON body, and a client can put any keys in it, not only the ones your form asks for. If your code saves every key as a column, the client chooses what gets stored: "role": "admin" makes an admin, and "id": 1 tries to take the admin's id. This hole is called mass assignment, and TypeScript can't stop it: the request's body is any, so the compiler accepts whatever keys it holds.

The fix is an allowlist: an array of the keys a client may set. You build the new row yourself and copy only the keys on your list. Everything else in the body is ignored. Here is the idea for a review form:

const REVIEW_FIELDS = ["stars", "text"];

const review: { [field: string]: any } = {};
for (const field of REVIEW_FIELDS) {
  if (field in data) {
    review[field] = data[field];
  }
}

The type { [field: string]: any } lets the empty object take keys you add later. Then answer with what the database stored, not with what the client sent. Ana asks to be an admin and becomes a customer. Ben asks for id 50 and gets the next free id:

POST /register  {"email": "ana@example.com", "password": "secret123", "name": "Ana Pop", "city": "Cluj", "role": "admin", "id": 1}
  →  201  {"city": "Cluj", "email": "ana@example.com", "id": 2, "name": "Ana Pop", "role": "customer"}
POST /register  {"email": "ben@example.com", "password": "tea4two!", "id": 50, "role": "admin"}
  →  201  {"city": null, "email": "ben@example.com", "id": 3, "name": null, "role": "customer"}
POST /login  {"email": "ana@example.com", "password": "secret123"}
  →  200  {"token": "<token>"}
GET /me  (Authorization: Bearer <token>)
  →  200  {"email": "ana@example.com", "id": 2, "role": "customer"}
POST /products  {"name": "Desk", "price": 120}  (Ana's token)
  →  403  {"error": "Admins only"}

Your task: add the columns name and city to the users table and write the allowlist SIGNUP_FIELDS. In the /register route, build newUser from that list, add the password's hash, save it with insertRow, and answer the stored row with 201.


Assignment
Follow the Coding Tutorial to close the mass assignment hole in POST /register with an allowlist.


Hint
Loop over SIGNUP_FIELDS, not over data: then the loop never even meets "role" or "id". Copy a field only if (field in data), so a missing name simply stays empty. Give newUser the type { [field: string]: any }, or TypeScript won't let you add keys to it.


STEP 1 OF 10
    Ctrl + Enter to check