Skip to content

Replace simple wallet lookup with cryptographic proof-of-membership verification #173

Description

@Lakes41

Replace simple wallet lookup with cryptographic proof-of-membership verification

Difficulty: Expert
Type: Security

Background: app/api/verify/route.ts's live-mode path calls client.verifyWallet(discordUserId, wallet), which — based on IntegrationClient's contract — presumably performs a lookup-style verification (does this Discord user's claimed wallet exist and hold a valid membership?) rather than a cryptographic proof that the requester actually controls the claimed wallet.

Problem: A lookup-style verification is vulnerable to a user simply claiming someone else's public wallet address (all Ethereum addresses are public) to fraudulently gain access to that wallet's associated roles/passes, since nothing in the flow proves the requester — not just the address — controls the private key.

Expected outcome: /api/verify (and the underlying IntegrationClient.verifyWallet contract) is upgraded to a genuine challenge-response cryptographic proof: the server issues a single-use nonce/challenge, the client signs it with the claimed wallet's private key (e.g. via the same wallet-connection flow as #38's SIWE work), and only a valid signature over that specific challenge is accepted as proof of control — closing the address-spoofing gap.

Suggested implementation:

  • Add a nonce-issuance step to the verification flow (reusable with feat: implement production-ready webhook verification utilities #38's SIWE nonce infrastructure if implemented, or standalone otherwise), tied to the specific (discordUserId, wallet) pair being verified to prevent cross-context replay.
  • Update /api/verify/route.ts to require and cryptographically verify a signature over the issued challenge (using viem's signature verification utilities) before calling through to IntegrationClient/recording a verified membership.
  • Update the Discord bot's verification command flow (wherever it currently prompts a user to submit a wallet) to guide the user through signing the challenge, not just pasting an address.
  • Ensure nonces are single-use, scoped, and time-limited, with replay attempts explicitly rejected and logged.

Acceptance criteria:

  • A verification request with a claimed wallet but no valid signature over the issued challenge is rejected.
  • A verification request with a valid signature over the correct challenge for that (discordUserId, wallet) pair succeeds.
  • A replayed or cross-context signature (valid for a different challenge or different Discord user) is rejected.
  • verify.test.ts/live-verify.test.ts are extended to cover unsigned, wrongly-signed, and replayed-signature scenarios.

Likely affected files: apps/dashboard/app/api/verify/route.ts, packages/integration-client/src/client.ts, apps/discord-bot/src/index.ts, apps/dashboard/test/verify.test.ts

Labels: security, feature, help wanted, priority: high, GrantFox OSS, Maybe Rewarded, Official Campaign | FWC26


Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSGrantFox Open Source Sponsorship program tagMaybe RewardedIssue may qualify for a reward upon successful completion per campaign rulesOfficial Campaign | FWC26Official FWC26 campaign issue — eligible for campaign scoring and rewardsfeatureNew feature, enhancement, or functional additionhelp wantedExtra attention is neededpriority: highpriority: highsecuritySecurity-related fix, hardening, audit, or vulnerability remediation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions