From 46c516e7155ad2e4ddfd3eaee8d6e7e72d51d4a3 Mon Sep 17 00:00:00 2001
From: 0xthrpw <0xthrpw@gmail.com>
Date: Wed, 27 Aug 2025 23:48:03 -0400
Subject: [PATCH] update llms txt complete
---
public/llms/complete/llms-full.txt | 6148 ++++++++++++++++++++++------
public/llms/complete/llms.txt | 68 +-
2 files changed, 4941 insertions(+), 1275 deletions(-)
diff --git a/public/llms/complete/llms-full.txt b/public/llms/complete/llms-full.txt
index 18946b5..d757bb6 100644
--- a/public/llms/complete/llms-full.txt
+++ b/public/llms/complete/llms-full.txt
@@ -1,6 +1,6 @@
# Aggregated Ethereum Identity Context - Full
-> Updated at: 22:35 05/28/25
+> Updated at: 21:35 08/27/25
# docs.efp.app llms-full.txt
@@ -23837,7 +23837,7 @@ Issued At: 2023-01-30T00:00:00.000Z
## Implementations
-The team at [SpruceID](https://login.xyz) has done a phenomenal job writing plug-and-play utilities that let you easily integrate ethereum-based authentication into your project.
+The team at [SIWE](https://siwe.xyz) has done a phenomenal job writing plug-and-play utilities that let you easily integrate ethereum-based authentication into your project.
Whether you are using [Next.js](todo-link), [React](todo-link), or [OpenID Connect](todo-link).
In addition to the above SIWE has been integrated into [connectkit](todo-link) & more.
@@ -36930,2477 +36930,6149 @@ contract DummyNonCCIPAwareResolver is IExtendedResolver, ERC165 {
```
---
-
-# docs.login.xyz llms.txt
+# docs.siwe.xyz llms.txt
> Offering resources and guidance for integrating Sign-In with Ethereum, enhancing user control over digital identities in web applications, while promoting best practices and supporting community involvement within the Ethereum ecosystem.
-Sign-In with EthereumYour Keys, Your IdentifierNextQuickstart GuideLast updated 3 years ago
+> Updated at: 21:35 08/27/25
-Your Keys, Your Identifier
+# β Deployment Guide
-Last updated 3 years ago
+This guide covers deploying the SIWE OIDC Provider in production environments. Choose from multiple deployment options based on your infrastructure needs.
-Sign-In with Ethereum is a new form of authentication that enables users to control their digital identity with their Ethereum account and ENS profile instead of relying on a traditional intermediary. Already used throughout Web3, this effort standardizes the method with best practices and makes it easy to adopt securely.
+## Deployment Options
-To hop right in, check out our Quickstart Guide.
+The SIWE OIDC Provider can be deployed in two primary modes:
-Sign-in With Ethereum was a standard built collaboratively with the greater Ethereum community. For more information on the EIP, check out the following page:
+1. **[Cloudflare Workers](#cloudflare-workers-deployment)** - Serverless, globally distributed
+2. **[Standalone Binary](#standalone-binary-deployment)** - Self-hosted with full control
-For more information on Sign-In with Ethereum and its related benefits to both the Web3 ecosystem and Web2 services, check out the following page:
+## Prerequisites
-π» Login.xyz - Check out the Sign-In with Ethereum home page for more information about supporters, and recent activity.
+### General Requirements
-πΎ Discord - Join the #sign-in-with-ethereum channel in the Spruce Discord Server for additional support.
+- Domain name with HTTPS support
+- Basic knowledge of OIDC flows
+- Client applications that support OpenID Connect
-π Blog - Check out the latest updates on Sign-In with Ethereum posted on the Spruce blog.
+### For Standalone Deployment
-We host a Sign-In with Ethereum community where we discuss relevant updates, new libraries, additional integrations, and more. If you're interested in contributing to Sign-In with Ethereum, we encourage that you join the calls by filling in this form.
+- **Redis** database instance
+- **Docker** or container runtime (recommended)
+- **Reverse proxy** (nginx, Apache, or cloud load balancer)
-# Quickstart Guide
+### For Cloudflare Workers
-Sign-In with EthereumQuickstart GuideThis guide will show how to implement Sign-In with Ethereum (SIWE) in a client-server JavaScript web application.PreviousSign-In with EthereumNextCreating SIWE MessagesLast updated 3 years ago
+- **Cloudflare account** with Workers enabled
+- **Wrangler CLI** installed locally
-This guide will show how to implement Sign-In with Ethereum (SIWE) in a client-server JavaScript web application.
+## Cloudflare Workers Deployment
-Requirements
+Cloudflare Workers provide a serverless, globally distributed deployment option.
-An Ethereum account in the installed MetaMask wallet
+### 1. Setup Repository
-The repository for this tutorial can be found here:
+```bash
+# Clone the SIWE OIDC repository
+git clone https://github.com/signinwithethereum/siwe-oidc
+cd siwe-oidc
+```
-# Creating SIWE Messages
+### 2. Install Wrangler CLI
-Sign-In with EthereumQuickstart GuideCreating SIWE MessagesThis section describes how to generate Sign-In with Ethereum messages and print them to the console.PreviousQuickstart GuideNextImplement the FrontendLast updated 2 years ago
+```bash
+# Install Wrangler globally
+npm install -g @cloudflare/wrangler
-This section describes how to generate Sign-In with Ethereum messages and print them to the console.
+# Or install locally in project
+npm install --save-dev @cloudflare/wrangler
+```
-Last updated 2 years ago
+### 3. Authenticate with Cloudflare
-A completed version of this part can be found in the example repository ().
+```bash
+# Login to Cloudflare
+wrangler auth
-Creating SIWE messages in JavaScript is straightforward when using the siwe library in npm. To begin, create a new project called siwe-print.
+# Verify authentication
+wrangler whoami
+```
-siwe
+### 4. Create KV Namespace
-siwe-print
+KV storage is used for session and client data:
-mkdir siwe-print && cd siwe-print/
+```bash
+# Create production KV namespace
+wrangler kv:namespace create "SIWE_OIDC_KV"
-yarn init --yes
+# Create preview KV namespace for staging
+wrangler kv:namespace create "SIWE_OIDC_KV" --preview
+```
-yarn add siwe ethers
+### 5. Configure wrangler.toml
-mkdir src/
+Update `wrangler.toml` with your account details:
-We can then write the following into ./src/index.js:
+```toml
+name = "siwe-oidc-provider"
+type = "webpack"
+account_id = "your-account-id"
+workers_dev = true
+route = ""
+zone_id = ""
-./src/index.js
+[build]
+command = "npm run build"
-const siwe = require('siwe');
+[build.upload]
+format = "service-worker"
-const domain = "localhost";
+[[kv_namespaces]]
+binding = "SIWE_OIDC_KV"
+id = "your-kv-namespace-id"
+preview_id = "your-preview-kv-namespace-id"
-const origin = "https://localhost/login";
+[vars]
+SIWEOIDC_BASE_URL = "https://your-worker.your-subdomain.workers.dev"
+```
-function createSiweMessage (address, statement) {
+### 6. Deploy to Cloudflare
- const siweMessage = new siwe.SiweMessage({
+```bash
+# Deploy to production
+wrangler publish
- domain,
+# Deploy to preview environment
+wrangler publish --env preview
+```
- address,
+### 7. Configure Custom Domain (Optional)
- statement,
+```bash
+# Add custom domain
+wrangler route add "oidc.yourdomain.com/*" your-zone-id
+```
- uri: origin,
+## Standalone Binary Deployment
- version: '1',
+For self-hosted environments, deploy as a standalone service with Redis.
- chainId: '1'
+### 1. Using Docker (Recommended)
- });
+#### Quick Start
- return siweMessage.prepareMessage();
+```bash
+# Run with docker-compose (includes Redis)
+curl -O https://raw.githubusercontent.com/spruceid/siwe-oidc/main/docker-compose.yml
+docker-compose up -d
+```
-}
+#### Manual Docker Deployment
+
+```bash
+# Start Redis container
+docker run -d --name redis \
+ -p 6379:6379 \
+ redis:7-alpine
+
+# Run SIWE OIDC Provider
+docker run -d --name siwe-oidc \
+ -p 8000:8000 \
+ -e SIWEOIDC_ADDRESS="0.0.0.0" \
+ -e SIWEOIDC_PORT="8000" \
+ -e SIWEOIDC_REDIS_URL="redis://redis:6379" \
+ -e SIWEOIDC_BASE_URL="https://oidc.yourdomain.com" \
+ --link redis \
+ ghcr.io/spruceid/siwe_oidc:latest
+```
+
+### 2. Using Docker Compose
+
+Create `docker-compose.yml`:
+
+```yaml
+version: '3.8'
+
+services:
+ redis:
+ image: redis:7-alpine
+ restart: unless-stopped
+ volumes:
+ - redis_data:/data
+ healthcheck:
+ test: ['CMD', 'redis-cli', 'ping']
+ interval: 10s
+ timeout: 5s
+ retries: 3
+
+ siwe-oidc:
+ image: ghcr.io/spruceid/siwe_oidc:latest
+ restart: unless-stopped
+ ports:
+ - '8000:8000'
+ environment:
+ - SIWEOIDC_ADDRESS=0.0.0.0
+ - SIWEOIDC_PORT=8000
+ - SIWEOIDC_REDIS_URL=redis://redis:6379
+ - SIWEOIDC_BASE_URL=https://oidc.yourdomain.com
+ - SIWEOIDC_RSA_PEM=${SIWEOIDC_RSA_PEM:-}
+ depends_on:
+ - redis
+ healthcheck:
+ test:
+ [
+ 'CMD',
+ 'curl',
+ '-f',
+ 'http://localhost:8000/.well-known/openid-configuration',
+ ]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+
+volumes:
+ redis_data:
+```
+
+Deploy with:
+
+```bash
+docker-compose up -d
+```
-console.log(createSiweMessage(
+### 3. Binary Installation
- "0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94",
+For direct binary installation:
- "This is a test statement."
+```bash
+# Download latest release
+wget https://github.com/spruceid/siwe-oidc/releases/latest/download/siwe-oidc-linux-x86_64
+chmod +x siwe-oidc-linux-x86_64
- ));
+# Run with environment variables
+SIWEOIDC_REDIS_URL=redis://localhost:6379 \
+SIWEOIDC_BASE_URL=https://oidc.yourdomain.com \
+./siwe-oidc-linux-x86_64
+```
-Now run the example:
+## Configuration Options
-node src/index.js
+### Environment Variables
-You should see output similar to the following message, with different values for the Nonce and Issued At fields:
+| Variable | Description | Default | Required |
+| -------------------- | ------------------------------- | ------------------------ | -------- |
+| `SIWEOIDC_ADDRESS` | IP address to bind to | `127.0.0.1` | No |
+| `SIWEOIDC_PORT` | Port to listen on | `8000` | No |
+| `SIWEOIDC_REDIS_URL` | Redis connection URL | `redis://localhost:6379` | Yes |
+| `SIWEOIDC_BASE_URL` | Public-facing base URL | None | Yes |
+| `SIWEOIDC_RSA_PEM` | RSA private key for JWT signing | Auto-generated | No |
-localhost wants you to sign in with your Ethereum account:
+### Advanced Configuration
-0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94
+#### Custom Signing Key
-This is a test statement.
+Generate and use a custom RSA key for JWT signing:
-URI: https://localhost/login
+```bash
+# Generate RSA private key
+openssl genrsa -out private.pem 2048
-Version: 1
+# Extract public key
+openssl rsa -in private.pem -pubout -out public.pem
-Chain ID: 1
+# Use in deployment
+export SIWEOIDC_RSA_PEM=$(cat private.pem)
+```
-Nonce: oNCEHm5jzQU2WvuBB
+#### Redis Configuration
-Issued At: 2022-01-28T23:28:16.013Z
+For production, configure Redis with persistence and security:
-To learn about all the available fields in a SiweMessage, check out the information in EIP-4361
+```bash
+# Redis with persistence and password
+docker run -d --name redis \
+ -p 6379:6379 \
+ -v redis_data:/data \
+ -e REDIS_PASSWORD=your-secure-password \
+ redis:7-alpine \
+ redis-server --requirepass your-secure-password --appendonly yes
+```
-SiweMessage
+## Reverse Proxy Setup
-The fields we are most interested in for the purposes of this guide are address and statement. address is the Ethereum address which the user is signing in with, and the statement as this will describe to the user what action we wish to perform on their behalf.
+### Nginx Configuration
-address
+```nginx
+server {
+ listen 443 ssl http2;
+ server_name oidc.yourdomain.com;
-statement
+ ssl_certificate /path/to/your/cert.pem;
+ ssl_certificate_key /path/to/your/key.pem;
-Often, as in this example, we don't need to do any manipulation of the message, so we can immediately convert it into the textual representation that the user will sign.
+ location / {
+ proxy_pass http://localhost:8000;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
-00_print
+ # CORS headers for OIDC
+ add_header 'Access-Control-Allow-Origin' '*' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
+ add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
+ }
+}
+```
-# Implement the Backend
+### Apache Configuration
-Sign-In with EthereumQuickstart GuideImplement the BackendHere we learn how to build the backend server to handle the user's submission using Express.js.PreviousImplement the FrontendNextConnect the FrontendLast updated 2 years ago
+```apache
+
+ ServerName oidc.yourdomain.com
-Here we learn how to build the backend server to handle the user's submission using Express.js.
+ SSLEngine on
+ SSLCertificateFile /path/to/your/cert.pem
+ SSLCertificateKeyFile /path/to/your/key.pem
-A completed version of this part can be found here (). This example uses only uses the command line in the terminal to print messages, no monitoring of the browser console log is necessary.
+ ProxyPreserveHost On
+ ProxyRequests Off
+ ProxyPass / http://localhost:8000/
+ ProxyPassReverse / http://localhost:8000/
-The backend server gives the frontend a nonce to include in the SIWE message and also verifies the submission. As such, this basic example only provides two corresponding endpoints:
+ Header always set Access-Control-Allow-Origin "*"
+ Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
+ Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
+
+```
-/nonce to generate the nonce for the interaction via GET request.
+## Local Development
-/nonce
+### Development Setup
-GET
+```bash
+# Clone repository
+git clone https://github.com/spruceid/siwe-oidc
+cd siwe-oidc
-/verify to verify the submitted SIWE message and signature via POST request.
+# Start development environment with Docker Compose
+docker-compose -f docker-compose.dev.yml up
-/verify
+# Edit /etc/hosts for local testing
+echo "127.0.0.1 oidc.localhost" >> /etc/hosts
+```
-POST
+### Testing the Deployment
-While this simple example does not check the nonce during verification, all production implementations should, as demonstrated in the final section .
+```bash
+# Test OIDC configuration endpoint
+curl https://oidc.yourdomain.com/.well-known/openid-configuration
-1. Setup the project directory:
+# Register a test client
+curl -X POST https://oidc.yourdomain.com/register \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "redirect_uris": ["https://yourapp.com/callback"],
+ "client_name": "Test Client",
+ "token_endpoint_auth_method": "client_secret_basic"
+ }'
+```
-mkdir siwe-backend && cd siwe-backend/
+## Health Monitoring
-yarn add cors express siwe ethers
+### Health Check Endpoints
-2. Make sure that the package.json type is module like the following:
+- **Status**: `GET /.well-known/openid-configuration` - Returns 200 if service is healthy
+- **Metrics**: Custom monitoring endpoints can be added via environment variables
-package.json
+### Monitoring Setup
-type
+```yaml
+# docker-compose monitoring addition
+services:
+ prometheus:
+ image: prom/prometheus
+ ports:
+ - '9090:9090'
+ volumes:
+ - ./prometheus.yml:/etc/prometheus/prometheus.yml
-module
+ grafana:
+ image: grafana/grafana
+ ports:
+ - '3000:3000'
+ environment:
+ - GF_SECURITY_ADMIN_PASSWORD=admin
+```
-{
+## Security Considerations
- "name": "backend",
+### Production Checklist
- "version": "1.0.0",
+- [ ] **HTTPS Only**: Ensure all traffic uses HTTPS
+- [ ] **Secure Redis**: Use authentication and encryption
+- [ ] **Custom Keys**: Generate and securely store RSA signing keys
+- [ ] **Domain Validation**: Verify redirect URI domains
+- [ ] **Rate Limiting**: Implement request rate limiting
+- [ ] **Monitoring**: Set up logging and alerting
+- [ ] **Backups**: Regular Redis data backups
+- [ ] **Updates**: Keep container images updated
- "main": "index.js",
+### Important Notes
- "type": "module",
+β οΈ **Frontend-API Domain Requirement**: The frontend application must be served from the same subdomain as the OIDC API endpoint for security reasons.
- "license": "MIT",
+β **Valid**: `app.yourdomain.com` β `oidc.yourdomain.com`
+β **Invalid**: `yourapp.com` β `oidc.anotherdomain.com`
- "scripts": {
+## Troubleshooting
- "start": "node src/index.js"
+### Common Issues
- },
+1. **CORS Errors**: Ensure proper CORS headers in reverse proxy
+2. **Redis Connection**: Verify Redis is running and accessible
+3. **Domain Issues**: Check that frontend and API share subdomain
+4. **SSL Issues**: Verify certificate is valid and properly configured
- "dependencies": {
+### Debug Mode
- "siwe": "^2.1.4",
+Enable debug logging:
- "cors": "^2.8.5",
+```bash
+# Add debug environment variable
+RUST_LOG=debug \
+SIWEOIDC_REDIS_URL=redis://localhost:6379 \
+./siwe-oidc
+```
- "ethers": "^6.3.0",
+---
- "express": "^4.18.2"
+import FullWidthLink from '@site/src/components/full-width-link'
- }
+# OIDC Provider
-3. Populate src/index.js with the following:
+## Rationale
-src/index.js
+Many organizations want to consolidate the Sign in with Ethereum workflow to a single identity service (Identity Provider or IdP) that could be used to access all their federated services (Relying Parties or RPs) using [OpenID Connect](https://openid.net/connect/) to forward the user's session. This reduces overhead and mitigates security risks by consolidating authentication to one protected site instead of several, especially in complex IT systems that have many services for their users to access.
-import cors from 'cors';
+## Getting Started
-import express from 'express';
+The OIDC Provider implementation of Sign in with Ethereum can be found here:
-import { generateNonce, SiweMessage } from 'siwe';
+
+
-const app = express();
+Currently, two runtime modes are supported: (1) a standalone executable (using
+Axum and Redis) and (2) a WASM module within a Cloudflare Worker. Both are built
+from the same codebase, specializing at build time. Compilation with a `cargo` target
+of `wasm32` will build for Cloudflare Worker deployments.
-app.use(express.json());
+---
-app.use(cors());
+import FullWidthLink from '@site/src/components/full-width-link'
-app.get('/nonce', function (_, res) {
+# π¦ Rust
- res.setHeader('Content-Type', 'text/plain');
+The Rust implementation of Sign in with Ethereum can be found here:
- res.send(generateNonce());
+
-});
+## Getting Started
-app.post('/verify', async function (req, res) {
+
- const { message, signature } = req.body;
+For detailed implementation and usage instructions, refer to the GitHub repository and crates.io documentation.
- const siweMessage = new SiweMessage(message);
+---
- try {
+# Library Implementations
- await siweMessage.verify({ signature });
+SIWE provides official libraries in multiple programming languages, making it easy to integrate Sign in with Ethereum authentication into applications regardless of your tech stack. Each library implements the [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) specification and provides both message creation and signature verification capabilities.
- res.send(true);
+## Supported Languages
- } catch {
+### [TypeScript/JavaScript](typescript)
- res.send(false);
+The original and most feature-complete SIWE implementation.
- }
+- **Package**: `siwe` on npm
+- **Platforms**: Node.js, Browser, React Native
+- **Features**: Complete [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) support, TypeScript definitions, extensive testing
+- **Best for**: Web applications, React/Vue/Angular apps, Node.js backends
-app.listen(3000);
+### [Rust](rust)
-4. You can run the server with the following command.
+High-performance implementation for Rust applications.
-yarn start
+- **Package**: `siwe` on crates.io
+- **Platforms**: Server applications, CLI tools, embedded systems
+- **Features**: Memory-safe, fast verification, serde serialization
+- **Best for**: High-performance backends, blockchain infrastructure, CLI tools
-In a new terminal window, test the /nonce endpoint to make sure the backend is working:
+### [Python](python)
-curl 'http://localhost:3000/nonce'
+Pythonic implementation for Python developers.
-In the same new terminal window, test the /verify endpoint use the following, and ensure the response is true:
+- **Package**: `siwe` on PyPI
+- **Platforms**: Django, Flask, FastAPI applications
+- **Features**: Async/await support, dataclass integration, type hints
+- **Best for**: Django/Flask apps, data analysis tools, ML/AI applications
-true
+### [Ruby](ruby)
-curl 'http://localhost:3000/verify' \
+Ruby gem with Rails integration support.
- -H 'Content-Type: application/json' \
+- **Package**: `siwe` gem on RubyGems
+- **Platforms**: Rails applications, Sinatra, standalone Ruby scripts
+- **Features**: ActiveSupport integration, Rails middleware, comprehensive docs
+- **Best for**: Ruby on Rails applications, API backends
- --data-raw '{"message":"localhost:8080 wants you to sign in with your Ethereum account:\n0x9D85ca56217D2bb651b00f15e694EB7E713637D4\n\nSign in with Ethereum to the app.\n\nURI: http://localhost:8080\nVersion: 1\nChain ID: 1\nNonce: spAsCWHwxsQzLcMzi\nIssued At: 2022-01-29T03:22:26.716Z","signature":"0xe117ad63b517e7b6823e472bf42691c28a4663801c6ad37f7249a1fe56aa54b35bfce93b1e9fa82da7d55bbf0d75ca497843b0702b9dfb7ca9d9c6edb25574c51c"}'
+### [Go](go)
-We can verify the received SIWE message by parsing it back into a SiweMessage object (the constructor handles this), assigning the received signature to it and calling the verify method:
+Go implementation for Go developers.
-verify
+- **Package**: `github.com/signinwithethereum/siwe-go`
+- **Platforms**: Go web servers, microservices, CLI applications
+- **Features**: Standard library compatibility, efficient verification, minimal dependencies
+- **Best for**: Microservices, Go web applications, infrastructure tools
-message.verify({ signature })
+### [Elixir](elixir)
-message.verify({ signature }) in the above snippet makes sure that the given signature is correct for the message, ensuring that the Ethereum address within the message produced the matching signature.
+Functional implementation for Elixir/Phoenix applications.
-In other applications, you may wish to do further verification on other fields in the message, for example asserting that the authority matches the expected domain, or checking that the named address has the authority to access the named URI.
+- **Package**: `siwe` on Hex
+- **Platforms**: Phoenix applications, LiveView, OTP applications
+- **Features**: GenServer integration, Phoenix plugs, fault tolerance
+- **Best for**: Phoenix web apps, real-time applications, distributed systems
-A small example of this is shown later where the nonce attribute is used to track that a given address has signed the message given by the server.
+## Quick Start Comparison
-# Connect the Frontend
+Here's how to get started with each library:
-Sign-In with EthereumQuickstart GuideConnect the FrontendHere we learn how to update the frontend to send the signed messages to the server.PreviousImplement the BackendNextImplement SessionsLast updated 2 years ago
+### JavaScript/TypeScript
-Here we learn how to update the frontend to send the signed messages to the server.
+```bash
+npm install siwe ethers
+```
-A completed version of the updated frontend can be found here (). This example uses the to print messages, so it should be actively monitored.
+```javascript
+import { SiweMessage } from 'siwe'
+
+const message = new SiweMessage({
+ domain: 'example.com',
+ address: '0x...',
+ uri: 'https://example.com',
+ version: '1',
+ chainId: 1,
+})
+```
-1. Revisit the siwe-frontend directory, stop any running servers, and update src/index.js:
+### Rust
-siwe-frontend
+```toml
+[dependencies]
+siwe = "0.6"
+```
-src/index.js:
+```rust
+use siwe::Message;
-import { BrowserProvider } from 'ethers';
+let message = Message {
+ domain: "example.com".parse()?,
+ address: "0x...".parse()?,
+ uri: "https://example.com".parse()?,
+ version: siwe::Version::V1,
+ chain_id: 1,
+ // ...
+};
+```
-import { SiweMessage } from 'siwe';
+### Python
-const domain = window.location.host;
+```bash
+pip install siwe
+```
-const origin = window.location.origin;
+```python
+from siwe import SiweMessage
+
+message = SiweMessage(
+ domain="example.com",
+ address="0x...",
+ uri="https://example.com",
+ version="1",
+ chain_id=1,
+)
+```
-const provider = new BrowserProvider(window.ethereum);
+### Ruby
-const BACKEND_ADDR = "http://localhost:3000";
+```bash
+gem install siwe
+```
-async function createSiweMessage(address, statement) {
+```ruby
+require 'siwe'
- const res = await fetch(`${BACKEND_ADDR}/nonce`);
+message = Siwe::Message.new(
+ domain: 'example.com',
+ address: '0x...',
+ uri: 'https://example.com',
+ version: '1',
+ chain_id: 1
+)
+```
- const message = new SiweMessage({
+### Go
- domain,
+```bash
+go get github.com/signinwithethereum/siwe-go
+```
- address,
+```go
+import "github.com/signinwithethereum/siwe-go"
- statement,
+message := siwe.Message{
+ Domain: "example.com",
+ Address: "0x...",
+ URI: "https://example.com",
+ Version: "1",
+ ChainID: 1,
+}
+```
- uri: origin,
+### Elixir
- version: '1',
+```elixir
+# In mix.exs
+{:siwe, "~> 0.3"}
+```
- chainId: '1',
+```elixir
+message = %Siwe.Message{
+ domain: "example.com",
+ address: "0x...",
+ uri: "https://example.com",
+ version: "1",
+ chain_id: 1
+}
+```
- nonce: await res.text()
+## Feature Comparison
- });
+| Feature | TypeScript | Rust | Python | Ruby | Go | Elixir |
+| ---------------------- | -------------- | ---------- | ------------- | -------------- | --------- | ------- |
+| Message Creation | β | β | β | β | β | β |
+| Signature Verification | β | β | β | β | β | β |
+| Nonce Generation | β | β | β | β | β | β |
+| EIP-191 Support | β | β | β | β | β | β |
+| EIP-712 Support | β | β | β | β | β | β |
+| EIP-1271 Support | β | β | β | β | β | β |
+| Async/Await | β | β | β | β | β | β |
+| Type Safety | β | β | β | β | β | β |
+| Framework Integration | React, Express | Axum, Warp | Django, Flask | Rails, Sinatra | Gin, Echo | Phoenix |
+| Browser Support | β | β | β | β | β | β |
- return message.prepareMessage();
+## Choosing the Right Library
-function connectWallet() {
+### For Web Applications
- provider.send('eth_requestAccounts', [])
+- **Frontend**: Use TypeScript/JavaScript for React, Vue, Angular, or vanilla JS
+- **Backend**: Choose based on your existing backend language and framework
- .catch(() => console.log('user rejected request'));
+### For Mobile Applications
-let message = null;
+- **React Native**: TypeScript/JavaScript
+- **Native iOS/Android**: Use appropriate native HTTP libraries with any backend
-let signature = null;
+### For Enterprise Applications
-async function signInWithEthereum() {
+- **Java/.NET**: Use HTTP clients to communicate with SIWE backend services
+- **Enterprise backends**: Go, Rust, or TypeScript for high performance
- const signer = await provider.getSigner();
+### For Rapid Prototyping
- message = await createSiweMessage(
+- **TypeScript/JavaScript**: Fastest to get started, works everywhere
+- **Python**: Great for data-driven applications and ML integration
+- **Ruby**: Excellent for Rails developers
- await signer.address,
+## Installation Guides
- 'Sign in with Ethereum to the app.'
+Each library has specific installation and setup instructions:
- );
+- **[TypeScript/JavaScript Setup](typescript#installation)**: npm, yarn, browser CDN
+- **[Rust Setup](rust)**: Cargo dependencies and features
+- **[Python Setup](python)**: pip, conda, virtual environments
+- **[Ruby Setup](ruby)**: gem, bundler, Rails integration
+- **[Go Setup](go)**: go mod, dependency management
+- **[Elixir Setup](elixir)**: mix deps, Phoenix integration
- console.log(message);
+## Migration Guides
- signature = await signer.signMessage(message);
+If you need to switch between libraries or upgrade versions:
- console.log(signature);
+- [TypeScript v1 to v2 Migration](typescript#migration-guide)
+- [Cross-language Migration Tips](#cross-language-migration)
+- Version Compatibility Matrix (see below)
-async function sendForVerification() {
+## Cross-Language Migration
- const res = await fetch(`${BACKEND_ADDR}/verify`, {
+When moving between different SIWE library implementations:
- method: "POST",
+### Message Format Compatibility
- headers: {
+All libraries generate identical [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) compliant messages, ensuring signatures created in one language can be verified in any other.
- 'Content-Type': 'application/json',
+### Configuration Mapping
- },
+```javascript
+// JavaScript
+const message = new SiweMessage({
+ domain: 'example.com',
+ address: '0x...',
+ statement: 'Sign in to our app',
+ uri: 'https://example.com',
+ version: '1',
+ chainId: 1,
+ nonce: 'abc123',
+ issuedAt: '2023-10-31T16:25:24Z',
+})
+```
- body: JSON.stringify({ message, signature }),
+```python
+# Python equivalent
+message = SiweMessage(
+ domain="example.com",
+ address="0x...",
+ statement="Sign in to our app",
+ uri="https://example.com",
+ version="1",
+ chain_id=1,
+ nonce="abc123",
+ issued_at="2023-10-31T16:25:24Z"
+)
+```
- console.log(await res.text());
+### Error Handling Patterns
-const connectWalletBtn = document.getElementById('connectWalletBtn');
+Each library follows language-specific error handling conventions but provides equivalent functionality:
-const siweBtn = document.getElementById('siweBtn');
+- **JavaScript/TypeScript**: Promise-based with try/catch
+- **Rust**: Result types with match expressions
+- **Python**: Exception-based with try/except
+- **Ruby**: Exception-based with begin/rescue
+- **Go**: Error return values with if err != nil
+- **Elixir**: `{:ok, result} | {:error, reason}` tuples
-const verifyBtn = document.getElementById('verifyBtn');
+## Community Libraries
-connectWalletBtn.onclick = connectWallet;
+Beyond official libraries, the community has created additional implementations:
-siweBtn.onclick = signInWithEthereum;
+- **Java**: Community-maintained Spring Boot integration
+- **C#/.NET**: Community library for ASP.NET applications
+- **Swift**: iOS/macOS native implementation
+- **Kotlin**: Android-first implementation
+- **PHP**: Laravel and Symfony integrations
-verifyBtn.onclick = sendForVerification;
+Visit our [GitHub repository](https://github.com/signinwithethereum/siwe) for links to community libraries.
-2. Update src/index.html:
+## Version Compatibility
-src/index.html
+### Library Version Matrix
-
+| Library | Current Version | EIP-4361 Spec | Node.js/Runtime | Notes |
+|---------|----------------|---------------|-----------------|-------|
+| TypeScript | 2.x | Full support | Node 16+ | Breaking changes from v1 |
+| Rust | 0.6.x | Full support | N/A | Stable API |
+| Python | 3.x | Full support | Python 3.7+ | Async support added |
+| Ruby | 2.x | Full support | Ruby 2.7+ | Rails 6+ recommended |
+| Go | 1.x | Full support | Go 1.18+ | Generics support |
+| Elixir | 0.3.x | Full support | Elixir 1.12+ | Phoenix 1.6+ |
-
+### Breaking Changes
-
+import FullWidthLink from '@site/src/components/full-width-link'
-
+# π Python
-
+The Python implementation of Sign in with Ethereum can be found here:
- No ENS Profile detected.
+
-This will create a table with data based on the user's ENS information if it exists. Otherwise, if there isn't any data, it will remain hidden and a "No ENS Profile detected." message will be displayed.
+
-Finally, we can finish by updating the index.js file to include what's needed.
+:::note
-Update the frontend/src/index.js file to the following:
+Sign in with Ethereum can be found on [pypi](https://pypi.org/project/siwe/)
-frontend/src/index.js
+:::
-const profileElm = document.getElementById('profile');
+---
-const noProfileElm = document.getElementById('noProfile');
+import FullWidthLink from '@site/src/components/full-width-link'
-const welcomeElm = document.getElementById('welcome');
+# β¨οΈ TypeScript
-const ensLoaderElm = document.getElementById('ensLoader');
+The TypeScript implementation of Sign in with Ethereum can be found here:
-const ensContainerElm = document.getElementById('ensContainer');
+
-const ensTableElm = document.getElementById('ensTable');
+## Getting Started
-let address;
+The TypeScript implementation is available on npm and provides comprehensive [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) support.
- profileElm.classList = 'hidden';
+### Installation
- noProfileElm.classList = 'hidden';
+You can install the Sign in with Ethereum library as an npm package.
- welcomeElm.classList = 'hidden';
+### Additional Resources
- address = await signer.getAddress()
+- [Quickstart Guide](/quickstart)
+- [Migrating to v2](/libraries/typescript/migrating-to-v2)
+- [TypeScript Quickstart](/libraries/typescript/typescript-quickstart)
- if (!res.ok) {
+### Supported Platforms
- console.error(`Failed in getInformation: ${res.statusText}`);
+The library offers implementations for multiple languages and frameworks:
- return
+- TypeScript
+- Rust
+- Elixir
+- Python
+- Ruby (including Rails)
+- Go
- displayENSProfile();
+### Integrations
- let result = await res.text();
+The library supports various integrations:
- console.log(result);
+- Discourse
+- NextAuth.js
+- Auth0
- address = result.split(" ")[result.split(" ").length - 1];
+For detailed implementation instructions and examples, refer to the specific documentation sections in the sidebar.
-async function displayENSProfile() {
+## API Reference
- const ensName = await provider.lookupAddress(address);
+### SiweMessage Class
- if (ensName) {
+The main class for creating and verifying SIWE messages.
- profileElm.classList = '';
+#### Constructor
- welcomeElm.innerHTML = `Hello, ${ensName}`;
+```typescript
+new SiweMessage(params: SiweMessageParams)
+```
- let avatar = await provider.getAvatar(ensName);
+**Parameters:**
- if (avatar) {
+| Parameter | Type | Required | Description |
+| ---------------- | ---------- | -------- | ----------------------------------------------------------------------------------------- |
+| `domain` | `string` | β | RFC 3986 authority requesting the signing |
+| `address` | `string` | β | Ethereum address (EIP-55 checksum format) |
+| `uri` | `string` | β | RFC 3986 URI referring to the resource |
+| `version` | `string` | β | Must be `"1"` for [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) compliance |
+| `chainId` | `number` | β | EIP-155 Chain ID |
+| `statement` | `string` | β | Human-readable ASCII assertion |
+| `nonce` | `string` | β | Randomized token (auto-generated if not provided) |
+| `issuedAt` | `string` | β | ISO 8601 datetime (defaults to current time) |
+| `expirationTime` | `string` | β | ISO 8601 datetime for expiration |
+| `notBefore` | `string` | β | ISO 8601 datetime for validity start |
+| `requestId` | `string` | β | System-specific identifier |
+| `resources` | `string[]` | β | List of URI references |
- welcomeElm.innerHTML += ` `;
+#### Methods
- ensLoaderElm.innerHTML = 'Loading...';
+##### `prepareMessage(): string`
- ensTableElm.innerHTML.concat(`
+### Next.js API Routes
-
+```typescript
+// pages/api/nonce.ts
+import type { NextApiRequest, NextApiResponse } from 'next'
+import { generateNonce } from 'siwe'
-
+export default function handler(req: NextApiRequest, res: NextApiResponse) {
+ if (req.method !== 'GET') {
+ return res.status(405).json({ error: 'Method not allowed' })
+ }
-
+ const nonce = generateNonce()
+ res.json({ nonce })
+}
-Next, we'll update the index.js file to reach out to OpenSea's API using the logged-in user's address, then format the output to place the information in the table. If the user has no NFTs, we'll display a "No NFTs found" message in the loader div.
+// pages/api/verify.ts
+import type { NextApiRequest, NextApiResponse } from 'next'
+import { SiweMessage } from 'siwe'
-div
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ if (req.method !== 'POST') {
+ return res.status(405).json({ error: 'Method not allowed' })
+ }
-const nftElm = document.getElementById('nft');
+ try {
+ const { message, signature } = req.body
+ const siweMessage = new SiweMessage(message)
-const nftLoaderElm = document.getElementById('nftLoader');
+ const result = await siweMessage.verify({ signature })
-const nftContainerElm = document.getElementById('nftContainer');
+ if (result.success) {
+ res.json({ success: true, user: result.data })
+ } else {
+ res.status(401).json({ error: 'Verification failed' })
+ }
+ } catch (error) {
+ res.status(400).json({ error: error.message })
+ }
+}
+```
-const nftTableElm = document.getElementById('nftTable');
+## Advanced Features
-async function getNFTs() {
+### EIP-1271 Smart Contract Signatures
- let res = await fetch(`https://api.opensea.io/api/v1/assets?owner=${address}`);
+Verify signatures from smart contracts:
- if (!res.ok) {
+```typescript
+import { SiweMessage } from 'siwe'
+import { ethers } from 'ethers'
- throw new Error(res.statusText)
+const message = new SiweMessage(messageParams)
+const provider = new ethers.providers.JsonRpcProvider('https://...')
- let body = await res.json();
+const result = await message.verify({
+ signature,
+ provider, // Required for EIP-1271 verification
+})
+```
- if (!body.assets || !Array.isArray(body.assets) || body.assets.length === 0) {
+### Custom Domain Validation
- return []
+```typescript
+const message = new SiweMessage(messageString)
- return body.assets.map((asset) => {
+// Override domain validation
+const result = await message.verify({
+ signature,
+ domain: 'custom-domain.com',
+})
+```
- let {name, asset_contract, token_id} = asset;
+### Time-based Validation
- let {address} = asset_contract;
+```typescript
+// Verify at specific time
+const result = await message.verify({
+ signature,
+ time: '2023-10-31T16:30:00Z',
+})
+```
- return {name, address, token_id};
+## Migration Guide
- });
+### From v1 to v2
- } catch (err) {
+Version 2 introduces breaking changes for better TypeScript support:
- console.error(`Failed to resolve nfts: ${err.message}`);
+#### Message Creation
- return [];
+```typescript
+// v1
+const message = new SiweMessage({
+ domain: 'example.com',
+ address: '0x...',
+ // ... other fields
+})
-async function displayNFTs() {
+// v2 - Same API, improved types
+const message = new SiweMessage({
+ domain: 'example.com',
+ address: '0x...',
+ // ... other fields
+})
+```
- nftLoaderElm.innerHTML = 'Loading NFT Ownership...';
+#### Verification Response
- nftElm.classList = '';
+```typescript
+// v1
+const result = await message.verify({ signature })
+if (result.success) {
+ console.log(result.data) // Direct access
+}
- let nfts = await getNFTs();
+// v2 - Enhanced error handling
+const result = await message.verify({ signature })
+if (result.success) {
+ console.log(result.data) // Same structure
+} else {
+ console.error(result.error) // Detailed error info
+}
+```
- if (nfts.length === 0) {
+## Troubleshooting
- nftLoaderElm.innerHTML = 'No NFTs found';
+### Common Issues
- let tableHtml = "
Name
Address
Token ID
";
+#### "Invalid signature" Error
- nfts.forEach((nft) => {
+- Verify the message string exactly matches what was signed
+- Check that the address is in EIP-55 checksum format
+- Ensure the signature is in the correct format (0x prefixed hex)
- tableHtml += `
${nft.name}
${nft.address}
${nft.token_id}
`
+#### "Invalid nonce" Error
- nftTableElm.innerHTML = tableHtml;
+- Verify nonces are only used once
+- Check nonce expiration/cleanup logic
+- Ensure nonce matches between message creation and verification
- nftContainerElm.classList = '';
+#### TypeScript Compilation Errors
- nftLoaderElm.innerHTML = '';
+- Update to latest TypeScript version (4.5+)
+- Ensure `strict: true` in tsconfig.json
+- Install `@types/node` if using Node.js APIs
- displayNFTs();
+### Browser Compatibility
-Similar to the previous guide, to see the result, go into frontend and run:
+The library supports all modern browsers with ES6+ support:
-Now, when a user signs in, information on NFT holdings is displayed below the ENS information (if available).
+- Chrome 60+
+- Firefox 55+
+- Safari 12+
+- Edge 79+
-OpenSea's API is a great resource for interacting with NFT data off-chain. Learn more .
+For older browser support, use the ES5 build:
-05_nft_resolution/frontend
+```html
+
+```
-# TypeScript
+## Performance
-LibrariesTypeScriptA TypeScript implementation of EIP-4361: Sign In With Ethereum.PreviousResolve NFT HoldingsNextMigrating to v2Last updated 3 years ago
+### Bundle Size
-A TypeScript implementation of EIP-4361: Sign In With Ethereum.
+- **Minified**: ~45KB
+- **Gzipped**: ~12KB
+- **Tree-shaking**: Supports ES modules for optimal bundling
-The TypeScript implementation of Sign-In with Ethereum can be found here:
+### Verification Performance
-Sign-In with Ethereum can be installed as an npm package. For more information and package information, click .
+- **Message parsing**: ~0.1ms
+- **Signature verification**: ~10-50ms (depends on provider)
+- **Memory usage**: ~2MB per verification
-npm
+### Optimization Tips
-# Migrating to v2
+```typescript
+// Reuse provider instances
+const provider = new ethers.providers.JsonRpcProvider(RPC_URL)
-LibrariesTypeScriptMigrating to v2TypeScript v2PreviousTypeScriptNextTypeScript QuickstartLast updated 2 years ago
+// Cache verification results for identical signatures
+const verificationCache = new Map()
-TypeScript v2
+async function cachedVerify(message: string, signature: string) {
+ const key = `${message}-${signature}`
+ if (verificationCache.has(key)) {
+ return verificationCache.get(key)
+ }
-If you are using siwe v1.1.6, we encourage you to update to the latest version (2.1.x). The following guide walks you through how to update your application.
+ const result = await new SiweMessage(message).verify({ signature })
+ verificationCache.set(key, result)
+ return result
+}
+```
-siwe v1.1.6
+## Resources
-2.1.x
+- **GitHub**: [https://github.com/signinwithethereum/siwe](https://github.com/signinwithethereum/siwe)
+- **npm**: [https://www.npmjs.com/package/sign-in-with-ethereum](https://www.npmjs.com/package/sign-in-with-ethereum)
+- **TypeScript Playground**: [https://siwe-demo.vercel.app](https://siwe-demo.vercel.app)
+- **Examples**: [https://github.com/signinwithethereum/siwe-examples](https://github.com/signinwithethereum/siwe-examples)
-The function validate(sig, provider) is now deprecated and is replaced by verify(VerifyParams, VerifyOpts). These two new parameters accept the following fields:
+---
-validate(sig, provider)
+_Need help with integration? Check out our [Quickstart Guide](../quickstart/index.md) or [Integration Examples](../integrations/index.md)._
-verify(VerifyParams, VerifyOpts)
+---
-export interface VerifyParams {
+import FullWidthLink from '@site/src/components/full-width-link'
- /** Signature of the message signed by the wallet */
+# π Go
- signature: string;
+The Go implementation of Sign in with Ethereum can be found here:
- /** RFC 4501 dns authority that is requesting the signing. */
+
- domain?: string;
+## Installation
- /** Randomized token used to prevent replay attacks, at least 8 alphanumeric characters. */
+Install the library using Go get:
- nonce?: string;
+```bash
+go get -u github.com/signinwithethereum/siwe-go
+```
- /**ISO 8601 datetime string of the current time. */
+## Usage
- time?: string;
+### Parsing a SIWE Message
-export interface VerifyOpts {
+```go
+var message *siwe.Message
+var err error
- /** ethers provider to be used for EIP-1271 validation */
+message, err = siwe.ParseMessage(messageStr)
+```
- provider?: providers.Provider;
+### Verifying and Authenticating a SIWE Message
- /** If the library should reject promises on errors, defaults to false */
+```go
+// Verify using EIP-191, returns the Ethereum public key
+var publicKey *ecdsa.PublicKey
+var err error
- suppressExceptions?: boolean;
+publicKey, err = message.VerifyEIP191(signature)
-The new function makes it easier to match fields automatically - like domain, nonce and match against other TimeDate instead of now (time).
+// Check time constraints
+if message.ValidNow() {
+ // Message is valid
+}
-domain
+// Combined verification with optional nonce and timestamp
+publicKey, err = message.Verify(signature, optionalNonce, optionalTimestamp)
+```
-nonce
+### Serializing a SIWE Message
-time
+```go
+fmt.Printf("%s", message.String())
+```
-The return type was also modified. It now returns a SiweResponse instead of a SiweMessage, and this new object is defined by the following interface:
+### Signing Messages from Go Code
-SiweResponse
+```go
+func signHash(data []byte) common.Hash {
+ msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
+ return crypto.Keccak256Hash([]byte(msg))
+}
-export interface SiweResponse {
+func signMessage(message string, privateKey *ecdsa.PrivateKey) ([]byte, error) {
+ sign := signHash([]byte(message))
+ signature, err := crypto.Sign(sign.Bytes(), privateKey)
- /** Boolean representing if the message was verified with success. */
+ if err != nil {
+ return nil, err
+ }
- success: boolean;
+ signature[64] += 27
+ return signature, nil
+}
+```
- /** If present `success` MUST be false and will provide extra information on the failure reason. */
+---
- error?: SiweError;
+# Sign in with Ethereum
- /** Original message that was verified. */
+**Sign in with Ethereum** (SIWE) is an authentication method for Ethereum accounts. It can be used by any kind of app, whether crypto-related or not.
- data: SiweMessage;
+## Key Benefits
-As part of the new API, new error types were introduced to clarify when a message fails verification. These errors are defined at:
+### π€ **Complements other elements of the Ethereum Identity Stack**
-More information regarding the rationale behind the API Harmonization and TypeScript v2.0 beta release can be found here:
+After the user authenticates, apps may use their onchain **[ENS](https://ens.domains)** username and profile and **[EFP](https://efp.app)** social graph.
-# TypeScript Quickstart
+### [βοΈ **Enrich your app's UX with onchain data**](./quickstart/retrieve-onchain-data.mdx)
-LibrariesTypeScriptTypeScript QuickstartA Quickstart example using the TypeScript SIWE LibraryPreviousMigrating to v2NextRustLast updated 3 years ago
+Seamlessly connects user identity with onchain activities, enabling applications to verify user ownership of NFTs, tokens, and other blockchain assets.
-A Quickstart example using the TypeScript SIWE Library
+### π‘οΈ **Self-Sovereign Identity**
-Goals
+Users maintain control over their identity credentials, eliminating dependency on centralized identity providers like Google or Facebook.
-Run a Sign-In with Etheruem example locally
+### ποΈ **Single Sign-On**
-Sign-In with Ethereum using a preferred wallet
+Works across any application that implements the SIWE standard, creating a unified authentication and account experience.
-version 14.0 or higher
-First clone the siwe repository from GitHub by running the following command:
+## How It Works
-git clone https://github.com/spruceid/siwe-notepad
+SIWE follows a simple authentication flow:
-Next, enter the directory and run the example by using the following commands:
+1. **Message Creation**: Application generates a human-readable sign-in message containing domain, address, and security parameters, following the [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) standard.
+2. **User Signing**: User signs the message with the Ethereum wallet of their choice.
+3. **Signature Verification**: Application verifies the signature cryptographically to authenticate the user.
+4. **Session Establishment**: Upon successful verification, a secure session is created for the authenticated user.
-cd siwe-notepad
+## Open EIP standard
-npm install
+SIWE is defined by **[EIP-4361](https://eips.ethereum.org/EIPS/eip-4361)** standard.
-npm run dev
+## Getting Started
-Finally, visit the example at http://localhost:4361 (or whichever port npm allocated).
+Ready to implement SIWE in your application? Here are some quick paths forward:
-Once the example has loaded, sign in with Ethereum by clicking on one of the wallet options, enter some text, and save that text. After disconnecting, try reconnecting to reload that text once the session has been reestablished.
+### π **Quick Start**
-The full example can be found here:
+Follow our [Quickstart Guide](quickstart/index.md) for a step-by-step tutorial on implementing SIWE from scratch.
-# Rust
+### π **Choose Your Library**
-LibrariesRustA Rust implementation of EIP-4361: Sign In With Ethereum.PreviousTypeScript QuickstartNextElixirLast updated 2 years ago
+We provide official libraries for multiple programming languages:
-A Rust implementation of EIP-4361: Sign In With Ethereum.
+- [TypeScript](libraries/typescript)
+- [Rust](libraries/rust)
+- [Python](libraries/python)
+- [Ruby](libraries/ruby)
+- [Go](libraries/go)
+- [Elixir](libraries/elixir)
-The Rust implementation and latest documentation for Sign-In with Ethereum can be found here:
+### πͺͺ **Ethereum Identity Kit component library and API**
-Sign-In with Ethereum can be found on .
+We offer the [Ethereum Identity Kit](https://ethidentitykit.com/) component library and API to help you integrate SIWE and the rest of the Ethereum identity stack.
-# Elixir
+### π **Pre-built Integrations**
-LibrariesElixirAn Elixir implementation of EIP-4361: Sign In With Ethereum.PreviousRustNextPythonLast updated 3 years ago
+Get started quickly with existing integrations:
-An Elixir implementation of EIP-4361: Sign In With Ethereum.
+- [NextAuth.js](./integrations/nextauth.js.mdx)
+- [Auth0](./integrations/auth0.mdx)
+- [Discourse](./integrations/discourse)
-The Elixir implementation of Sign-In with Ethereum can be found here:
+## Security First
-The package can be installed by adding siwe to your list of dependencies in mix.exs:
+SIWE prioritizes security through:
-mix.exs
+- **Nonce-based replay protection** to prevent message reuse attacks
+- **Domain binding** to prevent cross-site message abuse
+- **Expiration timestamps** for time-limited authentication
+- **Best practices guidance** for secure implementation
-def deps do
+Learn more about [Security Best Practices](/security-considerations).
- [
+## Enterprise Ready
- {:siwe, "~> 0.3"}
+For enterprise applications, SIWE provides:
- ]
+- **[OpenID Connect (OIDC) Provider](./oidc-provider/index.mdx)** for standards-compliant integration
+- **Scalable authentication** supporting millions of users
+- **Compliance-friendly** audit trails and security controls
+- **Professional support** and deployment guidance
-end
+Learn more about [OpenID Connect Integration](/integrations/auth0).
-To see how this works in iex, clone this repository and from the root run:
+## Community & Support
-iex
+SIWE is an open-source project with an active community:
-$ mix deps.get
+- **GitHub**: [Contribute to the project and report issues](https://github.com/signinwithethereum/)
+- **Twitter**: Follow [@signinethereum](https://twitter.com/signinethereum) for updates
-Then create two files message.txt:
+Explore the [Integrations](integrations/index.md) section to see SIWE implementations in production.
-message.txt
+## Standards Compliance
-login.xyz wants you to sign in with your Ethereum account:
+SIWE fully complies with:
-0xfA151B5453CE69ABf60f0dbdE71F6C9C5868800E
+- [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361): Sign in with Ethereum specification
+- [OpenID Connect](oidc-provider/index.mdx) 1.0 for enterprise integration
+- [OAuth 2.0](integrations/auth0.mdx) for authorization flows
+- Web3 wallet standards for broad compatibility
-Sign-In With Ethereum Example Statement
+---
-URI: https://login.xyz
+import FullWidthLink from '@site/src/components/full-width-link'
-Nonce: ToTaLLyRanDOM
+# Onchain Data
-Issued At: 2021-12-17T00:38:39.834Z
+Once the user has authenticated with their Ethereum account, you may want to consider making use of their onchain data to enrich their experience of your app.
-signature.txt:
+## Ethereum Name Service (ENS)
-signature.txt
+ENS enables onchain usernames and profiles.
-0x8d1327a1abbdf172875e5be41706c50fc3bede8af363b67aefbb543d6d082fb76a22057d7cb6d668ceba883f7d70ab7f1dc015b76b51d226af9d610fa20360ad1c
+
-then run
+## Ethereum Follow Protocol (EFP)
-$ iex -S mix
+EFP enables an onchain social graph for Ethereum accounts.
-Once in iex, you can then run the following to see the result:
+
-iex> {:ok, msg} = File.read("./message.txt")
+## Other Onchain Assets
-...
+You may also want to retrieve the user's NFTs, token balances, and other onchain assets.
-iex> {:ok, sig} = File.read("./signature.txt")
+
-iex> Siwe.parse_if_valid(String.trim(msg), String.trim(sig))
+---
-{:ok, %{
+# Connect the Frontend
- __struct__: Siwe,
+In this section of the Sign in with Ethereum quickstart guide, you'll learn how to update the frontend to send signed messages to the server.
- address: "0xfA151B5453CE69ABf60f0dbdE71F6C9C5868800E",
+## Prerequisites
- chain_id: "1",
+- A completed backend from the previous steps
+- Basic understanding of JavaScript and web development
- domain: "login.xyz",
+## Step-by-Step Implementation
- expiration_time: nil,
+### 1. Update `src/index.js`
- issued_at: "2021-12-17T00:38:39.834Z",
+```javascript
+import { BrowserProvider } from 'ethers'
+import { SiweMessage } from 'siwe'
- nonce: "ToTaLLyRanDOM",
+const domain = window.location.host
+const origin = window.location.origin
+const provider = new BrowserProvider(window.ethereum)
- not_before: nil,
+const BACKEND_ADDR = 'http://localhost:3000'
- request_id: nil,
+async function createSiweMessage(address, statement) {
+ const res = await fetch(`${BACKEND_ADDR}/nonce`)
+ const message = new SiweMessage({
+ domain,
+ address,
+ statement,
+ uri: origin,
+ version: '1',
+ chainId: '1',
+ nonce: await res.text(),
+ })
+ return message.prepareMessage()
+}
- resources: [],
+function connectWallet() {
+ provider
+ .send('eth_requestAccounts', [])
+ .catch(() => console.log('user rejected request'))
+}
- statement: "Sign-In With Ethereum Example Statement",
+let message = null
+let signature = null
- uri: "https://login.xyz",
+async function signInWithEthereum() {
+ const signer = await provider.getSigner()
- version: "1"
+ message = await createSiweMessage(
+ await signer.address,
+ 'Sign in with Ethereum to the app.'
+ )
+ console.log(message)
+ signature = await signer.signMessage(message)
+ console.log(signature)
+}
-}}
+async function sendForVerification() {
+ const res = await fetch(`${BACKEND_ADDR}/verify`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ message, signature }),
+ })
+ console.log(await res.text())
+}
-Any valid SIWE message and signature pair can be substituted.The functions described below can also be tested with msg, sig, or a value set to the result Siwe.parse_if_valid.
+const connectWalletBtn = document.getElementById('connectWalletBtn')
+const siweBtn = document.getElementById('siweBtn')
+```
-msg
+### 2. Wire Up Event Listeners
-sig
+Connect the buttons to their respective functions:
-Siwe.parse_if_valid
+```javascript
+connectWalletBtn.addEventListener('click', connectWallet)
+siweBtn.addEventListener('click', signInWithEthereum)
+```
-Sign-In with Ethereum can be installed as a hex. For more information and package information, click
+## Key Points
-hex
+- The frontend creates SIWE messages with proper domain and origin
+- Users sign the message with their connected wallet
+- Signed messages are sent to the backend for verification
+- Proper error handling ensures a smooth user experience
-# Ruby
+## Next Steps
-LibrariesRubyA Ruby implementation of EIP-4361: Sign In With Ethereum.PreviousPythonNextRailsLast updated 3 years ago
+Continue to the next section to learn about implementing sessions for enhanced security.
-A Ruby implementation of EIP-4361: Sign In With Ethereum.
+---
-The Ruby implementation of Sign-In with Ethereum can be found here:
+# Implement Sessions
-Additional packages may be required to install the gem:
+This guide demonstrates how to implement sessions with Express.js to add backend security for Sign in with Ethereum (SIWE).
-brew install automake openssl libtool pkg-config gmp libffi
+## Prerequisites
-sudo apt-get install build-essential automake pkg-config libtool \
+- A SIWE backend project
+- Express.js
+- Basic understanding of session management
- libffi-dev libssl-dev libgmp-dev python-dev
+## Implementation Steps
-After installing any required dependencies SIWE can be easily installed with:
+### 1. Install Dependencies
-gem install siwe
+```bash
+yarn add express-session
+```
-SIWE provides a Message class which implements EIP-4361.
+### 2. Update Backend Configuration
-Message
+Modify `src/index.js` to include session management:
-require 'siwe'
+```javascript
+import cors from 'cors'
+import express from 'express'
+import Session from 'express-session'
+import { generateNonce, SiweMessage } from 'siwe'
+
+const app = express()
+app.use(express.json())
+app.use(
+ cors({
+ origin: 'http://localhost:8080',
+ credentials: true,
+ })
+)
-require 'time'
+app.use(
+ Session({
+ name: 'siwe-quickstart',
+ secret: 'siwe-quickstart-secret',
+ resave: true,
+ saveUninitialized: true,
+ cookie: { secure: false, sameSite: true },
+ })
+)
-# Only the mandatory arguments
+app.get('/nonce', async function (req, res) {
+ req.session.nonce = generateNonce()
+ res.setHeader('Content-Type', 'text/plain')
+ res.status(200).send(req.session.nonce)
+})
-Siwe::Message.new("domain.example", "0x9D85ca56217D2bb651b00f15e694EB7E713637D4", "some.uri", "1")
+app.post('/verify', async function (req, res) {
+ try {
+ if (!req.body.message) {
+ res.status(422).json({
+ message: 'Expected prepareMessage object as body.',
+ })
+ return
+ }
+
+ let SIWEObject = new SiweMessage(req.body.message)
+ const { data: message } = await SIWEObject.verify({
+ signature: req.body.signature,
+ nonce: req.session.nonce,
+ })
+
+ req.session.siwe = message
+ req.session.cookie.expires = new Date(message.expirationTime)
+ req.session.save(() => res.status(200).send(true))
+ } catch (e) {
+ req.session.siwe = null
+ req.session.nonce = null
+ console.error(e)
+ res.status(400).send(`Failed to verify message: ${e.message}`)
+ }
+})
-# Complete SIWE message with default values
+app.get('/personal_information', function (req, res) {
+ if (!req.session.siwe) {
+ res.status(401).json({ message: 'You have to first sign_in' })
+ return
+ }
+ console.log('User is authenticated!')
+ res.setHeader('Content-Type', 'text/plain')
+ res.send(
+ `You are authenticated and your address is: ${req.session.siwe.address}`
+ )
+})
-Siwe::Message.new("domain.example", "0x9D85ca56217D2bb651b00f15e694EB7E713637D4", "some.uri", "1", {
+app.listen(3000, () => {
+ console.log(`Example app listening on port 3000`)
+})
+```
- issued_at: Time.now.utc.iso8601,
+---
- statement: "Example statement for SIWE",
+# Quickstart Guide
- nonce: Siwe::Util.generate_nonce,
+This guide will walk you through implementing Sign in with Ethereum (SIWE) authentication from scratch. By the end of this tutorial, you'll have a complete authentication system that allows users to sign in using their Ethereum wallets.
- chain_id: "1",
+## What You'll Build
- expiration_time: "",
+In this tutorial series, you'll create:
- not_before: "",
+- **SIWE Message Generation**: Learn to create properly formatted authentication messages
+- **Frontend Wallet Integration**: Connect to user wallets and request signatures
+- **Backend Verification Server**: Validate signatures and manage user sessions
+- **Complete Auth Flow**: Connect frontend and backend for seamless authentication
+- **Session Management**: Maintain user sessions across requests
- request_id: "",
+## Prerequisites
- resources: []
+Before starting this tutorial, you should have:
- })
+### Technical Knowledge
+- **JavaScript/TypeScript**: Intermediate familiarity with ES6+ features
+- **Node.js**: Experience with Node.js and npm/yarn package management
+- **Web Development**: Basic understanding of HTTP, APIs, and browser technologies
+- **Blockchain Basics**: Basic understanding of Ethereum addresses and transactions
-To parse from EIP-4361 you have to use Siwe::Message.from_message
+### Development Environment
+- **Node.js**: Version 16 or higher
+- **Package Manager**: npm or yarn installed
+- **Code Editor**: VS Code or similar IDE
+- **Web Browser**: Chrome, Firefox, or Edge with Ethereum wallet extension
-Siwe::Message.from_message
+### Optional Wallet Setup
+- **MetaMask**: Browser extension for testing (or any Ethereum wallet)
+- **Test ETH**: Small amount on testnets for transaction fees (not required for authentication)
-Siwe::Message.from_message "domain.example wants you to sign in with your Ethereum account:\n0x9D85ca56217D2bb651b00f15e694EB7E713637D4\n\nExample statement for SIWE\n\nURI: some.uri\nVersion: 1\nChain ID: 1\nNonce: k1Ne4KWzBHYEFQo8\nIssued At: 2022-02-03T20:06:19Z"
+## Tutorial Structure
-Messages can be parsed to and from JSON strings, using Siwe::Message.from_json_string and Siwe::Message.to_json_string respectively:
+This quickstart is divided into 7 progressive lessons:
-Siwe::Message.from_json_string
+### 1. [Creating a SIWE Message](creating-messages)
+Learn the fundamentals of SIWE message creation using the official library. You'll understand the message format, required fields, and security considerations.
-Siwe::Message.to_json_string
+**Estimated Time**: 15 minutes
+**What You'll Learn**: Message formatting, nonce generation, security best practices
-Siwe::Message.from_json_string "{\"domain\":\"domain.example\",\"address\":\"0x9D85ca56217D2bb651b00f15e694EB7E713637D4\",\"uri\":\"some.uri\",\"version\":\"1\",\"chain_id\":\"1\",\"nonce\":\"k1Ne4KWzBHYEFQo8\",\"issued_at\":\"2022-02-03T20:06:19Z\",\"statement\":\"Example statement for SIWE\",\"expiration_time\":\"\",\"not_before\":\"\",\"request_id\":\"\",\"resources\":[]}"
+### 2. [Frontend Setup](frontend-setup)
+Build a React frontend that connects to user wallets and requests message signatures. Covers wallet connection, address detection, and signature requests.
-Siwe::Message.new("domain.example", "0x9D85ca56217D2bb651b00f15e694EB7E713637D4", "some.uri", "1").to_json_string
+**Estimated Time**: 25 minutes
+**What You'll Learn**: Wallet integration, ethers.js usage, React implementation
-Verification and authentication is performed via EIP-191, using the address field of the SiweMessage as the expected signer. The validate method checks message structural integrity, signature address validity, and time-based validity attributes.
+### 3. [Backend Verification](backend-verification)
+Create an Express.js server that verifies SIWE signatures and manages nonces. Includes API endpoints for nonce generation and signature verification.
-begin
+**Estimated Time**: 20 minutes
+**What You'll Learn**: Signature verification, API design, nonce management
- message.validate(signature) # returns true if valid throws otherwise
+### 4. [Connecting Frontend & Backend](connect-the-frontend.md)
+Connect your frontend and backend to create a complete authentication flow. Implement proper error handling and user feedback.
-rescue Siwe::ExpiredMessage
+**Estimated Time**: 15 minutes
+**What You'll Learn**: API integration, error handling, user experience
- # Used when the message is already expired. (Expires At < Time.now)
+### 5. [Session Management](implement-sessions.md)
+Add session management to maintain user authentication state across requests. Implement login/logout functionality and protected routes.
-rescue Siwe::NotValidMessage
+**Estimated Time**: 20 minutes
+**What You'll Learn**: Session handling, authentication middleware, security
- # Used when the message is not yet valid. (Not Before > Time.now)
+### 6. [Retrieve Onchain Data](retrieve-onchain-data.mdx)
+Enhance your application by retrieving onchain data like **[ENS](https://ens.domains)** profiles, **[EFP](https://efp.app)** social graph, and [onchain holdings](resolve-onchain-holdings.md) like NFTs and Assets.
-rescue Siwe::InvalidSignature
+**Estimated Time**: 25 minutes / Integration
+**What You'll Learn**: ENS, EFP integration, resolving tokens & NFTs
- # Used when the signature doesn't correspond to the address of the message.
+## Alternative Paths
-Siwe::Message instances can also be serialized as their EIP-4361 string representations via the Siwe::Message.prepare_message method:
+Depending on your needs, you can follow different paths through this tutorial:
-Siwe::Message
+## Code Repository
-Siwe::Message.prepare_message
+All tutorial code is available in our GitHub repository:
-Siwe::Message.new("domain.example", "0x9D85ca56217D2bb651b00f15e694EB7E713637D4", "some.uri", "1").prepare_message
+```bash
+# Clone the tutorial repository
+git clone https://github.com/signinwithethereum/siwe-quickstart
+cd siwe-quickstart
-Parsing and verifying a Siwe::Message:
+# Install dependencies
+npm install
- message = Siwe::Message.from_message "https://example.com wants you to sign in with your Ethereum account:\n0xA712a0AFBFA8656581BfA96352c9EdFc519e9cad\n\n\nURI: https://example.com\nVersion: 1\nChain ID: 1\nNonce: 9WrH24z8zpiYOoBQ\nIssued At: 2022-02-04T15:52:03Z"
+# Start the development environment
+npm run dev
+```
- message.validate "aca5e5649a357cee608ecbd1a8455b4143311381636b88a66ec7bcaf64b3a4743ff2c7cc18501a3401e182f79233dc73fc56d01506a6098d5e7e4d881bbb02921c"
+Each tutorial part has its own branch with the completed code for that section.
- puts "Congrats, your message is valid"
+## Getting Help
-Sign-In with Ethereum can be found on RubyGems. For more information and package information, click
+If you run into issues during the tutorial:
-RubyGems
+- **Documentation**: Check our comprehensive [Library Documentation](../libraries/index.md)
+- **Issues**: Report bugs or request clarifications on [GitHub](https://github.com/signinwithethereum/siwe)
+- **Examples**: Browse working implementations in our [Integrations](../integrations/index.md)
-# Go
+---
-LibrariesGoA Go implementation of EIP-4361: Sign In With Ethereum.PreviousRailsNextDiscourseLast updated 3 years ago
+# Other Onchain Assets
-A Go implementation of EIP-4361: Sign In With Ethereum.
+This guide demonstrates how to pull information about a user's onchain holdings including NFTs, tokens, and other crypto assets.
-The Go implementation of Sign-In with Ethereum can be found here:
-SIWE can be easily installed in any Go project by running:
+## Implementation Steps
-go get -u github.com/spruceid/siwe-go
+### 1. Update HTML
-SIWE exposes a Message struct which implements EIP-4361.
+Modify `index.html` to include sections for both NFT and asset holdings:
-Parsing is done via the siwe.ParseMessage function:
+```html
+
+
Onchain Holdings
+
+
+
+
Token Assets
+
+
+
+
+
+
+
+
+
NFT Collection
+
+
+
+
+
+
+```
-siwe.ParseMessage
+### 2. Update JavaScript (index.js)
-var message *siwe.Message
+Add functions to retrieve and display both asset and NFT holdings:
-var err error
+#### Asset Holdings Functions
-message, err = siwe.ParseMessage(messageStr)
+```javascript
+// Element references
+const holdingsElm = document.getElementById('holdings')
+const assetsLoaderElm = document.getElementById('assetsLoader')
+const assetsTableElm = document.getElementById('assetsTable')
+const nftElm = document.getElementById('nft')
+const nftLoaderElm = document.getElementById('nftLoader')
+const nftTableElm = document.getElementById('nftTable')
+
+// Asset holdings functions
+async function getTokenBalances() {
+ try {
+ // Using Moralis API for token balances
+ const response = await fetch(
+ `https://deep-index.moralis.io/api/v2/${address}/erc20`,
+ {
+ headers: {
+ 'X-API-Key': 'your-moralis-api-key',
+ 'accept': 'application/json'
+ }
+ }
+ )
+
+ if (!response.ok) {
+ throw new Error(response.statusText)
+ }
+
+ const data = await response.json()
+
+ return data
+ .filter(token => parseFloat(token.balance) > 0)
+ .map(token => ({
+ name: token.name,
+ symbol: token.symbol,
+ balance: (parseFloat(token.balance) / Math.pow(10, token.decimals)).toFixed(4),
+ address: token.token_address,
+ decimals: token.decimals
+ }))
+ } catch (error) {
+ console.error('Failed to fetch token balances:', error)
+ return []
+ }
+}
-The function will return a nil pointer and an error if there was an issue while parsing.
+async function getETHBalance() {
+ try {
+ const balance = await provider.getBalance(address)
+ const ethBalance = ethers.utils.formatEther(balance)
+
+ return {
+ name: 'Ethereum',
+ symbol: 'ETH',
+ balance: parseFloat(ethBalance).toFixed(4),
+ address: 'native',
+ decimals: 18
+ }
+ } catch (error) {
+ console.error('Failed to fetch ETH balance:', error)
+ return null
+ }
+}
-Verification and Authentication is performed via EIP-191, using the address field of the Message as the expected signer. This returns the Ethereum public key of the signer:
+async function displayAssets() {
+ assetsLoaderElm.innerHTML = 'Loading token assets...'
+
+ try {
+ const [ethBalance, tokenBalances] = await Promise.all([
+ getETHBalance(),
+ getTokenBalances()
+ ])
+
+ const allAssets = ethBalance ? [ethBalance, ...tokenBalances] : tokenBalances
+
+ if (allAssets.length === 0) {
+ assetsLoaderElm.innerHTML = 'No token assets found'
+ return
+ }
+
+ let tableHtml = '
`
+ for (const key of keys) {
+ const value = await resolver.getText(key)
+ if (value) {
+ ensTableElm.innerHTML += `
${key}:
${value}
`
+ }
+ }
+
+ document.getElementById('ensContainer').classList = ''
+ } else {
+ welcomeElm.innerHTML = `Hello, ${address}`
+ noProfileElm.classList = ''
+ }
- cd: $home/plugins
+ welcomeElm.classList = ''
+}
+```
- cmd:
+### 3. CSS for Avatar Display
- - sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git
+Add some basic styling for the avatar:
- - sudo -E -u discourse git clone https://github.com/spruceid/discourse-siwe-auth.git # <-- added
+```css
+.avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ margin-left: 8px;
+ vertical-align: middle;
+}
-Follow the existing format of the docker_manager.git line; if it does not contain sudo -E -u discourse then insert - git clone https://github.com/spruceid/discourse-siwe-auth.git.
+.hidden {
+ display: none;
+}
-docker_manager.git
+table {
+ border-collapse: collapse;
+ width: 100%;
+}
-sudo -E -u discourse
+td {
+ border: 1px solid #ddd;
+ padding: 8px;
+}
-- git clone https://github.com/spruceid/discourse-siwe-auth.git
+td:first-child {
+ font-weight: bold;
+ background-color: #f2f2f2;
+}
+```
-Rebuild the container:
+## Key Features
-./launcher rebuild app
+- **ENS Name Lookup**: Resolves the ENS name associated with an address
+- **Avatar Display**: Shows the user's ENS avatar if available
+- **Profile Metadata**: Displays common ENS text records like email, URL, and social media
+- **Fallback Handling**: Gracefully handles addresses without ENS profiles
-To disable it either remove the plugin or uncheck discourse siwe enabled at (Admin Settings -> Plugins -> discourse-siwe -> discourse siwe enabled ).
+## ENS Text Records
-discourse siwe enabled
+The implementation checks for these common text records:
-Admin Settings -> Plugins -> discourse-siwe -> discourse siwe enabled
+- `email`: Contact email address
+- `url`: Personal or professional website
+- `description`: Bio or description
+- `com.twitter`: Twitter handle
-By default, a statement is added to the messages: Sign-in to Discourse via Ethereum. To edit this statement access the settings (same as before) and update it.
+## Error Handling
-To install and enable the plugin on your self-hosted Discourse use the :
+The code includes proper error handling for:
-This plugin uses the newest Web3Modal v2, in order to use it you need to create a free project id at and configure it in the plugin.
+- Addresses without ENS names
+- Missing or empty text records
+- Network connectivity issues
-# NextAuth.js
+---
-IntegrationsNextAuth.jsA complete open source authentication solution.PreviousDiscourseNextAuth0Last updated 2 years ago
+import FullWidthLink from '@site/src/components/full-width-link'
-A complete open source authentication solution.
+# EFP Social Graph
-is an easy-to-implement, full-stack (client/server) open-source authentication library originally designed for and serverless applications.
+
-The library provides the ability to set up a custom credential provider, which we can take advantage of in order to authenticate users using their existing Ethereum wallet via Sign-In with Ethereum (EIP-4361).
+## Resources
-The complete example can be found .
+- [EFP Documentation](https://docs.efp.app) - Complete protocol documentation
+- [EFP API Reference](https://ethidentitykit.com/docs/api) - Full API specification
+- [Ethereum Identity Kit](https://ethidentitykit.com) - React components for EFP integration
+- [EFP App](https://efp.app) - Reference implementation
-First clone the official NextAuth.js example using your terminal:
+## What is EFP?
-git clone https://github.com/nextauthjs/next-auth-example
+Ethereum Follow Protocol (EFP) is an onchain social graph protocol for Ethereum accounts. It enables users to follow other Ethereum addresses, creating a decentralized social network layer. Unlike traditional social networks, EFP stores all relationship data onchain, making it composable and censorship-resistant.
-Then, switch to the project directory:
+Key features:
+- **Decentralized Social Graph**: All follow relationships stored onchain
+- **Composable**: Can be integrated into any application
+- **Tag System**: Support for custom tags like "top8", "mute", "block"
+- **No Vendor Lock-in**: Open protocol accessible by anyone
-cd next-auth-example
+## Component Library (React)
-After cloning, modify the given .env.local.example file, and populate it with the following variables:
+You can use the Ethereum Identity Kit to integrate EFP into your application:
-.env.local.example
+
-NEXTAUTH_URL=http://localhost:3000
+## API Integration
-NEXTAUTH_SECRET=somereallysecretsecret
+### Basic User Stats
-Note: After this, rename the file to .env.local. This example will be routed to http://localhost:3000.
+Get followers and following counts for any Ethereum address:
-.env.local
+```javascript
+async function getEFPStats(address) {
+ try {
+ const response = await fetch(`https://api.ethfollow.xyz/api/v1/users/${address}/stats`)
+ const stats = await response.json()
+
+ return {
+ followers: stats.followers_count,
+ following: stats.following_count,
+ }
+ } catch (error) {
+ console.error('Error fetching EFP stats:', error)
+ return null
+ }
+}
+```
-yarn add siwe@beta ethers wagmi
+### Get User's Following List
-Now, modify pages/_app.tsx to inject the WagmiProvider component:
+Retrieve the complete list of accounts a user follows:
-pages/_app.tsx
+```javascript
+async function getUserFollowing(address, limit = 100) {
+ try {
+ const response = await fetch(
+ `https://api.ethfollow.xyz/api/v1/users/${address}/following?limit=${limit}`
+ )
+ const data = await response.json()
+
+ return data.following.map(follow => ({
+ address: follow.address,
+ ens: follow.ens,
+ avatar: follow.avatar,
+ tags: follow.tags || []
+ }))
+ } catch (error) {
+ console.error('Error fetching following list:', error)
+ return []
+ }
+}
+```
-WagmiProvider
+### Get User's Followers
-import { Session } from "next-auth"
+Retrieve the list of accounts following a user:
-import { SessionProvider } from "next-auth/react"
+```javascript
+async function getUserFollowers(address, limit = 100) {
+ try {
+ const response = await fetch(
+ `https://api.ethfollow.xyz/api/v1/users/${address}/followers?limit=${limit}`
+ )
+ const data = await response.json()
+
+ return data.followers.map(follower => ({
+ address: follower.address,
+ ens: follower.ens,
+ avatar: follower.avatar
+ }))
+ } catch (error) {
+ console.error('Error fetching followers list:', error)
+ return []
+ }
+}
+```
-import type { AppProps } from "next/app"
+## Implementation Steps
-import { WagmiConfig, createClient, configureChains, chain } from "wagmi"
+### 1. Update HTML (frontend/src/index.html)
-import { publicProvider } from "wagmi/providers/public"
+Add a section for displaying EFP social graph data:
-import "./styles.css"
+```html
+
+ `
+
+ // Get and display some recent follows
+ if (stats.following > 0) {
+ const following = await getUserFollowing(address, 5)
+ let connectionsHTML = '