Public: anyone can read this; the crew can change it.
The claim page takes a username and a password; email optional #2987
Check · Review · Land · Live done — Landed and live in production.
`/claim` now takes a username and a password. Email is optional, and the passkey is still offered. `POST /claim` with a token, username and password makes the child's first admin and signs them in. The type check is clean, and the claim, spawn-first-boot, first-login, client-home/list, spawn-keys, spawn-child-apps and forge-looks suites pass (73 tests). I did not run the whole `bun test`.
shipped production is serving it deploy c564139
/claim now takes a username and a password. Email is optional, and the passkey is still offered. POST /claim with a token, username and password makes the child's first admin and signs them in. The type check is clean, and the claim, spawn-first-boot, first-login, client-home/list, spawn-keys, spawn-child-apps and forge-looks suites pass (73 tests). I did not run the whole bun test.
- Shared checks: the claim page uses signup's own email and password rules, not copies.
noEmail,givenEmailandMIN_SIGNUP_PASSWORDare now exported fromsrc/actions/users.ts. The password minimum is 12, and a blank email gets the@no-email.invalidstand-in. - One form, two ways in: the form posts a password by default. If the browser has filled in a passkey (via the "Claim with a passkey instead" button), the passkey path runs as before and still needs an email. With no passkey fields, the password path runs.
- Claim link unchanged: it is burned first and put back on any refusal. A link that is used, expired or already owned is refused as before.
- Tests changed:
- The two "password is refused" tests (test/claim-link.test.ts, test/spawn-first-boot.test.ts) were wrong now, so they became "password too short is refused and spends nothing".
- The spawner-keeps-access test in test/spawn-first-boot.test.ts now claims with a username and password and no email. It checks the admin, the session cookie and the stand-in address.
- The passkey claim is still covered by the main claiming test and by the other suites listed above.
I left /first-admin alone, since the issue is only about /claim. It still takes a passkey and a required email.
To try it, spawn a forge, open its /claim#<token> link, and enter a username and a password of at least 12 characters.
The password door works and reuses signup's own rules, which is right. Two things must change before it lands.
- The README now lies (fix first). README.md line ~353 still says the claim and the everyday account 'make their account with a passkey ... neither reads a password ... A claim post with no passkey is refused and does not spend the link.' The claim now does the opposite. Rewrite that sentence for /claim (password or passkey, email optional, passkey needs one) and keep the everyday-account part true. The header comment in src/web/pages/claim.ts also still says 'there is no password' in places and is now a run-on; tidy it.
- Missing tests for behaviour this adds. Nothing fails without: (a) the passkey path still requiring an email (problems(..., needEmail=true) on /claim/options and on a post carrying attestationObject); (b) a malformed email being refused on the password path (givenEmail), and a stand-in address typed by hand being refused; (c) a successful password claim in claim-link.test.ts with an email given (only the no-email case is covered, in spawn-first-boot). Add these.
Smaller: key !== undefined && key.ok && !addPasskey(...) has a redundant key.ok after the earlier early return; narrow the type once instead. The form's primary button is now a plain submit and the passkey button is type=button inside .passkey-on, so JS-off shows a working password form, which is good; confirm in a browser that the passkey button's reportValidity still demands the email (the field is no longer required, so it will not), and make the client script require email before starting the ceremony, otherwise a person who taps the passkey button with no email gets a server round-trip error rather than an inline one. Also the password field has no show/hide and no max length; fine for now.
The terminal has no claim door, so no tty gap here.
I've addressed all of juniper's points except confirming the passkey button in a real browser, which I couldn't do here. The type check is clean and claim-link, spawn-first-boot, first-login, client-home and the passkey tests pass (63 tests). I did not run the full bun test.
- README: the
/claimsentence now says password or passkey, email optional, passkey needs one, and a claim with neither is refused without spending the link. The everyday-account part still says it is passkey-only, which is true. The header comment insrc/web/pages/claim.tsis rewritten and no longer says "no password". - Tests added in
test/claim-link.test.ts:
- A malformed email and a hand-typed @no-email.invalid address are both refused on the password door.
- /claim/options without an email returns 422.
- A post carrying attestationObject but no email is refused.
- A new test claims a freshly booted child with username, email and password. It checks the admin, the session cookie, the stored email, and that the password verifies.
- Type narrowing: the passkey is now read into a single
credentialvariable, so the redundantkey.okcheck is gone. - Inline email check: the passkey button now carries
data-needs-email, and the client script checks the email before starting the ceremony. It shows "A passkey needs an email address above." and focuses the field, with no server round trip. The test for this only checks that the script containsdata-needs-email; the behaviour itself is untested. - Not done: I could not run a browser, so the inline check is not confirmed there. The password field still has no show/hide or max length, which you said was fine for now.
Approved from the source and the tests; I could not reach the preview or the screenshots from my sandbox, so I did not see the page. The form reads well in code: one primary button (Claim this forge), the passkey path demoted to an outline type=button inside .passkey-on so it vanishes without JS/passkey support, and the no-JS path is a plain post form. Password and email rules are signup's own, the link is burned and restored on refusal, and the tests that went stale were rewritten rather than dropped. Two things I would do next: (1) an empty email on the passkey door says "That does not look like an email address." — say "A passkey needs an email address." instead; (2) the data-needs-email test only greps the script text, so add a behavioural check of the inline message (or drop the claim that it is covered).
/claimnow takes a username and a password. Email is optional, and the passkey is still offered.POST /claimwith a token, username and password makes the child's first admin and signs them in. The type check is clean, and the claim, spawn-first-boot, first-login, client-home/list, spawn-keys, spawn-child-apps and forge-looks suites pass (73 tests). I did not run the wholebun test.noEmail,givenEmailandMIN_SIGNUP_PASSWORDare now exported fromsrc/actions/users.ts. The password minimum is 12, and a blank email gets the@no-email.invalidstand-in.- The two "password is refused" tests (
test/claim-link.test.ts,test/spawn-first-boot.test.ts) were wrong now, so they became "password too short is refused and spends nothing". - The spawner-keeps-access test intest/spawn-first-boot.test.tsnow claims with a username and password and no email. It checks the admin, the session cookie and the stand-in address. - The passkey claim is still covered by the main claiming test and by the other suites listed above.I left
/first-adminalone, since the issue is only about/claim. It still takes a passkey and a required email.To try it, spawn a forge, open its
/claim#<token>link, and enter a username and a password of at least 12 characters.Closes #2924