The **MetaMask Chrome Extension** serves as the critical bridge for **developers**, injecting the Ethereum API (`window.ethereum`) needed for **Web3 Login** and decentralized application (DApp) functionality.
This guide provides the installation steps, security protocols, and essential code snippets required to integrate MetaMask authentication into your **Decentralized Applications** (DApps).
1. Download & Environment Setup
1.1 Installation for **Chrome Users**
- Navigate to the **MetaMask Chrome Extension** page on the official Chrome Web Store.
- Click "Add to Chrome" and confirm the necessary permissions.
- Post-installation, open the extension and complete the setup: "Create a new Wallet" or "Import Secret Recovery Phrase."
- **Developer Tip:** Ensure you pin the extension to your browser toolbar for quick access and network switching.
1.2 Key Security: The Secret Recovery Phrase
As a **developer**, you may use multiple wallets (e.g., testnet, staging, mainnet). The **Secret Recovery Phrase** is the foundational **security** layer. **Never** hardcode or store this phrase in any part of your application code or version control.
Private Key Exposure:
Directly exposing the **Secret Recovery Phrase** or Private Keys via code is the fastest way to compromise all connected accounts. All authentication must flow through the `window.ethereum` API.
Testing Best Practice:
For development, create dedicated, funded test accounts (on Sepolia or other testnets) that hold no real assets. This minimizes risk during integration testing.
2. The **Web3 Login** / Authentication Flow
2.1 The Connection Check
Before initiating **Web3 Login**, check for the presence of the provider injected by the **MetaMask Chrome Extension**.
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask is installed!');
} else {
// Prompt user to download/install
console.warn('MetaMask not detected.');
}
2.2 Initiating **Login** (eth_requestAccounts)
The core **Web3 Login** method is `eth_requestAccounts`. This pops up the MetaMask modal asking the user to connect their wallet.
const connectWallet = async () => {
try {
// Triggers the connection modal
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const userAddress = accounts[0];
// Now you have the user's public address (their identity)
console.log("Connected user:", userAddress);
return userAddress;
} catch (error) {
console.error("User denied account access or error:", error);
}
};
2.3 Handling Address & Network Changes
To maintain a consistent **Web3 Login** session, listen for events. If the user changes the selected account or switches the network, your DApp must react.
// Account change listener
window.ethereum.on('accountsChanged', (newAccounts) => {
if (newAccounts.length === 0) {
// User logged out/disconnected all accounts
console.log("Logged out.");
} else {
// Update UI with new account
console.log("New address:", newAccounts[0]);
}
});
// Network change listener
window.ethereum.on('chainChanged', (chainId) => {
window.location.reload(); // Recommended for complex DApps
});
3. Interacting with the Ethereum API
3.1 Sending Transactions (eth_sendTransaction)
This method is used for sending **Ether** or interacting with smart contracts (changing state). The **MetaMask Chrome Extension** handles the crucial step of calculating and displaying the **Gas Fees** before the user signs.
const sendEth = async (toAddress, amountInWei) => {
try {
const txHash = await window.ethereum.request({
method: 'eth_sendTransaction',
params: [{
from: await connectWallet(), // The sender's address
to: toAddress,
value: amountInWei, // Value sent in Wei
gas: '0x5208', // 21000 Gwei (Standard ETH transfer)
}],
});
console.log('Transaction sent:', txHash);
} catch (error) {
console.error('Transaction failed:', error);
}
};
3.2 Off-Chain **Security** with Message Signing
To authenticate users to a traditional server (e.g., a database) without paying **Gas Fees** for a transaction, use message signing. The user signs a specific, readable message (e.g., "Login to DApp XYZ") with their private key, proving they own the account. This is the bedrock of non-custodial **Web3 Login**.
const signMessage = async (message) => {
const from = await connectWallet();
const msg = `0x${Buffer.from(message, 'utf8').toString('hex')}`;
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [msg, from],
});
// Signature is sent to your backend for verification
return { signature, address: from };
};
3.3 Custom RPC & Network Switching (Developer Control)
Modern DApps often deploy on Layer 2 networks like Polygon, Arbitrum, or Optimism due to lower **Gas Fees**. The **MetaMask Chrome Extension** allows **developers** to programmatically prompt the user to add or switch to a new network using `wallet_addEthereumChain` and `wallet_switchEthereumChain`.
const switchNetwork = async (chainId) => {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId }], // e.g., '0x89' for Polygon Mainnet
});
} catch (error) {
if (error.code === 4902) {
// Error 4902 means the chain hasn't been added yet - prompt add
await addCustomNetwork();
} else {
console.error(error);
}
}
};
4. Advanced Developer **Security** and User Experience
4.1 Preventing Phishing/Spoofing
- **Front-end:** Always use the official **MetaMask Chrome Extension** documentation for API calls.
- **User Education:** Include warnings in your DApp's UI about verifying URLs and protecting their **Secret Recovery Phrase**.
- **EIP-712:** For complex data signing, utilize EIP-712 to make the signing payload more human-readable, improving user **security**.
- **Gas Fees:** Clearly display the cost of a transaction, separating the value of the action from the required **Gas Fees**.
4.2 UX and Fallback Logic
- **No Provider Fallback:** If `window.ethereum` is undefined, provide a clear link to the **MetaMask Chrome Extension** download page for **Chrome Users**.
- **Mobile Support:** Recognize that mobile users will likely use MetaMask's in-app browser or WalletConnect, requiring different connection logic.
- **Transaction Status:** Implement robust polling or listeners to give real-time updates on transaction mining status instead of assuming instant confirmation.
- **Error Handling:** Implement `try...catch` blocks for all `window.ethereum.request` calls, especially for user rejections (error code 4001).
5. Developer Troubleshooting FAQs
Q: `window.ethereum` is undefined even with the **MetaMask Chrome Extension** installed. Why? ▼
A: Check if you are running the check too early, before the DOM is fully loaded, or if the user is using a browser that explicitly blocks extension injection (like some mobile browsers). Ensure your connection logic runs inside an `async` function and is triggered by a user action (like clicking a **Web3 Login** button). Always test on official environments like a local server, not a raw file path.
Q: My users complain about high **Gas Fees**. How can I address this programmatically? ▼
A: High **Gas Fees** are a network issue, not a MetaMask issue. Your options are: 1) Switch to a Layer 2 network and prompt the user via `wallet_addEthereumChain` (Section 3.3). 2) Optimize your smart contract code to reduce the computational cost of transactions. 3) For off-chain actions, prioritize using message signing (`personal_sign`) instead of transactions where appropriate.
Q: My connection request fails with error code 4001. ▼
A: Error 4001 (`user rejected request`) is the most common rejection during **Web3 Login** or transaction signing. This is expected user behavior. Handle this gracefully by displaying a non-disruptive message to the user (e.g., "Connection required to proceed") without breaking your application flow. Do not use `alert()` or `confirm()`.
Q: How do I know which network the user is currently on? ▼
A: Use the `eth_chainId` RPC method to retrieve the chain ID (in hex format) and compare it against your known values (e.g., `0x1` for Ethereum Mainnet). You must listen to the `chainChanged` event (Section 2.3) to dynamically update your DApp when the user manually switches the network in the **MetaMask Chrome Extension**.
Q: What is the difference between `eth_accounts` and `eth_requestAccounts`? ▼
A: `eth_requestAccounts` triggers the MetaMask connection modal, asking the user for permission to connect—this is your primary **Web3 Login** method. `eth_accounts` checks if the user *is already connected* to your DApp without prompting them. **Developers** should use `eth_accounts` on page load to check session status, and `eth_requestAccounts` only when a connection is necessary.