Skip to content

feat(leads): let the user answer a seed login's verification code - #137

Merged
ralyodio merged 1 commit into
masterfrom
feat/seed-login-verification-code
Jul 28, 2026
Merged

ralyodio merged 1 commit into
masterfrom
feat/seed-login-verification-code

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

A gated directory interrupts a sign-in with "enter the six-digit code we just sent you", and #130 reported that as unanswerable.

That was wrong, and the distinction matters: the server can't answer it — and shouldn't be able to, since the whole point of the code is that it reaches the account's owner. But the owner is right there.

How it works

The browser session is held open on the challenge page, the prompt is surfaced in the Seed logins panel, and the code the user types is entered into that same live form. The sign-in carries on from there.

This is exactly the pattern lib/sp/verificationChallenge.ts already uses for browser-automated social posts. Its detectCodeChallenge and handleCodeChallenge have no database coupling, so they're reused unchanged — only the waiter needed rebinding from sp_post to the credential row.

The code is not a secret we keep

Written to a column, read once by the waiter, and cleared in the same breath so it can't be replayed. A timeout hands the browser slot back rather than pinning it, and clears the prompt so the UI stops claiming something is waiting.

Honest failure modes preserved

A challenge that isn't a code — a device approval, a captcha — still reports as what it is, now distinguished from "a code was asked for and nobody was there to enter it".

Migration

20260728040000_seed_credential_verification.sql — three columns on outreach_seed_credentials, mirroring what sp_post already has. Applied to prod.

Checks

  • tsc --noEmit clean
  • 751/751 tests pass
  • production build compiles

🤖 Generated with Claude Code

A gated directory interrupts a sign-in with "enter the six-digit code we
just sent you", and that was reported as unanswerable. It is not: the
server cannot answer it, and should not be able to, because the point of
the code is that it reaches the account's owner. But the owner is right
there.

So the browser session is held open on the challenge page, the prompt is
surfaced in the Seed logins panel, and the code the user types is
entered into that same live form. The sign-in then carries on.

This is the pattern lib/sp/verificationChallenge.ts already uses for
browser-automated social posts, and its detector and handler are generic
enough to reuse unchanged — only the waiter needed rebinding from sp_post
to the credential row.

The code is never a stored secret. It is written to a column, read once
by the waiter, and cleared in the same breath so it cannot be replayed.
A timeout hands the browser slot back rather than pinning it, and clears
the prompt so the UI stops claiming something is waiting.

A challenge that is not a code — a device approval, a captcha — still
reports as what it is, now distinguished from "nobody was there to
answer".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 5e8b618 into master Jul 28, 2026
8 checks passed
@ralyodio
ralyodio deleted the feat/seed-login-verification-code branch July 28, 2026 03:30
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