WEB3DEV

Cover image for Como criar um fluxo de autenticação Web3
Adriano P. Araujo
Adriano P. Araujo

Posted on • Atualizado em

Como criar um fluxo de autenticação Web3

Meu nome é Nader Dabit. Trabalho como engenheiro de Relações com Desenvolvedores com Edge & Node e The Graph.

O código completo para este projeto está localizado aqui.

A autenticação Web3 é um dos tópicos que mais ressoam com os desenvolvedores que conheço quando se fala em web3.

Ao contrário dos serviços centralizados que coletam e rastreiam suas informações pessoais e perfis de usuários, na web3 podemos usar carteiras blockchain e criptografia de chave pública para nos identificar. Podemos então optar por vincular ou não nossa própria identidade a esse endereço usando protocolos como IDX ou ENS ou ter vários endereços para vários tipos de aplicativos e casos de uso.

Assinaturas criptográficas são usadas para provar a propriedade de um endereço. Podemos usar essas assinaturas para gravar transações em uma blockchain, mas também para assinar mensagens de qualquer tipo e decodificá-las em um servidor usando bibliotecas web3 como web3.js e ethers.js.

Usando esta técnica, podemos verificar se o remetente de uma mensagem é realmente autêntico, permitindo-nos realizar autenticação e autorização em apenas algumas linhas de código.

Neste post, quero mostrar como implementar isso em um aplicativo Next.js, criando um exemplo básico de como um fluxo de autenticação do mundo real pode ser. Também quero mostrar como configurar uma UI multiplataforma que permitirá que os usuários escolham entre várias carteiras que estão acessando no seu aplicativo de um navegador da Web ou móvel.

A maneira como faremos isso é usando a biblioteca ethers.js e os utilitários signMessage e VerifyMessage da biblioteca.

Por exemplo, podemos assinar uma mensagem de uma aplicação do lado do cliente usando a carteira blockchain do usuário assim:

const signature = await signer.signMessage(some_string)

Em seguida, verifique-o em um servidor usando uma combinação de assinatura, endereço e string:


const decodedAddress = ethers.utils.verifyMessage(some_string, signature)
if (decodedAddress === sender_address) {`
  // assinatura verificada
}

Enter fullscreen mode Exit fullscreen mode

Vamos ver como podemos implementar essa técnica em uma aplicação full stack, além de incluir outras partes importantes do fluxo de autenticação, como várias opções de carteira, e fazê-lo funcionar em várias plataformas.

Para usar o aplicativo que estamos prestes a construir, você precisará ter uma carteira web3 como Metamask ou Rainbow instalada em seu dispositivo ou como uma extensão em seu navegador da web.

Um salve para Rahat Chowdhury, cujo workshop de ss foi expandido para esta postagem no blog e para o projeto de exemplo.

Começando

Para começar, vamos primeiro criar um novo aplicativo Next.js:

npx create-next-app sign-in-with-ethereum

Em seguida, mude para o novo diretório e instale as seguintes dependências usando npm ou yarn:

npm instala ethers web3modal @walletconnect/web3-provider @toruslabs/torus-embed

Em seguida, na pasta pages/api crie um novo arquivo chamado auth.js e adicione o seguinte código:


import { users } from '../../utils/users'

export default function auth(req, res) {

  const {address} = req.query
  let user = users[address]
  if (!user) {
    user = {
      address,
      nonce: Math.floor(Math.random() * 10000000)
    }
    users[address] = user
  } else {
    const nonce = Math.floor(Math.random() * 10000000)
    user.nonce = nonce
    users[address] = user
  }
  res.status(200).json(user)  

}

Enter fullscreen mode Exit fullscreen mode

Esta é uma API de usuário básica que verificará se existe um usuário no registro. Se houver, atualizamos o nonce do usuário (que será usado para verificar o usuário na próxima etapa) e devolvemos as informações do usuário ao cliente. Se o usuário ainda não foi criado, criamos um novo usuário e atribuímos a ele um nonce.

Em seguida, crie um arquivo no diretório api chamado verify.js com o seguinte código:


/* pages/api/verify.js */

import { ethers } from 'ethers'

import { users } from '../../utils/users'


export default function verify(req, res) {

  let authenticated = false

  const {address, signature} = req.query

  const user = users[address]

  const decodedAddress = ethers.utils.verifyMessage(user.nonce.toString(), signature)

  if(address.toLowerCase() === decodedAddress.toLowerCase()) authenticated = true

  res.status(200).json({authenticated})

}

Enter fullscreen mode Exit fullscreen mode

Este código é tudo o que precisamos para autenticar uma transação no servidor. A assinatura é criada no lado do cliente e enviada junto com a requisição, assinada com alguma string única, no nosso caso um nonce que foi gerado no servidor no passo anterior. Usamos esse nonce para decodificar a transação no servidor e, se o endereço da string decodificada corresponder ao endereço do chamador, podemos supor que a transação foi enviada pelo mesmo usuário.

Em seguida, crie uma pasta no diretório raiz chamada utils e crie um novo arquivo chamado users.js com o seguinte código:


/* utils/users.js */

export const users = {}

Enter fullscreen mode Exit fullscreen mode

Este é um registro de usuário básico que provavelmente não é algo que você usaria em produção, mas nos permite simular como algo assim pode ser feito em um aplicativo do mundo real. O back-end para isso pode ser qualquer coisa, desde um protocolo descentralizado como Ceramic, ThreadDB ou GunDB, até um serviço centralizado ou BD.

Em seguida, vamos atualizar pages/index.js com o código do lado do cliente. Primeiro, remova todo o código existente e adicione as seguintes importações e declarações de variável:


/* pages/index.js */

import React, { useState } from 'react'
import { ethers } from 'ethers'
import Web3Modal from 'web3modal'
import WalletConnectProvider from '@walletconnect/web3-provider'

Enter fullscreen mode Exit fullscreen mode

Aqui importamos o Web3Modal e o WalletConnect, o que permitirá que os usuários tenham uma boa variedade de opções para conectar sua carteira web3.

Em seguida, vamos criar a interface do usuário principal. (o código para este arquivo também está localizado aqui)

Você pode obter um ID de projeto Infura gratuitamente aqui. Se você não quiser se inscrever no Infura , pode deixar de fora o objeto walletconnect e ainda terá as opções Torus e Metamask.



/* pages/index.js */
const ConnectWallet = () => {

    const [account, setAccount] = useState('')
    const [connection, setConnection] = useState(false)
    const [loggedIn, setLoggedIn] = useState(false)



    async function getWeb3Modal() {
      let Torus = (await import('@toruslabs/torus-embed')).default
      const web3Modal = new Web3Modal({
        network: 'mainnet',
        cacheProvider: false,
        providerOptions: {
          torus: {
            package: Torus
          },

          walletconnect: {
            package: WalletConnectProvider,
            options: {
              infuraId: 'your-infura-id'
            },

          },

        },

      })
      return web3Modal
    }



    async function connect() {
      const web3Modal = await getWeb3Modal()
      const connection = await web3Modal.connect()
      const provider = new ethers.providers.Web3Provider(connection)
      const accounts = await provider.listAccounts()
      setConnection(connection)
      setAccount(accounts[0])

    }


    async function signIn() {
      const authData = await fetch(`/api/auth?address=${account}`)
      const user = await authData.json()
      const provider = new ethers.providers.Web3Provider(connection)
      const signer = provider.getSigner()
      const signature = await signer.signMessage(user.nonce.toString())
      const response = await fetch(`/api/verify?address=${account}&signature=${signature}`)
      const data = await response.json()
      setLoggedIn(data.authenticated)

    }


    return(
        <div style={container}>
          {

            !connection && <button style={button} onClick={connect}> Connect Wallet</button>

          }

          { connection && !loggedIn && (
            <div>
              <button style={button} onClick={signIn}>Sign In</button>
            </div>
          )}
          {
            loggedIn && <h1>Welcome, {account}</h1>
           }

        </div>
    )
}

const container = {
  width: '900px',
  margin: '50px auto'
}


const button = {
  width: '100%',
  margin: '5px',
  padding: '20px',
  border: 'none',
  backgroundColor: 'black',
  color: 'white',
  fontSize: 16,
  cursor: 'pointer'

}

export default ConnectWallet

Enter fullscreen mode Exit fullscreen mode

A função connect solicitará que os usuários façam login com a carteira de sua escolha. Depois que eles se conectarem, o endereço da carteira será armazenado no estado local. Se eles estiverem em um dispositivo móvel,  a WalletConnect permite que eles se autentiquem com este e que sejam  redirecionados de volta ao aplicativo para continuar

A função signIn se conectará ao registro do usuário e gerará o nonce. Em seguida, ele retornará o nonce _ao cliente, permitindo que o usuário assine a transação com o _nonce. No servidor, verificaremos se o endereço recuperado da assinatura assinada com o nonce recuperado do servidor corresponde ao endereço do usuário. Se isso corresponder, o usuário será autenticado e atualizaremos a UI de acordo.

Testando

Para compilar e executar o aplicativo, execute o seguinte comando:

npm run build && npm start

Quando o aplicativo for carregado, você poderá fazer login com sua carteira ethereum.

O código completo para este projeto está localizado aqui.

Os royalties para este post serão divididos com Rahat Chowdhury, que é o eth-sso-workshop que expandi para este tutorial.

Próximos passos

Aqui estão alguns outros projetos para analisar se você estiver interessado em autenticação web3.


Este artigo foi escrito por Nader Dabite traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.

Top comments (2)

Collapse
 
joaosobanski profile image
joaosobanski

Estou tendo problema na instalação da dependencia @walletconnect/web3-provider
não está encontrando repositorio =(

Collapse
 
devaraujo profile image
Adriano P. Araujo

Desculpa pela demora, mas ja tentou consultar o github da lib?
github.com/walletconnect/walletcon...