Repository navigation
Require email verification before a booking is finalized - #2
Open
jeremymanning wants to merge 3 commits into
Open
jeremymanning wants to merge 3 commits into
jeremymanning wants to merge 3 commits into
Conversation
Fixtures used September 2026 dates; reconcile is scoped to future bookings, so once those dates elapsed the rows were skipped as history and four tests failed. Derive the fixture year from the current date instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP8VHeDuMhdC6865MY5VfR
Non-legitimate bookings (throwaway names/addresses, keyboard-mash notes, often booked then cancelled within a minute) were landing directly on the calendar, notifying the owner and making the owner's account send invites to arbitrary addresses. They looked human, so the honeypot and rate limits didn't catch them. createBooking now stores a pending row and emails the visitor a confirm link (confirm.html). Nothing reaches the calendar, the invitee or the owner until the visitor clicks 'Confirm booking'; confirmBooking then re-checks availability under the lock, creates the event and sends the normal confirmation emails. Pending rows don't hold the slot (first to verify wins), so unverified requests can't squat times. Links expire after VERIFY_LINK_MINUTES (60); REQUIRE_EMAIL_VERIFICATION=false restores immediate booking. Confirmation requires a click so mail link-scanners can't verify on the visitor's behalf. Also: share event creation / response shaping between create, confirm and reschedule; extract the token-page message renderer (PageMessage); fix cancel/confirm pages rendering raw ISO dates (TimezoneUtil was never initialised there) and misaligned action buttons. Refs #1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP8VHeDuMhdC6865MY5VfR
- A link confirmed more than GHOST_GRACE_MINUTES after the request produced a confirmed row whose createdAt was already stale, so its just-created (not yet visible) event read as deleted: a second visitor could confirm the same slot, and reconcile could cancel the live booking. createdAt is now reset when the event is created. - isRangeAvailable measured min-notice from the confirm click, so near-limit slots failed at confirm with a false 'someone else booked it'. It now takes the request time (start must still be in the future). - A failed verification send (or exhausted MailApp quota) still told the visitor to check their email. createBooking now returns EMAIL_UNAVAILABLE. - Persist the confirmed row before sending confirmation emails, so nobody gets cancel/reschedule links for a row that failed to save. Each fix has a regression test that fails on the previous commit. Refs #1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP8VHeDuMhdC6865MY5VfR
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1.
Summary
Non-legitimate bookings were landing straight on the calendar. They had throwaway names and addresses and keyboard-mash notes, and were often booked then cancelled within a minute. Each one also made the owner's account send an invite to an arbitrary address.
createBookingnow stores a pending row and emails the visitor a link toconfirm.html?token=…. There is no calendar event, invite or owner email until they click Confirm booking.confirmBookingre-checks availability under the lock, then creates the event and invite and sends the normal confirmation emails.VERIFY_LINK_MINUTES(default 60). SettingREQUIRE_EMAIL_VERIFICATION=falserestores immediate booking.createdAtis reset when a booking is confirmed, so the ghost guard can't open a double-booking.Deploy order
pending, so visitors still see the current confirmation.clasp push, and update the existing deployment so the web-app URL stays the same. Doing this before step 1 would make the old frontend show "Booking Confirmed!" for bookings that are only pending.Testing
npm test: all backend files pass (17 new email-verification tests) and 78/78 frontend tests pass. Each red-team fix has a test that fails on the pre-fix code..gshandlers (Google services stubbed) covered: submit → "check your email" → confirm page (load doesn't confirm) → click → confirmed → reload shows already confirmed. It also covered two visitors racing for the same slot, where the second sees "time no longer available". Screenshots were checked at desktop and 375px widths.🤖 Generated with Claude Code
https://claude.ai/code/session_01SP8VHeDuMhdC6865MY5VfR
Generated by Claude Code