Skip to main content

Try it Live

Run SIWE examples in the interactive playground
New to SIWE? Start with Fundamentals for guided examples and authentication concepts.

Type Definition

Sign-In with Ethereum (EIP-4361) implementation for decentralized authentication. Creates structured messages users sign with private keys, proving address ownership without key exposure.

Authentication Flow

┌──────────────┐      ┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   Frontend   │      │   Wallet    │      │   Backend   │      │   Database  │
└──────────────┘      └─────────────┘      └─────────────┘      └─────────────┘
      │                     │                     │                     │
      │  1. Request Nonce   │                     │                     │
      ├────────────────────────────────────────>  │                     │
      │                     │                     │                     │
      │                     │             Generate nonce │              │
      │                     │             Store for 5min │──────────>  │
      │  2. Get nonce       │                     │                     │
      │  <────────────────────────────────────────┤                     │
      │                     │                     │                     │
      │ 3. Create message   │                     │                     │
      │    with nonce       │                     │                     │
      ├────────────────────>│                     │                     │
      │                     │                     │                     │
      │  4. Sign message    │                     │                     │
      │  <────────────────┤ │                     │                     │
      │                     │ signature           │                     │
      │  5. Verify message  │                     │                     │
      ├────────────────────────────────────────>  │                     │
      │                     │                     │                     │
      │                     │             Check nonce │                │
      │                     │             Verify sig   │──────────>   │
      │                     │             Check valid  │  <──────────┤
      │                     │             Create token │              │
      │  6. Session token   │                     │                     │
      │  <────────────────────────────────────────┤                     │
      │                     │                     │                     │
      └──────────────┘      └─────────────┘      └─────────────┘      └─────────────┘

Message Structure

A SIWE message is a human-readable text format combining user intent with cryptographic proof:
example.com wants you to sign in with your Ethereum account:
0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e

Sign in to Example App

URI: https://example.com/login
Version: 1
Chain ID: 1
Nonce: testnonce123
Issued At: 2024-11-06T12:00:00.000Z
Expiration Time: 2024-11-06T13:00:00.000Z
Not Before: 2024-11-06T11:00:00.000Z

Resources:
- https://example.com/api/user
- https://example.com/api/profile
Key components:
  • Domain (RFC 4501): Where the message is being signed for
  • Address: Which account is signing
  • URI: What resource is being accessed
  • Statement: Human-readable context
  • Chain ID: Which blockchain
  • Nonce: One-time value, prevents replay attacks
  • Timestamps: Validity window
  • Resources: Optional list of what’s being granted access to

Factory

Siwe(params: MessageParams): Siwe
Creates Siwe message instance from parameters. Parameters:
  • params.domain: RFC 4501 dns authority
  • params.address: AddressType performing signing
  • params.uri: RFC 3986 URI
  • params.chainId: EIP-155 Chain ID
  • params.statement?: Human-readable assertion (optional)
  • params.expirationTime?: ISO 8601 expiration (optional)
  • params.notBefore?: ISO 8601 valid-from time (optional)
  • params.requestId?: System identifier (optional)
  • params.resources?: Resource URIs (optional)
  • params.nonce?: Custom nonce (auto-generated if omitted)
  • params.issuedAt?: Custom timestamp (current time if omitted)
Returns: Siwe message instance

Static Constructors

Siwe.create(params)

create(params: MessageParams): BrandedMessage
Create message with defaults. Same as factory.

Siwe.parse(text)

parse(text: string): BrandedMessage
Parse EIP-4361 formatted string to message. Throws: Error if format invalid

Static Utilities

Formatting

Siwe.format(message)

format(message: BrandedMessage): string
Format message to EIP-4361 string for signing.

Validation

Siwe.validate(message, options?)

validate(message: BrandedMessage, options?: { now?: Date }): ValidationResult
Validate message structure and timestamps. Returns: { valid: true } or { valid: false, error: ValidationError }

Signing & Verification

Siwe.getMessageHash(message)

getMessageHash(message: BrandedMessage): Uint8Array
Get EIP-191 personal sign message hash (32 bytes).

Siwe.verify(message, signature)

verify(message: BrandedMessage, signature: Signature): boolean
Verify signature matches message address. Parameters:
  • signature: 65-byte signature (r + s + v)
Returns: true if valid

Siwe.verifyMessage(message, signature, options?)

verifyMessage(message: BrandedMessage, signature: Signature, options?: { now?: Date }): ValidationResult
Combined validation and verification.

Nonce Generation

Siwe.generateNonce(length?)

generateNonce(length?: number): string
Generate cryptographically secure random nonce. Parameters:
  • length: Nonce length (default 11, min 8)
Returns: Base62 alphanumeric string

Instance Methods

All static utilities available as instance methods:
const message = Siwe.create({
  domain: "example.com",
  address: userAddress,
  uri: "https://example.com",
  chainId: 1,
});

message.format()           // string
message.validate()         // ValidationResult
message.getMessageHash()   // Uint8Array
message.verify(signature)  // boolean
Instance methods delegate to BrandedSiwe namespace functions.

Types

type BrandedMessage = {
  domain: string;
  address: AddressType;
  uri: string;
  version: string;
  chainId: number;
  nonce: string;
  issuedAt: string;
  statement?: string;
  expirationTime?: string;
  notBefore?: string;
  requestId?: string;
  resources?: string[];
}

type Signature = Uint8Array;  // 65 bytes: r (32) + s (32) + v (1)

type ValidationResult =
  | { valid: true }
  | { valid: false; error: ValidationError };

type ValidationError =
  | { type: "invalid_domain"; message: string }
  | { type: "invalid_address"; message: string }
  | { type: "invalid_uri"; message: string }
  | { type: "invalid_version"; message: string }
  | { type: "invalid_chain_id"; message: string }
  | { type: "invalid_nonce"; message: string }
  | { type: "invalid_timestamp"; message: string }
  | { type: "expired"; message: string }
  | { type: "not_yet_valid"; message: string }
  | { type: "signature_mismatch"; message: string };
See BrandedSiwe for branded type details.

Implementation

  • Delegates to BrandedSiwe namespace
  • Extends Object.prototype
  • Structured message format per EIP-4361
  • Supports optional fields via conditional spreading

Quick Start

import { Siwe, Address } from 'tevm';

// Create message
const message = Siwe.create({
  domain: "example.com",
  address: Address("0x..."),
  uri: "https://example.com/login",
  chainId: 1,
  statement: "Sign in to Example App",
});

// Format for wallet signing
const text = message.format();

// User signs with wallet
const signature = await wallet.signMessage(text);

// Validate and verify
const result = Siwe.verifyMessage(message, signature);
if (result.valid) {
  // Authenticated
}

Common Patterns

Authentication Flow

// Backend: Generate nonce
const nonce = Siwe.generateNonce();
storeNonce(userId, nonce);

// Frontend: Create and sign message
const message = Siwe.create({
  domain: window.location.host,
  address: userAddress,
  uri: window.location.origin,
  chainId: await provider.getChainId(),
  nonce,
});
const signature = await wallet.signMessage(message.format());

// Backend: Verify
const parsed = Siwe.parse(messageText);
const result = Siwe.verifyMessage(parsed, signature);
if (result.valid && verifyNonce(parsed.nonce)) {
  createSession(parsed.address);
}

Session Management

const message = Siwe.create({
  domain: "example.com",
  address: userAddress,
  uri: "https://example.com",
  chainId: 1,
  expirationTime: new Date(Date.now() + 3600000).toISOString(),
});

// Validate at session use
const result = message.validate({ now: new Date() });
if (!result.valid) {
  requestReauth();
}

API Documentation

Constructors

Creating SIWE messages from parameters or text. View constructors →

Parsing

Parse EIP-4361 formatted strings to message objects. View parsing →

Validation

Validate message structure and timestamps. View validation →

Signing & Verification

Create hashes and verify signatures with addresses. View signing →

Utilities

Nonce generation and helper functions. View utilities →

Usage Patterns

Real-world authentication and session patterns. View patterns →

Quick Reference: Security Checklist

Always Verify:
  • Domain matches request origin
  • Nonce is fresh and single-use
  • Signature is valid
  • Message not expired
  • Address matches session
  • HTTPS in production
  • Address - 20-byte Ethereum addresses used in messages
  • Signature - 65-byte signatures (r + s + v) for verification
  • Keccak256 - 32-byte hashes from message signing

References