Skip to content

feat(auth): mobile-first passwordless phone login — Part 2 (#944) - #1005

Merged
developer-ainative merged 11 commits into
mainfrom
feature/issue-944-phone-login-part2
Oct 7, 2026
Merged

developer-ainative merged 11 commits into
mainfrom
feature/issue-944-phone-login-part2

Conversation

@developer-ainative

Copy link
Copy Markdown
Contributor

Implements Builder-side Part 2 of #944 per docs/superpowers/specs/2026-10-06-mobile-phone-login-part2-design.md: two new API routes (register-phone, login-phone) proxying core's merged phone-identity endpoints, a phone-login next-auth provider modeled on the existing ainative-oauth provider, and a CSS-breakpoint-gated mobile-first phone form in Auth.tsx with a required email escape hatch.

A fresh-context whole-branch review (final commit in docs/superpowers/plans/2026-10-06-mobile-phone-login-part2.md's execution ledger) caught and this branch fixes:

  • Escape hatch was unreachable one direction (CSS hid the only control that could switch back)
  • Turnstile fail-closed on 100% of phone registrations (no widget was ever rendered/wired on this path)
  • email: null broke ~22 routes across the app that key account identity off session.user.email — switched to a synthetic email matching the existing guest-account convention
  • Dropped signup_source/ad-attribution ext forwarding required by the spec

Known blocker — do not merge until resolved

Core's /auth/register-phone and /auth/login-phone are merged to core main (core#8463, closing core#8459) but NOT live in production — core#8512 (open, owned by another team) tracks the deploy gap. This PR is built and unit-tested entirely against mocked fetch calls matching the documented/code-read contract; nothing here has been verified against the real network.

Before merge: re-run this against the live endpoint once core#8512 closes —

curl -X POST https://api.ainative.studio/api/v1/auth/register-phone -H "Content-Type: application/json" -d '{"phone":"+1...", "otp_code":"..."}'

— and do a real browser test of the mobile phone-form flow end-to-end (signup, login, and the NO_SUCH_PHONE_ACCOUNT fallback) before claiming this ships, per this repo's live-verification standard.

Deferred minors (not blocking, tracked for follow-up)

  • .m-auth-escape/.m-auth-error have no dedicated CSS — phone-path buttons/errors render unstyled
  • Raw core error strings surface on the phone submit path instead of friendly copy
  • register-phone's send-otp action has no rate limiting (unlike the sibling /api/build/register route) — real abuse surface unmeasurable until core#8512 closes
  • AINATIVE_API_URL vs AINATIVE_API_BASE_URL — two env vars for the same base URL used inconsistently across this feature's files

Full test suite: 522 files, 6650 tests passing, 50 skipped (pre-existing). tsc --noEmit clean.

AINative Admin added 11 commits October 6, 2026 12:28
Part 1 (core) is code-complete and merged (core#8463) but not yet live
in production (core#8512, tracked separately). This spec covers only
Builder-side Part 2: two new API routes, a phone-login next-auth
provider modeled on the existing ainative-oauth provider, and
CSS-breakpoint-gated mobile UI with a required email escape hatch.
…t review

Fixes four findings from the whole-branch review:

- Critical: the "Use phone instead" escape hatch was unreachable — it lived
  inside .m-auth-phone-form, which .m-auth-force-email hides entirely,
  stranding a founder who tapped "Use email instead" with no way back.
  Moved both escape-hatch controls to a sibling element neither force
  class ever hides.

- Critical: register-phone's Turnstile gate fail-closed on literally every
  phone signup in production, since no widget was ever rendered on that
  path and no token was ever sent. Added the widget to the phone-first
  form and wired its token through to the register call.

- Critical: authorizePhoneLogin set email:null, which broke ~22
  call sites across the app that key account identity off
  session.user.email with a plain `if (!email) return 401` check — a
  phone founder's session would authenticate and then 401 on every
  subsequent request. Switched to a synthetic, stable email
  (phone-<id>@phone.ainative.studio), mirroring the existing
  guest-account convention so none of those call sites need to change.

- Important: register-phone silently dropped the spec's explicit
  requirement to forward signup_source:'builder' + ad-attribution ext
  (gclid/utm), which would have broken paid-conversion attribution and
  the card-free keyless bypass for every phone signup.
@developer-ainative
developer-ainative merged commit 236a18e into main Oct 7, 2026
1 check passed
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