Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Integrate Wallet-Based Social Logins

A developer tutorial for replacing OAuth with wallet-based authentication using EIP-4361 and other standards. Includes code for signature verification and session handling.
Chainscore © 2026
introduction
TUTORIAL

How to Integrate Wallet-Based Social Logins

A developer's guide to implementing secure, non-custodial authentication using Web3 wallets like MetaMask, WalletConnect, and Particle Network.

Wallet-based authentication replaces traditional username/password or OAuth flows with cryptographic proof of ownership. Instead of a central server verifying credentials, the user signs a unique message with their private key, proving control of their blockchain address. This method is inherently non-custodial; the user's identity is their wallet, and the service never holds their keys. Popular SDKs for integration include Web3Modal for a unified wallet connector, Clerk with Web3 support, and Magic Link for email-based wallet abstraction. The core flow involves requesting a signature from the user's wallet and verifying it on your backend.

The technical implementation follows a standard challenge-response pattern. First, your backend generates a cryptographically secure, one-time nonce (a random string) for the user's public address. Your frontend then prompts the user to sign this nonce using their wallet, such as via personal_sign in Ethereum. The signed message is sent back to your server, which uses a library like ethers.js or viem to recover the signer's address from the signature. If the recovered address matches the user's claimed public address, authentication is successful. You then issue a session token (like a JWT) linked to that address for subsequent authorized requests.

For a social-like experience that abstracts wallet complexity, services like Particle Network or Dynamic are ideal. These platforms provide SDKs that allow users to log in with familiar social accounts (Google, Twitter) or email, which then creates or connects to a non-custodial wallet in the background. The user experience feels like a traditional social login, but the underlying authentication still uses wallet signatures. This approach significantly reduces onboarding friction while maintaining the security benefits of decentralized identity, making it suitable for mainstream applications.

Security is paramount. Always generate the nonce on your backend to prevent replay attacks. Use a short expiration time (e.g., 5 minutes) for the signing request. Be explicit in the signature message shown to the user, following EIP-4361 (Sign-In with Ethereum) standards to prevent phishing. Clearly state your dApp's domain, the nonce, and the request's purpose. Never ask users to sign a transaction for login; only use message signing. Implement rate limiting on your authentication endpoint and consider using SIWE-specific libraries for robust message formatting and verification.

Here is a minimal backend verification example using ethers v6: import { ethers } from 'ethers'; async function verifySignature(address, nonce, signature) { const recoveredAddress = ethers.verifyMessage(nonce, signature); return recoveredAddress.toLowerCase() === address.toLowerCase(); }. On the frontend, using viem: import { createWalletClient, custom } from 'viem'; const client = createWalletClient({ transport: custom(window.ethereum) }); const signature = await client.signMessage({ message: nonceFromBackend, account: userAddress });. Always test with multiple wallet providers and networks during development.

Wallet-based logins enable new use cases like portable reputation, token-gated content, and seamless cross-dApp identity. The authenticated public address can query on-chain activity for Sybil resistance or eligibility checks. The next step after integration is often managing user sessions and linking off-chain profile data. This foundation is essential for building applications that are both user-friendly and aligned with Web3 principles of self-sovereignty.

prerequisites
PREREQUISITES AND SETUP

How to Integrate Wallet-Based Social Logins

This guide outlines the essential tools and initial configuration needed to implement social login functionality using Web3 wallets like MetaMask or WalletConnect.

Before writing any code, you need to set up your development environment and choose a core authentication library. The most common approach is to use Sign-In with Ethereum (SIWE), a standard that allows users to sign a standard message with their wallet to prove ownership. You will need a backend server to verify these signatures. Popular libraries include siwe for Node.js or django-siwe for Python. Ensure your project has a package manager like npm or yarn installed, and that you have a basic understanding of your chosen web framework (e.g., Express.js, Next.js, Django).

On the frontend, you must integrate a wallet connection provider. For Ethereum Virtual Machine (EVM) chains, WalletConnect v2 is highly recommended for its cross-wallet and cross-chain compatibility, supporting over 350 wallets. Alternatively, you can use a framework-specific SDK like wagmi for React or viem for a more low-level approach. You will also need to obtain a Project ID from the WalletConnect Cloud Dashboard. For a simpler, MetaMask-only integration, you can use the @metamask/detect-provider package, but this limits user choice.

Your backend requires a secure method to manage sessions and nonces. A nonce is a random string generated server-side for each login attempt to prevent replay attacks. When a user initiates login, your backend creates and stores a nonce, sends it to the frontend, and the user signs a SIWE message containing this nonce. Upon receiving the signed message, your backend verifies the signature's validity and the nonce, then creates a session token (like a JWT). You must set up a database (e.g., PostgreSQL, Redis) to store these nonces and session data securely.

For production, you must configure your application's domain. SIWE messages include the domain field (e.g., yourapp.com), and the signature verification will fail if the signing domain doesn't match. Set your NEXT_PUBLIC_DOMAIN or equivalent environment variable to your app's canonical URL. You also need to decide on a chain ID; for mainnet logins, use 1 (Ethereum), but for testing, you might use 11155111 (Sepolia). Your frontend wallet connection must be configured to request this specific chain.

Finally, plan your user flow. A typical integration involves: 1. A "Connect Wallet" button triggering the connection modal. 2. A "Sign In" button that requests the SIWE message from your backend and prompts the user's wallet to sign. 3. Backend verification and session establishment. 4. Storing the session token in an HTTP-only cookie or secure client-side storage. Test this flow thoroughly on a testnet before deploying, using tools like the Sepolia Faucet to get test ETH for gas fees.

key-concepts-text
WALLET AUTHENTICATION

Key Concepts: Signatures, Nonces, and Sessions

Wallet-based social logins replace passwords with cryptographic proofs. This guide explains the core components that make this secure: digital signatures, nonces, and session management.

Wallet-based authentication uses a user's blockchain wallet as their identity. Instead of a password, the user signs a cryptographically generated message with their private key. This digital signature proves ownership of the wallet address without exposing the private key. The server can verify this signature using standard libraries like ethers.js or viem, confirming the user controls the address they claim. This method is inherently resistant to phishing and credential stuffing attacks common with traditional logins.

A critical security component is the nonce (number used once). The server generates a unique, random string for each login attempt and presents it to the user's wallet to sign. This prevents replay attacks, where an attacker intercepts a valid signature and reuses it. Nonces are typically time-bound, expiring after a few minutes. Common implementations store a pending nonce server-side or derive it from a timestamp and a secret to avoid database state, a pattern known as stateless nonces.

Once a signature is verified, the application establishes a session. This involves issuing a session token (like a JWT) to the client, which is sent with subsequent requests. The token payload should include the verified wallet address and an expiration time. Unlike traditional sessions, there is no "password" to hash and store; authorization is based solely on the proven link between the session and the cryptographic identity. Best practices include using secure, HTTP-only cookies for token storage and implementing short-lived tokens with refresh mechanisms.

Here's a simplified code flow using Ethereum's personal_sign:

  1. Client requests login: GET /api/auth/nonce returns a fresh nonce.
  2. User signs: The wallet prompts the user to sign: Sign this message to login: ${nonce}.
  3. Client sends proof: POST /api/auth/verify with { signature, address }.
  4. Server verifies: Use ethers.verifyMessage(nonce, signature) to recover the signer address and match it to the provided address.
  5. Session creation: On success, issue a JWT and set a cookie.

Integrating with SIWE (Sign-In with Ethereum) EIP-4361 is recommended for standardization. SIWE defines a structured message format that includes the domain, nonce, expiration time, and other security fields, reducing ambiguity and improving interoperability across apps. Libraries like siwe handle message construction and parsing. This standard also helps prevent cross-site request forgery and ensures users clearly understand what they are signing.

For production, consider security nuances. Always verify the message domain matches your application's origin. Implement robust nonce management to guarantee uniqueness. Be aware of wallet-specific behaviors, such as Ledger's requirement for a specific message prefix. Finally, remember that wallet authentication proves ownership, not reputation; combining it with on-chain data or off-chain profiles can create richer user identities.

PROTOCOL OVERVIEW

Wallet Auth Standards Comparison

Comparison of major standards for integrating wallet-based authentication into web applications.

Feature / MetricEIP-4361 (Sign-In with Ethereum)EIP-712 (Typed Data)EIP-191 (Personal Sign)

Standard Type

Authentication Protocol

Structured Data Signing

Plain Text Signing

Human-Readable Messages

Off-Chain Verification

Structured Data Schema

Built-in Nonce & Chain ID

Primary Use Case

Login & Session Auth

Complex DApp Interactions

Simple Signature Requests

Typical Gas Cost for User

~21k gas

~45k gas

~21k gas

Wallet UX Support (2024)

MetaMask, Coinbase, Rainbow

Widespread

Universal

frontend-flow
IMPLEMENTING THE UI

Step 1: Frontend Login Flow

This guide details the frontend implementation for integrating wallet-based social logins, focusing on the user interface and initial authentication flow using popular Web3 libraries.

The frontend login flow begins by integrating a Web3 wallet connection library. The most common choice is WalletConnect v2, which supports over 350 wallets including MetaMask, Rainbow, and Trust Wallet. You initialize the connection by creating a project on the WalletConnect Cloud to get a Project ID, then configure the Web3Modal library. This provides a standardized UI modal where users can select their preferred wallet to connect, abstracting away the complexity of individual wallet SDKs.

Once the Web3Modal is configured, you handle the connection event. When a user selects a wallet and approves the connection, your application receives a signer object and the user's public address. This address acts as a unique identifier, similar to a username. At this point, the user is "wallet-connected" but not yet "application-authenticated." The next critical step is to prove ownership of that address by requesting a cryptographic signature.

To authenticate, your backend must generate a unique, one-time nonce (a random string) and send it to the frontend. Your application then prompts the user to sign this nonce with their connected wallet using the personal_sign method. This signature is a piece of cryptographic proof that only the holder of the wallet's private key can produce. The frontend sends both the public address and this signature back to your backend API for verification, completing the login handshake.

A robust frontend must manage the user's authentication state. After successful backend verification, you will receive a session token (like a JWT). This token should be stored securely, typically in an HTTP-only cookie or via a state management library like Zustand or Redux. The UI should then conditionally render, hiding login buttons and displaying the user's truncated address (e.g., 0x7F3a...C4e2) and a logout button.

Error handling is crucial for user experience. Your code must gracefully manage common scenarios: the user rejects the connection in their wallet, the user switches accounts or chains in MetaMask, or the network connection fails. Implement listeners for events like accountsChanged and chainChanged to update the UI state or trigger re-authentication as needed. Libraries like Wagmi or Ethers.js provide helpful hooks and providers to streamline this state management.

For a production-ready implementation, consider enhancing the basic flow. This includes supporting multi-chain logins by specifying chain IDs, implementing session persistence so users remain logged in across page refreshes, and adding SIWE (Sign-In with Ethereum) for a standardized message format that improves security and user clarity about what they are signing. The complete frontend code establishes the foundation for the backend verification process covered in the next step.

backend-verification
SECURITY CORE

Step 2: Backend Signature Verification

After receiving the signed message from the user's wallet, your backend must cryptographically verify its authenticity to prevent spoofing and establish a secure session.

The core of wallet-based authentication is signature verification. Your server receives a payload containing the user's public address, the original message they signed (like a login challenge), and the resulting digital signature. Using the Elliptic Curve Digital Signature Algorithm (ECDSA), specifically the secp256k1 curve common to Ethereum and EVM chains, you can cryptographically prove that the signature was generated by the private key corresponding to the claimed public address. This process ensures the user truly controls the wallet, without ever exposing their private key to your application.

To perform verification, you need a library that supports ECDSA recovery. For Node.js backends, ethers.js v6 or viem are standard choices. The verification function takes the original message, the signature, and recovers the public address that created it. You then compare this recovered address to the one sent by the client. A match confirms authenticity. It's critical that your server reconstructs the exact message string that was presented to the user's wallet; any discrepancy, even an extra space, will cause verification to fail.

Here is a practical example using ethers.js: import { ethers } from 'ethers'; async function verifySignature(message, signature, claimedAddress) { const recoveredAddress = ethers.verifyMessage(message, signature); return recoveredAddress.toLowerCase() === claimedAddress.toLowerCase(); }. This function uses ethers.verifyMessage which automatically hashes the message according to Ethereum's \x19Ethereum Signed Message:\n standard, a crucial step for security. Always normalize addresses to lowercase for comparison.

For production systems, implement additional security checks. Replay attacks are a major concern: a malicious actor could intercept a valid signature and reuse it. Mitigate this by making the signed message unique and time-bound. Include a nonce (a random number used once) and a timestamp in the challenge message. Your backend should verify the nonce hasn't been used before and that the timestamp is recent (e.g., within 5 minutes). Store used nonces in a short-lived cache or database to prevent reuse.

After successful verification, your backend should establish a session. Generate a standard session token (like a JWT) and associate it with the verified Ethereum address. This token is what your frontend application will use for subsequent authenticated API calls. The verified address becomes the user's primary identifier in your system. You can then use this to check permissions, link to off-chain data, or interact with smart contracts on the user's behalf, depending on your application's needs.

session-management
IMPLEMENTING SECURE SESSIONS

Step 3: Session and Logout Management

After a user authenticates with their wallet, your application must manage their session state securely and provide a clear logout mechanism.

A wallet-based session is fundamentally different from a traditional cookie-based session. Instead of a server-managed session ID, the session is validated cryptographically on each request. The typical flow involves the user signing a SIWE (Sign-In with Ethereum) message. Your backend verifies this signature, confirms the signer's address matches a registered user, and then issues a short-lived JWT (JSON Web Token). This JWT, containing the user's public address and permissions, is then used for authenticating subsequent API calls until it expires.

For security, JWTs should have a short expiration time (e.g., 15-60 minutes). Implement a refresh token mechanism to maintain a seamless user experience without compromising security. The refresh token, stored securely (preferably in an HTTP-only cookie), is used to obtain a new JWT without requiring the user to sign a message again. This pattern balances user convenience with the principle of least privilege, limiting the damage if a JWT is compromised.

Logout must be handled on both the client and server sides. On the client, clear the JWT from memory or local storage and invalidate any application state. On the server, you need a token blacklist or a mechanism to invalidate the refresh token. For a more robust solution, especially for sensitive applications, consider using session nonces. Increment a nonce stored on your backend for the user's account; any JWT signed with an old nonce is automatically rejected, providing instant logout across all devices.

Here is a simplified backend example for verifying a SIWE message and issuing a JWT using Node.js and the siwe library:

javascript
import { SiweMessage } from 'siwe';
import jwt from 'jsonwebtoken';

async function verifySiweAndCreateSession(message, signature) {
  // 1. Reconstruct and validate the SIWE message
  const siweMessage = new SiweMessage(message);
  const { data: fields } = await siweMessage.verify({ signature });

  // 2. Check if the address is authorized (e.g., in your DB)
  const user = await db.user.findUnique({ where: { address: fields.address } });
  if (!user) throw new Error('User not found');

  // 3. Issue a short-lived JWT
  const accessToken = jwt.sign(
    { sub: user.id, address: fields.address },
    process.env.JWT_SECRET,
    { expiresIn: '15m' }
  );

  // 4. Create & store a refresh token
  const refreshToken = crypto.randomBytes(40).toString('hex');
  await storeRefreshToken(user.id, refreshToken);

  return { accessToken, refreshToken };
}

Always inform users about active sessions. Provide a UI section where they can see their logged-in devices and revoke specific sessions. This is implemented by storing session metadata (device, IP, last active) with each refresh token. When a user revokes a session, delete that specific refresh token from your database. For maximum security, especially in financial dApps, you can integrate with smart contract-based session keys via standards like ERC-4337, allowing users to grant limited, expiring permissions to a frontend.

WALLET LOGINS

Frequently Asked Questions

Common technical questions and solutions for developers integrating wallet-based social logins using protocols like Sign-In with Ethereum (SIWE) and WalletConnect.

Sign-In with Ethereum (EIP-4361) is a standard that allows users to authenticate with a website using their Ethereum wallet instead of a traditional username/password. The process involves the user signing a structured message (the SIWE message) with their private key. This message contains the domain of the website, a statement, a nonce, and an expiration time.

Upon receiving the signature, the backend server can verify it using the signer's public address and the original message. This proves the user controls the wallet without exposing their private key. The verified address then serves as a unique, cryptographically secure user identifier for the session. This method is widely supported by wallets like MetaMask and is foundational for decentralized identity.

conclusion-next-steps
IMPLEMENTATION GUIDE

Conclusion and Next Steps

You have explored the core concepts and technical steps for integrating wallet-based social logins. This final section consolidates key takeaways and outlines practical next steps for developers.

Integrating wallet-based social logins moves your application beyond traditional Web2 authentication by leveraging self-custody and user sovereignty. The primary workflow involves generating a cryptographic signature (like a SIWE message) with the user's wallet, verifying it on your backend, and then issuing a session token. This process replaces password databases with cryptographic proof, significantly reducing attack surfaces and data liability. Key security considerations include validating the signature's domain, nonce, and expiration, and securely managing the session lifecycle.

For production deployment, your next steps should focus on robustness and user experience. Implement proper error handling for wallet connection failures and signature rejections. Consider using established libraries like Sign-In with Ethereum (SIWE) kits or services from providers like Privy, Dynamic, or Corbado to abstract complex cryptographic logic. These tools often handle multi-chain support, nonce management, and session persistence, allowing you to focus on core application logic. Always conduct thorough testing on testnets before mainnet deployment.

To deepen your implementation, explore advanced patterns. You can extend the basic login to include role-based permissions stored on-chain, perhaps using token-gating libraries. For a seamless multi-chain experience, investigate account abstraction (ERC-4337) for sponsoring gas fees or enabling social recovery. Monitor the evolving standards landscape, particularly around EIP-4361 (SIWE) and EIP-7212 for secp256r1 support, which could enable native passkey integration. The official Sign-In with Ethereum documentation is an essential resource for specification details and reference implementations.

Finally, measure the success of your integration through key metrics: wallet connection success rate, time-to-first-signature, and user retention post-login. Solicit feedback on the onboarding flow. The goal is to make Web3 access feel intuitive and secure, removing friction without compromising on the foundational principles of decentralization. By implementing wallet-based logins, you are not just adding a feature—you are building a gateway for users to truly own their digital identity within your application.

How to Integrate Wallet-Based Social Logins (EIP-4361) | ChainScore Guides