Skip to content

fix: roll back the user row when the signup OTP email fails to send - #208

Merged
Emmyt24 merged 1 commit into
mainfrom
fix/signup-otp-rollback
Aug 7, 2026
Merged

fix: roll back the user row when the signup OTP email fails to send#208
Emmyt24 merged 1 commit into
mainfrom
fix/signup-otp-rollback

Conversation

@Emmyt24

@Emmyt24 Emmyt24 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • signup created the users row before confirming the OTP email actually sent. When Resend rejects the send (unverified domain, sandbox recipient restriction, bad key, etc.), the account was left permanently stuck and unverifiable — every retry with that email hit "email already registered" with no way to ever get a working code.
  • This surfaced in production: EMAIL_FROM_ADDRESS is currently the Resend sandbox address (onboarding@resend.dev) while octohq.org finishes domain verification, and the sandbox only allows sending to the account owner's own address — so any other signup email failed to send and got stuck.
  • Fix: if the OTP send fails, delete the just-created row so the email is free to retry cleanly. Added EmailSender::new_failing() (test-fixtures only) to simulate the failure in a new regression test.

Test plan

  • New test signup_rolls_back_the_user_row_when_the_otp_email_fails_to_send — confirms the row is deleted and the same email can be retried.
  • cargo test --workspace --locked — all passing
  • cargo clippy --workspace --all-targets --locked -- -D warnings — clean
  • cargo fmt --all -- --check — clean

signup previously created the user row before confirming the OTP email
actually went out. When Resend rejects the send (unverified domain,
sandbox recipient restriction, bad key, etc.), that left a permanently
stuck, unverifiable account occupying the email — every retry hit
"email already registered" with no way to ever get a working code.

Now a failed send deletes the just-created row so the email is free
to retry signup cleanly. Adds EmailSender::new_failing() (test-fixtures
only) to simulate the failure in a new regression test.
@Emmyt24
Emmyt24 merged commit 413c4c4 into main Aug 7, 2026
1 check passed
phalap1 added a commit to phalap1/Octo-Protocol that referenced this pull request Aug 7, 2026
Picks up the email OTP work (Octo-Protocol-org#207, Octo-Protocol-org#208, Octo-Protocol-org#209). One real conflict, plus
six call sites the automatic merge left compiling against the old
signatures.

Conflict:
- withdraw_refuses_to_sign_for_a_client_custody_wallet called
  auth_token(&app); main changed the helper to auth_token(&app, &state)
  because signup now issues its token only after the emailed code is
  consumed. Took main's form.

Fixed while merging, all of it mechanical:
- five more auth_token(&app) call sites in the withdraw idempotency tests
  merged without markers, since main never saw those tests. Two of them
  moved state into build_router, so they now clone it like every other
  test in the file.
- withdraw_preflight_tests built AppState with five arguments; main added
  the EmailSender parameter. Uses EmailSender::new_captured(), same as the
  other test fixtures.
- withdraw_preflight_tests signed up by hand and read data.token off the
  response. That field is gone: signup returns data.user_id and the token
  comes from /v1/auth/verify-email. Now goes through
  common::signup_and_verify_full.

The withdrawal OTP main added sits on its own routes, /withdraw/request-otp
and /withdraw/confirm, and gates the already-signed XDR path in submit.rs.
It does not touch POST /v1/wallets/:id/withdraw, so the pre-flight balance
and minimum-reserve checks are unchanged.

cargo clippy --workspace --all-targets and cargo fmt --all are clean.
cargo test -p octo-api runs 153 tests against Postgres with no failures and
no skips, including the three pre-flight tests from Octo-Protocol-org#108.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant