Homework

Homework — attack the token, break the transaction

Expand the app. Add invitations to your app if it has a "share with someone who has no account" flow — otherwise work on SignFlow.

  1. Attack the token, find nothing to attack. Copy a signing link and mangle the token — change a character, truncate it. The route resolves the token and finds no row (the page itself arrives next lesson). Now look at the request line: there is no document id in it. Write two sentences contrasting this with L9's delete, where a document id is in the URL and must be owner-checked. Which bug class does each design admit?

  2. Break the transaction on purpose. Temporarily move MarkDocumentSent to run before the signer loop, then force the second CreateSigner to fail by inviting a duplicate email (the UNIQUE (document_id, email) constraint rejects it). Confirm the document did not flip to sent and no signer rows were committed. Restore the order. That is what the single transaction buys.

  3. De-dup and validate. Invite the same address twice in one textarea, plus one garbage string (not-an-email). Confirm parseEmails collapses the duplicate and drops the invalid one, and only the valid, unique addresses become signers.

  4. Why hash the token? The signers table stores token_hash, not the token. Suppose it stored the raw token and your database backup leaked. What could the holder of that backup do? Now explain why storing the hash makes the backup useless — the same argument as sessions and resets, a third time.

Next lesson lights up the link: the signer opens their page, reviews the document, and signs — and you meet the honesty caveat that defines what this signature is and is not.