Аутентифікація в Етеріумі
Якщо ви перейшли з традиційної веброзробки, ви звикли до входу за допомогою імені користувача та пароля, потоків OAuth і сесійних файлів cookie. Аутентифікація в Етеріумі працює інакше — і багато в чому простіше.
В Етеріумі користувач підтверджує свою особу шляхом підписання повідомлення за допомогою свого гаманця. Немає паролів, які потрібно зберігати. Немає бази даних облікових даних, яка може витекти. Лише криптографія.
Чим це відрізняється від Веб2?
| Веб2 | Етеріум |
|---|---|
| Ім'я користувача + пароль | Адреса гаманця + підпис |
| Сервер зберігає облікові дані | Користувач володіє приватним ключем |
| Сесії керуються через cookie / JWT | Сесії починаються з позамережевого підпису гаманця |
| «Увійти через Google» | «Увійти через Етеріум» |
| Процеси скидання пароля | Відновлення через сід-фразу |
Фундаментальна зміна: у Веб2 вас аутентифікує централізований сервер. В Етеріумі ви аутентифікуєте себе самі, доводячи, що контролюєте певну адресу — і будь-хто може перевірити це незалежно.
Передумови
Переконайтеся, що ви розумієте:
- Акаунти Етеріуму та як вони працюють
- Що таке гаманець і як його підключити
- Основи криптографії з відкритим і приватним ключами
Як працює аутентифікація на основі гаманця
Основний процес простий:
- Ваш децентралізований застосунок (dapp) просить користувача підключити свій гаманець (через МетаМаск, Rainbow, WalletConnect тощо)
- Гаманець ділиться адресою Етеріуму користувача — це його публічний ідентифікатор
- Ваш dapp генерує унікальне повідомлення (нонс або виклик)
- Користувач підписує повідомлення своїм приватним ключем (це відбувається всередині гаманця)
- Ваш бекенд перевіряє підпис на відповідність заявленій адресі
- Якщо підпис дійсний, користувач проходить аутентифікацію
Жоден пароль не вводився, не зберігався і не передавався.
Вхід через Етеріум (EIP-4361)
EIP-4361 (відкривається в новій вкладці) визначає стандартний формат повідомлення для входу через Етеріум, який зазвичай називають SIWE (Sign-In with Ethereum). Він замінює несистемне підписання повідомлень на структурований, безпечний стандарт.
Повідомлення SIWE виглядає так:
example.com wants you to sign in with your Ethereum account:
0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B
I accept the Terms of Service: https://example.com/tos
URI: https://example.com/login
Version: 1
Chain ID: 1
Nonce: 32891757
Issued At: 2024-06-12T14:30:00Z
Ключові особливості SIWE:
- Прив'язка до домену — повідомлення містить домен, що запобігає фішингу
- ID ланцюга (Chain ID) — вказує, для якої мережі дійсний підпис
- Нонс — запобігає атакам повторного відтворення (replay attacks)
- Термін дії — необов'язкова часова мітка, що обмежує вікно дійсності
- Ресурси — необов'язкові URI для доступу з обмеженою областю дії
Бібліотеки SIWE
- siwe (відкривається в новій вкладці) — офіційна реалізація на TypeScript від Spruce
- siwe-rs (відкривається в новій вкладці) — реалізація на Rust
- siwe-go (відкривається в новій вкладці) — реалізація на Go
Приклад: вхід на стороні клієнта за допомогою siwe
import { SiweMessage } from 'siwe'
import { BrowserProvider } from 'ethers'
async function signIn() {
const provider = new BrowserProvider(window.ethereum)
const signer = await provider.getSigner()
const address = await signer.getAddress()
// 1. Отримайте нонс з вашого бекенду
const { nonce } = await fetch('/api/auth/nonce').then(r => r.json())
// 2. Створіть та підпишіть SIWE повідомлення
const message = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in to My Dapp',
uri: window.location.origin,
version: '1',
chainId: 1,
nonce,
})
const signature = await signer.signMessage(message.prepareMessage())
// 3. Відправте на бекенд для перевірки
await fetch('/api/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, signature }),
})
}
Приклад: перевірка на стороні сервера (Node.js)
import { SiweMessage, generateNonce } from 'siwe'
// Видайте нонс та збережіть його в сесії, щоб /verify міг перевірити його пізніше
app.get('/api/auth/nonce', (req, res) => {
req.session.nonce = generateNonce()
res.json({ nonce: req.session.nonce })
})
app.post('/api/auth/verify', async (req, res) => {
try {
const { message, signature } = req.body
const siweMessage = new SiweMessage(message)
const { success, data } = await siweMessage.verify({
signature,
nonce: req.session.nonce,
})
if (success) {
// data.address — це перевірена адреса Етеріум
// Створіть сесію або JWT для користувача
req.session.address = data.address
res.json({ ok: true, address: data.address })
}
} catch {
res.status(401).json({ error: 'Invalid signature' })
}
})
Бібліотеки для підключення гаманця
Перед аутентифікацією потрібно, щоб користувач підключив свій гаманець. Ці бібліотеки полегшують цей процес:
- RainbowKit (відкривається в новій вкладці) — готовий до використання компонент React із гарним інтерфейсом
- ConnectKit (відкривається в новій вкладці) — готове модальне вікно для підключення гаманця
- AppKit (WalletConnect) (відкривається в новій вкладці) — підключення мультиланцюгових гаманців із вбудованим SIWE
- Wagmi (відкривається в новій вкладці) — бібліотека хуків React із
useAccount,useConnect
Перевірка підписів вручну
Якщо ви не бажаєте використовувати SIWE, ви можете перевіряти підписи безпосередньо:
import { verifyMessage } from 'ethers'
// Повідомлення, яке підписав користувач
const message = `Sign in to My Dapp. Nonce: ${storedNonce}`
// Відновіть адресу підписанта з підпису
const recoveredAddress = verifyMessage(message, signature)
// Порівняйте із заявленою адресою
if (recoveredAddress.toLowerCase() === claimedAddress.toLowerCase()) {
// Автентифікація успішна
}
Важливі зауваження щодо безпеки
- Завжди використовуйте нонс — це запобігає атакам повторного відтворення, коли використовується старий підпис
- Включайте домен — це запобігає використанню підписів на інших сайтах
- Перевіряйте термін дії — підписи повинні мати обмежене вікно дійсності
- Використовуйте SIWE (EIP-4361), коли це можливо — він автоматично обробляє все вищезазначене
- Ніколи не розкривайте приватні ключі — підписання відбувається всередині гаманця; ваш застосунок бачить лише результат
Керування сесіями
Після аутентифікації вам усе ще потрібні сесії — так само, як і у Веб2. Поширені патерни:
- Токени JWT — випускайте JWT після перевірки підпису, використовуйте для запитів до API
- Сесії на стороні сервера — зберігайте перевірену адресу в сесійному файлі cookie
- SIWE з ресурсами — визначайте токени доступу з обмеженою областю дії, прив'язані до конкретних URI
Ключова відмінність від Веб2: адреса Етеріуму користувача є його постійною ідентифікацією. Він може використовувати її в будь-якому dapp без створення нового акаунта.
Децентралізована ідентифікація
Аутентифікація в Етеріумі є частиною ширшого руху до суверенної ідентифікації (self-sovereign identity). Стандарти та проєкти в цій сфері включають:
- Служба імен Етеріуму (ENS) (відкривається в новій вкладці) — зрозумілі для людини імена (наприклад,
vitalik.eth), які перетворюються на адреси - Служба атестації Етеріуму (EAS) (відкривається в новій вкладці) — ончейн-атестації щодо ідентифікації та облікових даних
- Децентралізовані ідентифікатори W3C (DIDs) (відкривається в новій вкладці) — глобальний стандарт для верифікованої децентралізованої ідентифікації
- Ceramic Network (відкривається в новій вкладці) — децентралізовані потоки даних, прив'язані до DID
Додаткові матеріали
- EIP-4361: Вхід через Етеріум (відкривається в новій вкладці)
- Документація SIWE (відкривається в новій вкладці)
- Вхід через Етеріум на Auth0 (відкривається в новій вкладці)
- Документація з аутентифікації Reown AppKit (відкривається в новій вкладці)
- Документація ENS (відкривається в новій вкладці)