Repository navigation
feat(leads): let the user answer a seed login's verification code - #137
Merged
Merged
Conversation
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>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
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.
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.tsalready uses for browser-automated social posts. ItsdetectCodeChallengeandhandleCodeChallengehave no database coupling, so they're reused unchanged — only the waiter needed rebinding fromsp_postto 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 onoutreach_seed_credentials, mirroring whatsp_postalready has. Applied to prod.Checks
tsc --noEmitclean🤖 Generated with Claude Code