Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Web3 Connect Laravel Package

karhal/web3-connect

Karhal Web3 Connect brings crypto wallet authentication to Laravel. Let users sign in with MetaMask, WalletConnect, and other Web3 wallets, verify signatures server-side, and link wallet addresses to existing accounts—no passwords required.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package, and the repository is archived. Treat it as a reference implementation to mine for the wallet-login flow rather than something you composer require into a Laravel app. The realistic day-one move is to read the source, lift the challenge/signature logic, and re-implement it behind a Laravel guard.

Minimal path:

  1. Read composer.json first. Confirm the PHP version, the signature-verification dependency (typically kornrunner/keccak plus an elliptic-curve or web3.php-style library), and the Symfony version constraints. This tells you whether the crypto layer is portable or entangled with Symfony services.
  2. Find the authenticator/controller. In a Symfony bundle this is usually a Security\Authenticator, a login controller, or a GuardAuthenticator. That file contains the entire flow: nonce issuance, message construction, signature verification, and user lookup.
  3. Identify the message format. Look for the exact string the server asks the wallet to sign (personal_sign / EIP-191) and whether it follows EIP-4361 (Sign-In with Ethereum). If it's a custom format, note the fields — address, nonce, domain, statement, chain ID, issued-at.
  4. Find where the nonce lives. Session, cache, or database. This is the piece you'll swap for Laravel's Cache or session store.
  5. Find the recovery step. The server must derive the address from the signature and compare it to the claimed address. If the bundle instead trusts a client-supplied address, that's a red flag to fix in your port.

First use case to target: "Connect Wallet → Sign → Authenticate." The user clicks connect, your app issues a single-use nonce, the wallet signs a human-readable message, the server recovers the address, and you log in (or create) the matching user. That is the whole product; everything else is hardening.

Implementation Patterns

Challenge/response is the core loop. Generate a cryptographically random nonce, store it server-side with a short TTL (2–5 minutes), and bind it to the session or to the claimed address. On the callback, consume it atomically — delete-on-read — so a captured signature can't be replayed.

Separate the three flows. They look similar but have different security requirements:

  • Login — address maps to an existing user; reject unknown wallets unless you allow auto-registration.
  • Register — address creates a new user; still require the signature before insert.
  • Link — an already-authenticated user attaches a wallet; require a fresh nonce even though they're logged in.

Model it as a Laravel guard. A viaRequest guard or a dedicated WalletAuthController that calls Auth::login($user) keeps it idiomatic. Register a wallet guard in config/auth.php, resolve the user by a wallet_address column, and let middleware do the rest. Don't bolt this onto the existing session guard.

Server-side verification is non-negotiable. The client sends {address, signature, nonce}. The server rebuilds the exact message it issued, recovers the signer, and compares checksummed addresses. Never accept "the wallet told me it's 0xabc…" as proof. In Laravel this is a small WalletSignatureVerifier service wrapping kornrunner/keccak and an ecrecover implementation — the same primitives the bundle uses, minus the Symfony wiring.

Adopt EIP-4361 (SIWE) message formatting even if the bundle uses something custom. Wallets, libraries, and future-you all understand it. The message includes domain, address, statement, URI, nonce, chain ID, and timestamps — which gives you replay protection for free if you validate domain and chain ID on the server.

Store the nonce where you can expire it. Laravel's cache with a put($key, $nonce, now()->addMinutes(5)) and pull() on verification is cleaner than a database table. Key it by session ID or by a server-issued challenge ID returned to the client.

Testing with deterministic keys. Use Hardhat/Anvil's well-known private keys and precomputed signatures in feature tests. Fix the nonce in the test, assert the recovered address, and assert a second use of the same signature fails. This is the only reliable way to catch encoding bugs.

Extension points to design in from the start: a chain allowlist (reject signatures from unexpected chainId), a pluggable verifier interface (so you can add EIP-1271 contract-wallet support later), and events like WalletLinked / WalletAuthenticated so you can hook in audit logging.

Gotchas and Tips

  • The archive status is the headline. No security patches, no dependency updates, no upstream fixes. The crypto libraries it depends on move; pinned versions will rot. Budget for owning this code.
  • License is NOASSERTION. There is no clear grant to copy or redistribute. Get a legal read before lifting source verbatim; re-implementing the flow from the published standard (EIP-4361/EIP-191) is safer.
  • Symfony idioms don't translate. services.yaml, bundle registration, and GuardAuthenticator have no Laravel equivalent. Port the logic, not the structure.
  • Address casing will bite you. Ethereum addresses are checksummed (mixed case). Normalize to lowercase for storage and comparison, or use strict checksum validation — never do a naive === on raw input.
  • personal_sign vs eth_sign. Wallets prefix the message differently (EIP-191). If you rebuild the message incorrectly by one byte, recovery silently yields the wrong address. Log the exact message hash when debugging.
  • Replay and domain binding. A nonce without domain and chain-ID binding can be replayed across environments or chains. Include both in the signed message and validate both server-side.
  • Contract wallets (EIP-1271) won't verify with ecrecover. Smart-contract wallets (Safe, Argent) return a boolean from isValidSignature instead of an ECDSA signature. Plain recovery will reject them. Decide early whether you support them; if yes, plan an on-chain call or a verifier abstraction.
  • Signature malleability and v values. Some libraries return v as 27/28, others as 0/1. Normalize before recovery or you'll get sporadic failures that only show up with certain wallets.
  • Rate-limit the nonce endpoint. It's cheap to spam and each nonce is a cache write. Throttle per IP and per session.
  • CSRF and session binding. The signature callback is a state-changing POST. Keep CSRF protection on, and bind the nonce to the session so a signature obtained in one browser can't complete a login in another.
  • Debugging recipe: log the recovered address, the claimed address, the message string, and the message hash side by side. Nine times out of ten the mismatch is message encoding (hex vs UTF-8) or a missing \x19Ethereum Signed Message:\n prefix.
  • Chain ID drift. Wallets switch networks mid-flow. Capture chainId at signature time and validate it against your allowlist rather than trusting the current connection.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings