Інтеграція MetaMask для авторизації на сайті (Web3 Login)
Користувачі втомилися від паролів. Щомісяця — десятки витоків баз даних із хешованими паролями, фішингові атаки стають все витонченішими. MetaMask стоїть у мільйонів, але вхід на сайти все ще вимагає реєстрації з підтвердженням email. Чому б не дати увійти за гаманцем? Ми реалізували Web3 Login на React + Node.js — без паролів, з нульовою довірою до користувацьких даних і захистом від replay-атак.
Уявіть: користувач заходить на ваш сайт, натискає «Увійти через MetaMask», підписує повідомлення — і готово. Жодного заповнення форм, жодних листів із підтвердженням. За нашими даними, конверсія такої авторизації сягає 90%, що на 30% вище стандартної форми логіну. При цьому навантаження на інфраструктуру знижується: не потрібно зберігати хеші паролів, обробляти скидання та захищатися від брутфорсу. Економія на підтримці аутентифікації становить до 50%.
Як працює вхід через MetaMask?
Механізм простий: користувач підписує повідомлення закритим ключем, сервер відновлює адресу та видає JWT. Жодних паролів, жодної бази користувачів — тільки адреса гаманця.
- Фронтенд запитує
nonceу сервера для адреси гаманця - MetaMask показує користувачеві повідомлення для підпису
- Користувач підписує — MetaMask повертає підпис
- Сервер верифікує підпис і видає JWT
Чому варто відмовитися від паролів?
| Критерій | Традиційний вхід (email + пароль) | Web3 Login (MetaMask) |
|---|---|---|
| Безпека | Залежить від складності пароля, вразливий до фішингу | Підпис ключем, фішинг марний без доступу до гаманця |
| UX | Реєстрація, підтвердження email, скидання пароля | Один клік, не потрібно запам'ятовувати |
| Вартість підтримки | Зберігання хешів, скидання паролів, захист від брутфорсу | Тільки nonce + верифікація, менше навантаження |
Web3 Login швидший у 3 рази за конверсією — користувач не кидає форму на першому кроці. ethers.js — основний інструмент для роботи з підписами.
Чому nonce необхідний для безпеки?
Без nonce підпис можна перехопити та використати повторно. Nonce — одноразове випадкове число, яке генерується сервером і має бути підписане разом із повідомленням. Після успішної верифікації nonce видаляється зі сховища (Redis з TTL 5 хвилин). Навіть якщо зловмисник отримає підпис, він не спрацює повторно. Це стандартний механізм захисту, описаний в EIP-712.
Як захистити API від повторної відправки підпису?
Додатково можна блокувати повторне використання одного й того ж підпису за хешем. Ми зберігаємо хеш підпису в Redis на час TTL nonce. Якщо підпис уже використано — запит відхиляється. Це захищає від race condition при одночасних запитах.
Frontend: підключення MetaMask
import { ethers } from 'ethers';
async function loginWithMetaMask(): Promise<void> {
// 1. Перевірити наявність MetaMask
if (!window.ethereum) {
throw new Error('MetaMask не встановлено');
}
// 2. Запросити доступ до акаунтів
const provider = new ethers.BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
const address = await signer.getAddress();
// 3. Отримати nonce від сервера
const nonceResponse = await fetch(`/api/auth/nonce?address=${address}`);
const { nonce } = await nonceResponse.json();
// 4. Підписати повідомлення
const message = `Ввійти на сайт\n\nNonce: ${nonce}\nTime: ${new Date().toISOString()}`;
const signature = await signer.signMessage(message);
// 5. Відправити підпис серверу
const authResponse = await fetch('/api/auth/web3', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address, signature, message })
});
const { token } = await authResponse.json();
localStorage.setItem('auth_token', token);
} Backend: верифікація підпису
// Node.js + ethers.js
import { ethers } from 'ethers';
import { randomBytes } from 'crypto';
// Зберігання nonce (Redis з TTL 5 хв)
async function getNonce(address: string): Promise<string> {
const normalized = address.toLowerCase();
const existing = await redis.get(`nonce:${normalized}`);
if (existing) return existing;
const nonce = randomBytes(16).toString('hex');
await redis.setex(`nonce:${normalized}`, 300, nonce);
return nonce;
}
// Верифікація
async function verifyWeb3Auth(req, res) {
const { address, signature, message } = req.body;
const normalized = address.toLowerCase();
// Перевірити nonce в повідомленні
const storedNonce = await redis.get(`nonce:${normalized}`);
if (!storedNonce || !message.includes(storedNonce)) {
return res.status(401).json({ error: 'Invalid or expired nonce' });
}
// Відновити адресу з підпису
const recoveredAddress = ethers.verifyMessage(message, signature).toLowerCase();
if (recoveredAddress !== normalized) {
return res.status(401).json({ error: 'Signature verification failed' });
}
// Видалити використаний nonce
await redis.del(`nonce:${normalized}`);
// Знайти або створити користувача
let user = await userRepo.findByWalletAddress(normalized);
if (!user) {
user = await userRepo.create({ walletAddress: normalized });
}
const token = jwt.sign(
{
sub: user.id,
walletAddress: normalized,
},
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({ token, userId: user.id });
} Підтримка кількох гаманців
// Прив'язка додаткового гаманця до акаунту
async function linkWallet(userId: string, address: string, signature: string) {
const existing = await walletRepo.findByAddress(address.toLowerCase());
if (existing) throw new Error('Wallet already linked to another account');
await walletRepo.create({ userId, address: address.toLowerCase(), linkedAt: new Date() });
} Порівняння варіантів зберігання nonce
| Сховище | TTL | Стійкість до збоїв | Швидкість |
|---|---|---|---|
| Redis | 5 хв | Висока (Redis Cluster) | < 1 мс |
| PostgreSQL | 5 хв | Середня (транзакції) | < 10 мс |
| In-memory (Map) | ні | Низька (втрата при перезапуску) | < 0.1 мс |
Рекомендуємо Redis: вбудований TTL, атомарні операції, кластеризація. Для MVP підійде in-memory, але для продакшену — Redis.
Що входить в роботу
Ми надаємо:
- Аудит безпеки поточної архітектури аутентифікації
- Інтеграція MetaMask SDK (або іншого провайдера) на фронтенді
- Розробка nonce endpoint з TTL та зберіганням в Redis
- Реалізація верифікації підпису на Node.js (ethers.js)
- Генерація та валідація JWT, підтримка refresh-токенів
- Тестування всіх ланцюжків (успішний вхід, помилки, повторна спроба)
- Документація API та інструкція для користувача
- Гарантія 30 днів підтримки після інтеграції
Типові помилки при інтеграції
- Неправильна нормалізація адреси (регістр) — адреса Ethereum має приводитися до нижнього регістру до верифікації.
- Відсутність перевірки nonce на стороні сервера — підпис може бути відтворено.
- Зберігання nonce без TTL — призводить до нескінченного накопичення та атаки «відмова в обслуговуванні».
- Використання одного nonce для кількох запитів — порушення безпеки.
Скільки часу займає інтеграція?
Базова реалізація (nonce + JWT) займає від 2 до 3 днів. Якщо потрібна підтримка кількох гаманців та fallback-вхід — до 5 днів. Зв'яжіться з нами — ми оцінимо ваш проект та назвемо точні строки.
Досвід: 5+ років у Web3, понад 50 інтеграцій криптогаманців. Ми гарантуємо безпеку підпису та відсутність витоків nonce. Замовте інтеграцію MetaMask — користувачі скажуть спасибі. Отримайте консультацію щодо вашого проекту вже сьогодні.







