Сегодня мы напишем легкий сниппет для авторизации пользователей на сайте при помощи кошелька Metamask.
Сниппет будет состоять из двух частей — django template
и django view
.
Начнем с django template
.
Допустим у нас есть шаблон для авторизации templates/login.html
Создадим кнопку авторизации
при нажатии на которую будет вызван Metamask.
<button type="button" onclick="web3Login()"> Log in with Metamask </button>
Создадим обслуживающий скрипт.
<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js" type="application/javascript"></script> <script> function web3Login() { /* Подписываем сообщение */ try { window.ethereum.enable().then(function () { provider = new ethers.providers.Web3Provider(window.ethereum); provider.getNetwork().then(function (result) { if (result['chainId'] != 1) { alert('Switch to Mainnet!'); } else { provider.listAccounts().then(function (result) { accountAddress = result[0]; signer = provider.getSigner(); signer.signMessage("Sign to auth {{ csrf_token }}").then((signature) => {web3LoginBackend(accountAddress, signature)}); }) } }) }) } catch { alert('Please install MetaMask for your browser.') } } function web3LoginBackend(accountAddress, signature) { /* Отправляем сообщение на функцию авторизации/регистрации */ var form = document.createElement('form'); form.action = '{% url "main:auth_web3" %}'; // TODO замените на свой адрес form.method = 'POST'; var input = document.createElement('input'); input.type = 'hidden'; input.name = 'csrfmiddlewaretoken'; input.value = '{{ csrf_token }}'; form.appendChild(input); var input = document.createElement('input'); input.type = 'hidden'; input.name = 'accountAddress'; input.value = accountAddress; form.appendChild(input); var input = document.createElement('input'); input.type = 'hidden'; input.name = 'signature'; input.value = signature; form.appendChild(input); document.body.appendChild(form); form.submit(); } </script>
Данный скрипт состоит из двух функций web3Login
и web3LoginBackend
.
При нажатии на кнопку авторизации
будет вызвана функция web3Login
, активирующая окно «Запрос подписи» в расширении Metamask. Подписывать мы будем сообщение вида Sign to auth {{ csrf_token }}.
После нажатия на кнопку «Подписать» в окне «Запрос подписи» расширения Metamask будет вызвана функция web3LoginBackend
, которая отправит данные (csrf token, адрес кошелька и подпись сообщения) в django view
.
Перейдем к django view
.
import datetime import secrets import names import secrets import string import shortuuid from django.contrib import messages from django.contrib.auth import authenticate from django.contrib.auth import login from django.contrib.auth.models import User from django.http import HttpResponse from django.http import HttpResponseRedirect from django.shortcuts import render from django.shortcuts import reverse from eth_account.messages import defunct_hash_message from web3.auto import w3 from user_profile.models import UserProfile # TODO измените на свою модель def auth_web3(request): public_address = request.POST["accountAddress"] signature = request.POST["signature"] csrf_token = request.POST["csrfmiddlewaretoken"] original_message = f"Sign to auth {csrf_token}" message_hash = defunct_hash_message(text=original_message) signer = w3.eth.account.recoverHash(message_hash, signature=signature) short_uuid = shortuuid.uuid() if signer == public_address: user_profile = UserProfile.objects.filter(eth_account_address=public_address).first() if user_profile: try: user = user_profile.user except: messages.add_message(request, messages.WARNING, _("Профайл не найден")) return HttpResponseRedirect( reverse( "main:user_login", # TODO измените на свой адрес ) ) user.backend = "django.contrib.auth.backends.ModelBackend" login(request, user) return HttpResponseRedirect(reverse("main:dashboard")) # TODO измените на свой адрес else: alphabet = string.ascii_letters + string.digits password = "".join(secrets.choice(alphabet) for i in range(20)) first_name = names.get_first_name() last_name = names.get_last_name() email = f"{short_uuid}@{HOST}" user = User.objects.create_user(email=email, username=short_uuid, first_name=first_name, last_name=last_name, password=password) user_profile = UserProfile() user_profile.user = user user_profile.eth_account_address = public_address user_profile.save() user.backend = "django.contrib.auth.backends.ModelBackend" login(request, user) messages.success(request, _("Успешная регистрация.")) return HttpResponseRedirect(reverse("main:dashboard")) # TODO измените на свой адрес else: messages.add_message(request, messages.WARNING, _("Обновите страницу и попробуйте еще раз")) return HttpResponseRedirect( reverse( "main:user_login", # TODO измените на свой адрес ) )
На строке 26 мы дублируем сообщение с django template
original_message = f"Sign to auth {csrf_token}"
И восстанавливаем адрес кошелька при помощи подписи signature
полученной из django tempate
после отправки данных в django view
.
message_hash = defunct_hash_message(text=original_message)
signer = w3.eth.account.recoverHash(message_hash, signature=signature)
Если восстановленный адрес signer
равен адресу public_address
(адресу кошелька пользователя) отправленному из django template
, то авторизация/регистрация прошла успешно и все что нам остается это сделать немного «магии» для авторизации/регистрации.
P.S. Рабочий пример можно найти по адресу workhours.space. А еще это удобный и бесплатный трекер времени с удобным ботом в телеграм — пользуйтесь.
ссылка на оригинал статьи https://habr.com/ru/post/684958/
Добавить комментарий