Skip to main content
Pylon ships a complete EIP-4361 (Sign-In With Ethereum) implementation in the binary. Users prove ownership of their Ethereum address by signing a structured message. Pylon recovers the address from the signature and mints a session. You do not need Magic.link, a WalletConnect server, or a third-party SIWE service.

What it does

  1. Frontend asks Pylon for a fresh nonce for the user’s address.
  2. Frontend builds an EIP-4361 message (Sign in to acme.com\n\nAddress: 0x...\n...nonce: ...\n...) and asks the user’s wallet to sign it via personal_sign.
  3. Pylon recovers the address from the signature using secp256k1 ECDSA and Keccak-256 (Ethereum’s variant). It validates the message’s domain, nonce, and expiry, then mints a session keyed on the wallet.

Endpoints

Schema

The user entity needs a walletAddress field:
The unique constraint stops two accounts from claiming the same address.

Sign-in flow

1. Request a nonce

Response:
The nonce is bound to the address. Using a nonce minted for one address against a different address fails verification. The nonce is single-use and short-lived. Errors:

2. Build and sign the message

The frontend constructs an EIP-4361 message and asks the wallet to sign it (MetaMask, WalletConnect, Coinbase Wallet, etc.):

3. Verify

Response on success:
displayName is optional. Pylon uses it only when it creates a new User row for a first-time address. The default is the short form 0x742d…beb0. Errors:

Verification details

Pylon validates:
  • Signature recovery: secp256k1 ECDSA and a Keccak-256 hash of the EIP-191 prefix (\x19Ethereum Signed Message:\n<len>) followed by the message recover a 20-byte address. It must match message.address, case-insensitive.
  • Nonce: the nonce must exist in the nonce store, bind to message.address, and be single-use. Pylon consumes it on first verify.
  • Domain: message.domain must match the request’s Host header. This stops replay across deployments.
  • Issued and expiration: if message.expirationTime is set, it must be in the future. If message.notBefore is set, it must be in the past.
Pylon lowercases the recovered address before lookup and storage. This makes checksummed 0x742d35Cc... and lowercase 0x742d35cc... addresses resolve to the same User row.

Security guarantees

  • secp256k1 and Keccak-256: Pylon uses the k256 crate, the same audited primitives Ethereum uses.
  • Nonce binding to address: using nonce-for-A to sign in as B fails verification.
  • Single-use nonces: Pylon consumes each nonce on first verify, regardless of success.
  • Domain pinning: Pylon validates message.domain against the Host header and rejects replay across deployments.
  • Expiration enforcement: Pylon honors expirationTime and notBefore.
  • EIP-191 prefix: Pylon applies it correctly, which defeats signature reuse from any other Ethereum-signed payload.

Where to go next

  • Sessions: what /siwe/verify mints.
  • OAuth: other identity providers for the same user. Link multiple sign-in methods to one account.