Skip to content

feat(web): domain- and network-bound CLI-link challenges - #343

Merged
blockchain-maxis merged 2 commits into
blockchain-maxis:mainfrom
Mamavee001:feat/cli-link-challenge-domain-separation
Sep 2, 2026
Merged

feat(web): domain- and network-bound CLI-link challenges#343
blockchain-maxis merged 2 commits into
blockchain-maxis:mainfrom
Mamavee001:feat/cli-link-challenge-domain-separation

Conversation

@Mamavee001

Copy link
Copy Markdown
Contributor

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, and sep10.ts treats 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-sdk WebAuth primitives sep10.ts already uses, but with a distinct home domain (cli.<root-domain>, vs. sep10.ts's plain root domain). SEP-10's own domain check inside WebAuth.readChallengeTx is 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 configured NEXT_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.ts gains a cliLink bucket at the same 12/min as sep10).
  • 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 in cli-link.test.ts).
  • node scripts/check-docs.mjs — passes, no new env vars introduced (both new functions derive from the existing NEXT_PUBLIC_ROOT_DOMAIN / NEXT_PUBLIC_STELLAR_NETWORK).

…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).
@netlify

netlify Bot commented Aug 30, 2026

Copy link
Copy Markdown

Deploy Preview for stellar-signet ready!

Name Link
🔨 Latest commit 63ff96f
🔍 Latest deploy log https://app.netlify.com/projects/stellar-signet/deploys/6a9807ee2af0e600088b4dd0
😎 Deploy Preview https://deploy-preview-343--stellar-signet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

@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
blockchain-maxis merged commit 088107a into blockchain-maxis:main Sep 2, 2026
10 of 11 checks 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.

CLI link challenges need domain separation from the sign-in SEP-10 challenge CLI must guard against linking a key across networks

2 participants