An AI assistant wrote the shop's sign-up route. It answers 201 and looks fine, but look at what it saves:
@app.post("/signup")
def signup():
data = request.get_json()
cursor = db.execute("INSERT INTO users (email, password_hash) VALUES (?, ?)", (data["email"], data["password"]))
db.commit()
return jsonify({"id": cursor.lastrowid, "email": data["email"]}), 201
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 route, already written for you, checks passwords with check_password_hash, 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. generate_password_hash mixes in a random salt first, so two people with the same password get different hashes. check_password_hash tells you whether a password fits a saved hash:
from werkzeug.security import generate_password_hash, check_password_hash
stored = generate_password_hash("open sesame")
print(stored[:17]) # scrypt:32768:8:1$
print(check_password_hash(stored, "open sesame")) # True
print(check_password_hash(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": "scrypt:32768:8:1$<salt 1>$<hash 1>"},
{"email": "ben@example.com", "id": 2, "password_hash": "scrypt:32768:8:1$<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 signup. Save the email and generate_password_hash 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 signup so it stores a salted hash instead of the password.
Hint
The login route is already correct: it only needs a real hash in the column. generate_password_hash is already imported. Keep both the password and the hash out of the answer.
Free plan: 30 tutor messages. PRO is unlimited.
