A computação descentralizada, com raízes que remontam a conceitos como a eliminação de pontos centrais de falha, ganhou força com a ascensão da internet. Hoje, a ideia de identidade descentralizada está se tornando cada vez mais relevante. A atividade online de um usuário, quando registrada em blockchain, pode revelar seus interesses e construir uma identidade digital única e verificável.
Em contraste com os tradicionais fluxos de registro por e-mail e senha, ou mesmo os convenientes logins sociais (Google, GitHub), que expõem dados a terceiros e permitem a criação de perfis comportamentais, o Web3.0 oferece uma alternativa focada na privacidade: o MetaMask.
MetaMask
MetaMask é uma carteira de criptomoedas que facilita a interação com a blockchain Ethereum. Através de extensões de navgeador e aplicativos móveis, o MetaMask permite que os usuários acessem suas carteiras e interajam com aplicações descentralizadas (dApps). Para utilizá-lo, é necessário primeiro possuir uma carteira MetaMask, que pode ser configurada através da loja de extensões do navegador (por exemplo, para Chrome).
Após a instalação, crie uma conta e guarde com segurança sua senha, endereço da carteira e frase de recuperação (seed phrase).
Fluxo de Login com Carteira
O processo de login com MetaMask difere significativamente dos logins de terceiros tradicionais. Em vez de redirecionamentos e autorizações de plataformas externas, o login com carteira envolve uma assinatura digital realizada no lado do cliente. O processo geral é:
- No frontend, o usuário inicia o processo de login.
- A aplicação solicita permissão para acessar a conta MetaMask.
- Uma mensagem criptografada é criada, contendo informações como o domínio do site e um timestamp.
- O MetaMask é usado para assinar essa mensagem com a chave privada do usuário.
- O endereço público da carteira e a assinatura digital são enviados ao backend.
- O backend verifica a assinatura usando a chave pública correspondente e a mensagem original.
- Se a verificação for bem-sucedida, uma sessão é estabelecida para o usuário, geralmente por meio de um token JWT.

Assinatura no Frontend (Vue.js e Ethers.js)
Para implementar a assinatura no frontend, utilizaremos a biblioteca ethers.js. Abaixo está um exemplo de como integrar e realizar a assinatura usando Vue.js e Axios para comunicação com o backend.
Primeiro, inclua as bbiliotecas necessárias no seu HTML:
<script src="path/to/ethers-v4.min.js"></script>
<script src="path/to/axios.js"></script>
<script src="path/to/vue.js"></script>
Em seguida, no componente Vue, defina a lógica para a ativação do login:
const App = {
data() {
return {
email: "",
password: "",
provider: null,
accountAddress: "",
signer: null
};
},
methods: {
async connectWallet() {
if (typeof ethereum === 'undefined') {
alert('MetaMask não está instalado. Por favor, instale a extensão.');
return;
}
try {
// Solicita acesso à conta
await ethereum.request({ method: 'eth_requestAccounts' });
this.provider = new ethers.providers.Web3Provider(window.ethereum);
const network = await this.provider.getNetwork();
// Verifica se está na rede principal Ethereum (Chain ID 1)
if (network.chainId !== 1) {
console.log("Por favor, mude para a rede principal Ethereum.");
// Opcional: pode adicionar lógica para mudar de rede
return;
}
const accounts = await this.provider.listAccounts();
this.accountAddress = accounts[0];
this.signer = this.provider.getSigner();
// Cria a mensagem a ser assinada com um timestamp para evitar replay attacks
const now = Math.floor(Date.now() / 1000);
// O timestamp é arredondado para evitar pequenas variações que invalidariam a assinatura
const sessionTimestamp = now - (now % 600); // Agrupa por blocos de 10 minutos
const message = `Autenticar em ${document.domain} às ${sessionTimestamp}`;
// Assina a mensagem
const signature = await this.signer.signMessage(message);
console.log("Endereço:", this.accountAddress);
console.log("Assinatura:", signature);
// Envia o endereço e a assinatura para o backend para verificação
this.verifySignature(this.accountAddress, signature);
} catch (error) {
console.error("Erro ao conectar a carteira:", error);
alert("Falha ao conectar ou assinar com a MetaMask.");
}
},
async verifySignature(address, signature) {
try {
const response = await axios.post('/api/verify-wallet', {
public_address: address,
signature: signature
});
if (response.data.errcode === 0) {
alert(`Bem-vindo: ${response.data.public_address}`);
localStorage.setItem("authToken", response.data.token);
localStorage.setItem("userAddress", response.data.public_address);
window.location.href = "/"; // Redireciona após login bem-sucedido
} else {
alert("Falha na verificação da assinatura.");
}
} catch (error) {
console.error("Erro na verificação da assinatura:", error);
alert("Erro ao comunicar com o servidor para verificação.");
}
},
async traditionalLogin() {
if (!this.email || !this.password) {
alert("Por favor, preencha o e-mail e a senha.");
return;
}
try {
const response = await axios.get('/api/login', {
params: {
email: this.email,
password: this.password
}
});
if (response.data.errcode === 0) {
alert(response.data.msg);
localStorage.setItem("authToken", response.data.token);
localStorage.setItem("userEmail", response.data.email);
window.location.href = "/";
} else {
alert(response.data.msg);
}
} catch (error) {
console.error("Erro no login tradicional:", error);
alert("Ocorreu um erro durante o login.");
}
}
}
};
// Instanciando a aplicação Vue
const app = Vue.createApp(App);
// Assumindo que myaxios e axios estão globalmente disponíveis ou configurados
// app.config.globalProperties.myaxios = myaxios;
// app.config.globalProperties.axios = axios;
app.mount("#app");
Verificação no Backend (Tornado)
No backend, utilizamos o framework Tornado e a biblioteca web3.py para verificar a assinatura. Isso garante que a assinatura foi realmente gerada pela chave privada associada ao endereço público apresentado.
Instale as dependências necessárias:
pip install tornado==6.1
pip install web3==5.29.1
pip install eth-account
Crie um manipulador (handler) assíncrono para lidar com a verificação:
import tornado.web
from web3 import Web3
from eth_account.messages import encode_defunct
import time
from tornado.escape import json_decode
# Assumindo que BaseHandler e MyJwt são definidos em outros módulos
# from .base import BaseHandler
# from utils.jwt_handler import MyJwt
# from models.user_model import User # Exemplo de modelo de usuário
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
# Implementação de autenticação baseada em token JWT, se necessário
pass
def get_json_body(self):
try:
return json_decode(self.request.body)
except:
return None
class CheckWalletSignatureHandler(BaseHandler):
async def post(self):
data = self.get_json_body()
public_address = data.get("public_address")
signature = data.get("signature")
if not public_address or not signature:
self.set_status(400)
self.write({"msg": "Endereço público e assinatura são obrigatórios.", "errcode": 1})
return
# Extrai o domínio da requisição
domain = self.request.host
if ":" in domain:
domain = domain.split(":")[0]
now = int(time.time())
# Recria a mensagem original usada no frontend
session_timestamp = now - (now % 600)
original_message = f"Autenticar em {domain} às {session_timestamp}"
try:
# Codifica a mensagem no formato esperado pela função de recuperação
message_hash = encode_defunct(text=original_message)
# Recupera o endereço que assinou a mensagem
signer_address = Web3().eth.account.recover_message(message_hash, signature=signature)
# Compara o endereço recuperado com o endereço fornecido
if signer_address.lower() == public_address.lower():
# Lógica para encontrar ou criar o usuário no banco de dados
user_email = public_address # Usando o endereço como identificador/email
user = None
try:
# Exemplo: buscar usuário no banco de dados
# user = await self.application.objects.get(User, email=user_email)
pass # Substituir pela lógica real de busca/criação de usuário
except Exception: # Erro ao buscar, significa que o usuário não existe
# Exemplo: criar novo usuário
# from django.contrib.auth.hashers import make_password
# user = await self.application.objects.create(User, email=user_email, password=make_password("generated_password_for_wallet_users"), role=1)
pass # Substituir pela lógica real de criação
# Gera um token JWT para o usuário
myjwt = MyJwt() # Instancie sua classe de manipulação JWT
token = myjwt.encode({"id": user.id if user else "new_user_id"}) # Use o ID real do usuário
self.write({"msg": "Autenticação bem-sucedida", "errcode": 0, "public_address": public_address, "token": token})
else:
self.write({"msg": "Falha na verificação da assinatura.", "errcode": 1})
except Exception as e:
print(f"Erro na verificação da assinatura: {e}")
self.set_status(500)
self.write({"msg": "Erro interno do servidor.", "errcode": 1})
# Configuração de rotas no Tornado (exemplo)
# url_handlers = [
# (r"/api/verify-wallet", CheckWalletSignatureHandler),
# # ... outras rotas
# ]
Após a verificação bem-sucedida da assinatura no backend, um token de autenticação é gerado e enviado de volta ao frontend. Este token pode ser armazenado localmente (por exemplo, no localStorage) e usado para autenticar requisições futuras à API.
Considerações Finais
A integração de identidades Web3.0 como MetaMask representa um avanço significativo em termos de privacidade e controle do usuário sobre seus dados. Embora a adoção de novas tecnologias possa enfrentar resistência inicial, a tendência de descentralização e o foco na privacidade do usuário são forças impulsionadoras que moldarão o futuro das aplicações online. O código-fonte completo deste exemplo está disponível em GitHub.