feat(web): domain- and network-bound CLI-link challenges - #343
Merged
blockchain-maxis merged 2 commits intoSep 2, 2026
Merged
Conversation
…ign-in Both web sign-in (apps/web/lib/sep10.ts) and CLI linking would otherwise verify a SEP-10 challenge transaction using the same home domain and web_auth_domain. Without separation, a challenge signed to sign in to the website is also valid proof for attaching a deploy wallet, and a signature captured from either context becomes universally useful against the other. Add apps/web/lib/cli-link.ts: buildCliLinkChallenge/verifyCliLinkChallenge, built on the same @stellar/stellar-sdk WebAuth primitives sep10.ts uses, but with a distinct home domain (cli.<root-domain>). SEP-10's own domain check in WebAuth.readChallengeTx is what makes the two purposes reject each other automatically — no separate bookkeeping needed. Also binds the requested Stellar network into the challenge via assertNetworkMatches, rejecting (naming both networks) a request whose network doesn't match this deployment's configured one, before a challenge is ever built. Exposed at /api/cli-link (GET challenge, POST verify), mirroring the existing /api/auth/sep10 route's shape and rate limiting. This does not yet attach a verified wallet to a profile: doing that safely requires proving the caller is also authorized to modify the target handle's profile, which proof-of-possession of a deploy key alone does not establish. That's a separate mechanism, deliberately left for a follow-up rather than shipping a half-built authorization check. Tests cover the round trip, wrong-signer rejection, a web sign-in challenge rejected as link proof and vice versa (blockchain-maxis#269's acceptance), and network-match/mismatch (blockchain-maxis#263's acceptance).
✅ Deploy Preview for stellar-signet ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
@Mamavee001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@Mamavee001 is attempting to deploy a commit to the blockchainmaxis-8449's projects Team on Vercel. A member of the Team first needs to authorize it. |
blockchain-maxis#331 moved LIMITS out of rate-limit-http.ts into rate-limit-policy.ts (that module imports next/server, which the node test runner cannot resolve), so this branch's new cliLink bucket went to the policy module instead. Took main's rate-limit-http.ts unchanged. Prettier over the two new files. Verified the security property directly, not just via the suite: built a signed web sign-in challenge and a signed CLI-link challenge from the same keypair and cross-fed them. cli-link domain : cli.signet.dev sign-in domain : signet.dev web sign-in signature as link proof -> InvalidChallengeError CLI link signature as sign-in proof -> InvalidChallengeError each signature in its own context -> accepted 261/261 web tests pass; typecheck, lint and build clean.
blockchain-maxis
merged commit Sep 2, 2026
088107a
into
blockchain-maxis:main
10 of 11 checks passed
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.
closes #269
closes #263
Both issues turned out to be one cohesive change: a CLI-link challenge needs its own domain (#269) and its own network binding (#263), and both live naturally in the same new challenge-issuing module alongside the same tests, so this is one PR rather than two artificially split ones.
Summary
Both web sign-in (
apps/web/lib/sep10.ts) and CLI linking verify a SEP-10 challenge transaction, andsep10.tstreats the home domain as the web auth domain for a single-domain deployment. Without separation, a challenge signed to sign in to the website would also be valid proof for attaching a deploy wallet, and vice versa — a signature captured from either context becomes universally useful against the other. Separately, a testnet deploy key linked under a mainnet profile would present worthless testnet contracts as real career history.Changes
apps/web/lib/cli-link.ts(new):buildCliLinkChallenge/verifyCliLinkChallenge, built on the same@stellar/stellar-sdkWebAuthprimitivessep10.tsalready uses, but with a distinct home domain (cli.<root-domain>, vs.sep10.ts's plain root domain). SEP-10's own domain check insideWebAuth.readChallengeTxis what makes the two purposes reject each other automatically — no separate bookkeeping needed, and no risk of a hand-rolled check drifting from the spec's own enforcement.assertNetworkMatches(requestedNetwork): rejects — naming both networks — a CLI-requested network that doesn't match this deployment's configuredNEXT_PUBLIC_STELLAR_NETWORK, called before a challenge is ever built. The network passphrase is also a required argument to both building and verifying (it's baked into the transaction's network ID hash), so a challenge built for one network cannot be replayed as proof against a deployment configured for the other.apps/web/app/api/cli-link/route.ts(new):GET(challenge) /POST(verify), mirroring/api/auth/sep10's shape, CORS, and rate limiting (apps/web/lib/rate-limit-http.tsgains acliLinkbucket at the same 12/min assep10).apps/web/lib/cli-link.test.ts(new): round trip, wrong-signer rejection, no-signature rejection — plus the two tests each issue's acceptance criteria calls out by name: a web sign-in challenge rejected as CLI-link proof and vice versa (CLI link challenges need domain separation from the sign-in SEP-10 challenge #269), and network match/mismatch, including the naming-both-networks check (CLI must guard against linking a key across networks #263).Scoping note, read before wiring this up further: this does not attach a verified wallet to a profile. Doing that safely requires proving the caller is also authorized to modify the target handle's profile — proof of possessing a deploy key alone isn't authorization to attach it to someone else's handle. That's a separate authorization mechanism (likely: an already-established session/JWT for the profile owner, checked alongside this proof-of-possession), and neither issue's acceptance criteria asked for it. I'd rather flag the gap explicitly than ship a half-built check that looks complete.
Verification
pnpm --filter @signet/web typecheck/lint/test— all pass (217/217 tests, 10 new incli-link.test.ts).node scripts/check-docs.mjs— passes, no new env vars introduced (both new functions derive from the existingNEXT_PUBLIC_ROOT_DOMAIN/NEXT_PUBLIC_STELLAR_NETWORK).