An AI assistant wrote the shop's sign-up handler. It answers 201 and looks fine, but look at what it saves:
app.HandleFunc("POST /signup", func(w http.ResponseWriter, r *http.Request) {
var data map[string]string
json.NewDecoder(r.Body).Decode(&data)
res, _ := db.Exec("INSERT INTO users (email, password_hash) VALUES (?, ?)", data["email"], data["password"])
id, _ := res.LastInsertId()
writeJSON(w, 201, map[string]any{"id": id, "email": data["email"]})
})
The column is called password_hash, but it holds the password itself. Anyone who sees the database (a backup, a leak) reads every password, and people reuse passwords on other sites. It also breaks the login: the shop's /login handler, already written for you, checks passwords with checkPassword, which expects a hash. With the AI's sign-up, every login answers 401.
A hash is a one-way scramble: the password gives the hash, but the hash never gives the password back. The file already has two helpers. hashPassword mixes in a random salt first, so two people with the same password get different hashes, and returns the text sha256$salt$hash. checkPassword tells you whether a password fits a saved hash:
stored := hashPassword("open sesame")
fmt.Println(stored[:7]) // sha256$
fmt.Println(checkPassword(stored, "open sesame")) // true
fmt.Println(checkPassword(stored, "Open sesame")) // false
The requests and the answers your app must give. GET /check/users is a checking route the course adds: it shows the users table with each salt and hash replaced by a label like <salt 1>.
POST /signup {"email": "ana@example.com", "password": "secret123"} → 201 {"email": "ana@example.com", "id": 1}
POST /signup {"email": "ben@example.com", "password": "secret123"} → 201 {"email": "ben@example.com", "id": 2}
GET /check/users → 200 [{"email": "ana@example.com", "id": 1, "password_hash": "sha256$<salt 1>$<hash 1>"},
{"email": "ben@example.com", "id": 2, "password_hash": "sha256$<salt 2>$<hash 2>"}]
POST /login {"email": "ana@example.com", "password": "secret123"} → 200 {"email": "ana@example.com", "id": 1}
POST /login {"email": "ana@example.com", "password": "Secret123"} → 401 {"error": "Wrong email or password"}
POST /login {"email": "ben@example.com", "password": "secret123"} → 200 {"email": "ben@example.com", "id": 2}
Your task: rewrite the /signup handler. Save the email and hashPassword of the password, never the password itself, and answer 201 with the new user's id and email.
Assignment
Follow the Coding Tutorial to rewrite the /signup handler so it stores a salted hash instead of the password.
Hint
The /login handler is already correct: it only needs a real hash in the column. hashPassword is already in the file, above newApp. Keep both the password and the hash out of the answer.
Free plan: 30 tutor messages. PRO is unlimited.
