Skip to main content

User Authentication

Connect and manage user wallet sessions in your dApp. This guide covers both Wagmi (recommended) and vanilla JavaScript approaches.

Connect to MetaMask - Step 1Connect to MetaMask - Step 2Connect to MetaMask - Step 3Connect to MetaMask - Step 4Connect to MetaMask - Step 9

With MetaMask SDK, you can:

  • Connect users' wallets to your dApp
  • Access user accounts (addresses)
  • Handle connection states (connected/disconnected)
  • Listen for account changes in real-time
  • Manage wallet sessions (connect/disconnect)
  • Support multiple wallet types (extension, mobile app)

Using Wagmi

Wagmi provides a simple, hook-based approach for handling wallet connections:

import { useAccount, useConnect, useDisconnect } from 'wagmi'

function ConnectWallet() {
const { address, isConnected, status } = useAccount()
const { connectors, connect, isPending } = useConnect()
const { disconnect } = useDisconnect()

if (isConnected) {
return (
<div>
<div>Connected to {address}</div>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
)
}

return (
<div>
{connectors.map((connector) => (
<button
key={connector.uid}
onClick={() => connect({ connector })}
disabled={isPending}
>
{isPending ? 'Connecting...' : `Connect ${connector.name}`}
</button>
))}
</div>
)
}

Handle Account Changes

Wagmi provides a dedicated hook for handling account lifecycle events:

import { useAccountEffect } from 'wagmi'

function WatchAccount() {
useAccountEffect({
onConnect(data) {
console.log('Connected!', {
address: data.address,
chainId: data.chainId,
isReconnected: data.isReconnected
})
},
onDisconnect() {
console.log('Disconnected!')
}
})

return <div>Watching for account changes...</div>
}

Using Vanilla JavaScript

If you're not using React, here's how to implement authentication directly:

import { MetaMaskSDK } from '@metamask/sdk';

// Initialize SDK
const MMSDK = new MetaMaskSDK();
const provider = MMSDK.getProvider();

// Connect wallet
async function connectWallet() {
try {
// Disable button while request is pending
document.getElementById('connectBtn').disabled = true;

const accounts = await provider.request({
method: 'eth_requestAccounts'
});

const account = accounts[0];
console.log('Connected:', account);

// Update UI
document.getElementById('status').textContent = `Connected: ${account}`;
document.getElementById('connectBtn').style.display = 'none';
document.getElementById('disconnectBtn').style.display = 'block';
} catch (err) {
if (err.code === 4001) {
console.log('User rejected connection');
} else {
console.error(err);
}
} finally {
document.getElementById('connectBtn').disabled = false;
}
}

// Handle account changes
provider.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
// User disconnected
document.getElementById('status').textContent = 'Not connected';
document.getElementById('connectBtn').style.display = 'block';
document.getElementById('disconnectBtn').style.display = 'none';
} else {
// Account changed
document.getElementById('status').textContent = `Connected: ${accounts[0]}`;
}
});

HTML

<div>
<div id="status">Not connected</div>
<button id="connectBtn" onclick="connectWallet()">Connect MetaMask</button>
<button id="disconnectBtn" style="display: none" onclick="disconnectWallet()">
Disconnect
</button>
</div>

Best Practices

  1. User Interaction

    • Only trigger connection requests in response to user actions (like button clicks)
    • Never auto-connect on page load
    • Provide clear feedback during connection states
  2. Error Handling

    • Handle common errors like user rejection (code 4001)
    • Provide clear error messages to users
    • Fallback gracefully when MetaMask is not installed
  3. Account Changes

    • Always listen for account changes
    • Update your UI when accounts change
    • Handle disconnection events
  4. Chain Support

    • Listen for network/chain changes
    • Verify the current chain meets your requirements
    • Provide clear messaging when users need to switch networks
    • Learn more here: Network Management

Common Errors

Error CodeDescriptionSolution
4001User rejected requestShow a message asking user to approve the connection
-32002Request already pendingDisable connect button while request is pending
-32603Internal JSON-RPC errorCheck if MetaMask is properly installed

Next Steps