Skip to content

fix(auth): /verify dead-end on cold visit and broken signup OTP flow #35

@omarelkhal

Description

@omarelkhal

Bug: /verify?fromSignup=true is a dead-end without signup context; signup OTP flow is broken

Summary

The email verification page assumes the user always arrives from register/login in the same tab. Direct navigation (bookmark, pasted URL, new tab) renders an OTP form with no email and no recovery path. Separately, the post-signup OTP path never sends a code on load and verifies with the wrong Better Auth API.

Environment

  1. bun install in repo root
  2. Copy .env.example.env; set DATABASE_URL, run bunx drizzle-kit push
  3. Set EMAIL_PROVIDER=log (OTP prints in dev server terminal)
  4. For full signup enforcement, set EMAIL_VERIFICATION_ENABLED=true (see note below on env parsing)
  5. bun devhttp://localhost:3000

Steps to reproduce

A) Cold visit (no redirect)

  1. Open a new tab / incognito window
  2. Go to http://localhost:3000/verify?fromSignup=true
  3. Wait a few seconds

Actual: Page stays on /verify, shows “Verify Your Email” and “sent to your email”, empty OTP inputs, disabled Verify button.

Expected: Redirect to /register (or /login) when there is no pending verification email in sessionStorage and no unverified session email.

B) Signup OTP never auto-sends

  1. Register at /register with a new email
  2. Land on /verify?fromSignup=true without clicking Resend

Actual: No OTP is sent. use-verification.ts only flips isSendingInitialOtp and never calls sendVerificationOtp.

Expected: OTP sent automatically using type email-verification.

C) Wrong verify API after signup

  1. Complete signup, click Resend on /verify?fromSignup=true
  2. Enter the OTP from terminal / DB (email-verification-otp-* vs sign-in-otp-*)

Actual: verifyCode() always calls client.signIn.emailOtp. Signup sends email-verification OTPs, which require client.emailOtp.verifyEmail.

Expected: When fromSignup=true, call emailOtp.verifyEmail; otherwise signIn.emailOtp.

Root cause

  • Verify page has no guard when sessionStorage.verificationEmail is missing
  • Initial OTP send effect was never implemented (only a flag)
  • Resend hardcodes OTP type sign-in regardless of fromSignup
  • “Back to signup” links to /signup but ShipFree route is /register

Suggested fix

See PR branch fix/verify-page-guards:

  • Redirect to /register when verification context is missing (client) and to /dashboard when already verified (server)
  • Send OTP on load with correct type from fromSignup query param
  • Use emailOtp.verifyEmail for signup verification
  • Fix back link to /register

Manual verification (after fix)

  1. Cold /verify?fromSignup=true → redirects to /register
  2. Register → /verify shows your email, OTP auto-sent (terminal log)
  3. Enter OTP → redirects to /dashboard with verified session

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions