13.12.20214 min

Miroslaw ShpakSoftware EngineerWorld of Defish

Nie potrzebujesz już JWT

Prostszy sposób uwierzytelniania użytkowników w WEB3 za pomocą podpisywanych wiadomości.

Nie potrzebujesz już JWT

Nie jest tajemnicą, że logowanie za pomocą Ethereum wkrótce stanie się dla użytkownika standardem, a hasła nie będą już potrzebne. Jednak rozwój zdecentralizowanych aplikacji (dApp) jest wciąż dość młodym kierunkiem i wiele standardów tego rozwoju jest dopiero w fazie ustaleń.

Aktualnie wszyscy deweloperzy piszą aplikacje przy pomocy starych praktyk, instynktownie używając standardu JWT do uwierzytelniania. Proponuję nieco inne podejście.

Sam zacząłem rozwijać zdecentralizowane aplikacje, używając JWT. Od pierwszego projektu czułem, że uwierzytelnianie zawsze jest czymś trudnymi, że w całym tym procesie musi być coś zbędnego. Po kilku projektach zdałem sobie sprawę, że sam JWT jest zbędny. Pozwólcie, że wyjaśnię dlaczego.

Powyższy schemat pokazuje, w jaki sposób  wykonałem uwierzytelnianie w moich pierwszych kilku projektach. Tutaj schemat niemal w całości powtarza standardową procedurę JWT, z tą różnicą, że zamiast loginu i hasła, użytkownik wysyła sygnaturę.

Dlaczego potrzebujemy JWT? Przecież nawet bez JWT można wiarygodnie zidentyfikować użytkownika, pobierając adres z jego sygnatury.

Tak można uprościć cały proces:

Użytkownik nadal tworzy sygnaturę, ale już z datą wygaśnięcia, więc jeśli atakujący ją zdobędzie, to nie będzie ona długo użyteczny (tak samo jak w przypadku JWT). Następnie sygnatura jest umieszczana w standardowym nagłówku Authorization i przetwarzana na serwerze poprzez pobranie adresu użytkownika i odnalezienie go w bazie danych. To wszystko. Nie trzeba stale aktualizować kluczy szyfrujących JWT na serwerze, więc ogólnie rzecz biorąc, na serwerze nie ciąży już tak duża odpowiedzialność.

Aby jeszcze bardziej uprościć ten przepływ, stworzyłem moduł web3-token. Aby go zainstalować, należy użyć polecenia:

import Web3Token from 'web3-token';

// Connection to MetaMask wallet (you can actually use any wallet)
// you can even use ethersjs instead of web3
const web3 = new Web3(ethereum);
await ethereum.enable();

// getting address from which we will sign message
const address = (await web3.eth.getAccounts())[0];

// generating a token with 1 day of expiration time
const token = await Web3Token.sign(msg => web3.eth.personal.sign(msg, address), '1d');

// attaching token to axios authorization header
axios.post('/registration', { name: 'Adam' }, {
  headers: {
    'Authorization': token,
  }
})

// checking how it finds me in backend's database
axios.get('/me', {
  headers: {
    'Authorization': token,
  }
})

Moduł ten może być użyty zarówno po stronie serwera, jak i klienta.

Przyjrzyjmy się przykładowi, zaczynając po stronie klienta.

import Web3Token from 'web3-token';

// Connection to MetaMask wallet (you can actually use any wallet)
// you can even use ethersjs instead of web3
const web3 = new Web3(ethereum);
await ethereum.enable();

// getting address from which we will sign message
const address = (await web3.eth.getAccounts())[0];

// generating a token with 1 day of expiration time
const token = await Web3Token.sign(msg => web3.eth.personal.sign(msg, address), '1d');

// attaching token to axios authorization header
axios.post('/registration', { name: 'Adam' }, {
  headers: {
    'Authorization': token,
  }
})

// checking how it finds me in backend's database
axios.get('/me', {
  headers: {
    'Authorization': token,
  }
})

Po wywołaniu metody .sign, zobaczysz coś podobnego do tego (jeśli używasz MetaMask):

Jak widać, komunikat jest całkowicie przejrzysty dla użytkownika, ponieważ musi on widzieć, co podpisuje. Zamiast więc używać JSON dla lepszej czytelności, zdecydowałem się użyć tej samej struktury, co dla nagłówków HTTP.

W treści wiadomości widzimy wersję tokena oraz datę wygaśnięcia.

const Web3Token = require('web3-token');

// getting a token from authorization header
const token = req.headers['Authorization']

const { address, body } = await Web3Token.verify(token);

// now you can find that user by his address 
// tip: better to do it case insensitive
req.user = await User.findOne({ address });

Jest to całkiem proste, wystarczy jedna linijka, a moduł przejmuje całą kryptografię. W magiczny sposób uzyskujemy adres użytkownika z podpisu i odnajdujemy go w bazie danych za pomocą tego adresu. Następnie, na przykład, możesz przydzielić temu użytkownikowi NFT.

W rezultacie otrzymujemy bardzo wygodną, bezstanową metodę uwierzytelniania użytkowników, idealną dla hybrydowych aplikacji zdecentralizowanych. Jedyną wadą jest to, że ciężko to przetestować z Postmanem ?

Bardzo bym chciał, żeby wyszło z tego coś w rodzaju standardu, ale do tego czasu jestem otwarty na krytykę (lub ewentualnie pytania/sugestie) poprzez telegram @bytesbay lub e-mail miroslaw.shpak@gmail.com. Obecnie przygotowuję również serię artykułów na temat tworzenia gier opartych na blockchain w czasie rzeczywistym z NFT, więc bądźcie czujni.

Web3 jest tuż-tuż.

Oryginał tekstu w języku angielskim można przeczytać tutaj.

<p>Loading...</p>