Sign-up stores a hash of each password. Logging in is the other half: the user sends their email and password, and the shop checks them. It's a POST, so the password travels in the body, not in a URL that ends up in logs and in the browser's history.
Checking a password: you can't undo a hash, but you can hash the typed password again with the same salt and compare. The helper CheckPasswordHash(passwordHash, password), already above the routes, does that and gives true or false. For example, with another password:
var stored = GeneratePasswordHash("tulip7");
Console.WriteLine(CheckPasswordHash(stored, "tulip7")); // True
Console.WriteLine(CheckPasswordHash(stored, "tulip8")); // False
When the email has no account or the password is wrong, answer 401 with one vague message for both. Two different messages would let anyone find out which emails have an account. After two sign-ups, the test requests get:
POST /register {"email": "ana@example.com", "password": "secret123"} → 201 {"email": "ana@example.com", "id": 1}
POST /register {"email": "ben@example.com", "password": "tea4two"} → 201 {"email": "ben@example.com", "id": 2}
POST /login {"email": "ana@example.com", "password": "secret123"} → 200 {"email": "ana@example.com", "id": 1}
POST /login {"email": "ana@example.com", "password": "secret999"} → 401 {"error": "Wrong email or password"}
POST /login {"email": "nobody@example.com", "password": "secret123"} → 401 {"error": "Wrong email or password"}
POST /login {"email": "ben@example.com", "password": "secret123"} → 401 {"error": "Wrong email or password"}
POST /login {"email": "ben@example.com", "password": "tea4two"} → 200 {"email": "ben@example.com", "id": 2}
Your task: add POST /login with app.MapPost. Find the user by email with a reader, answer {"error": "Wrong email or password"} with 401 when user.Read() finds no row or the password doesn't match the stored hash, and otherwise answer the user's "id" and "email" with 200.
Assignment
Follow the Coding Tutorial to build the /login route.
Hint
Test for a missing row first and join the two failures with ||: when !user.Read() is true, C# never reads user["password_hash"]. Read() also moves the reader onto the row, so it must come before the hash is read. Once the if returns a 401, C# wants a return after it too, so the file compiles again only with the last line.
Free plan: 30 tutor messages. PRO is unlimited.
