A simpler way to authenticate users with web3 using signed messages
使用签名消息通过 web3 对用户进行身份验证的更简单方法
It’s no secret that the Ethereum login will soon become a user standard and passwords will no longer be needed. Nevertheless, dApp development is still a fairly young direction and many standards for their development are still set.
以太坊登录很快就会成为用户标准并且不再需要密码,这已经不是什么秘密了。 尽管如此,dApp 开发仍然是一个相当年轻的方向,许多开发标准仍在制定中。
Now all developers continue to write dApps with old practices, instinctively using the same JWT for authentication. I propose a slightly different approach.
现在,所有开发人员都继续使用旧做法编写 dApp,本能地使用相同的 JWT 进行身份验证。我提出了一种稍微不同的方法。
I myself started developing dApps using JWT. From the first project, I felt that authentication always becomes tricky and that there must be something redundant in the process. After a couple of projects, I realized that the JWT itself is redundant. Let me explain why.
我自己开始开发d使用JWT应用。从第一个项目开始,我就觉得身份验证总是很棘手,而且过程中肯定有一些多余的东西。经过几个项目,我意识到 JWT 本身是多余的。让我解释一下原因。
This diagram shows how I did authentication on my first few projects. Here the scheme almost wholly repeats the standard procedure with JWT, the only thing is that instead of a login and password, the user sends a signature.
此图显示了我如何在最初的几个项目中进行身份验证。这里的方案几乎完全重复了 JWT 的标准过程,唯一的问题是用户发送签名而不是登录名和密码。
Why do we need to get JWT? After all, even without it, you can reliably identify the user by taking the address from his signature.
为什么我们需要获得 JWT?毕竟,即使没有它,您也可以通过从签名中获取地址来可靠地识别用户。
Here’s how to simplify it:
以下是简化它的方法:
The user still generates a signature, but already with an Expire-date inside, so that if an attacker gets the signature, it won’t be useful for long (the same as with the JWT). Further, the signature is placed in the standard Authorization header and processed on the server by taking the user’s address and finding the user in the database. That’s all. And you do not need to constantly update the encryption keys for the JWT on the server, so in general, a lot of responsibility falls off the server.
用户仍然会生成一个签名,但里面已经有一个过期日期,这样如果攻击者得到了签名,它就不会用太久(与 JWT 相同)。此外,签名被放置在标准的 Authorization 标头中,并通过获取用户地址并在数据库中查找用户在服务器上进行处理。就这样。并且您不需要不断更新服务器上 JWT 的加密密钥,因此一般来说,服务器会承担很多责任。
To simplify this flow even more, I made the web3-token module. To install it, use the command:
为了进一步简化这个流程,我制作了web3-token模块。要安装它,请使用以下命令:
$ npm i web3-token
This module can be used both on the server and on the client.
该模块可用于服务器和客户端。
Let’s look at an example, starting with the client-side.
让我们看一个例子,从客户端开始。
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,
}
})
After calling the .sign method, you will see something similar to this (if you are using MetaMask).
调用 .sign方法后,您将看到与此类似的内容(如果您使用的是 MetaMask)。
(MetaMask notification to sign our token)
(用于签署我们的令牌的 MetaMask 通知)
As you can see, the message is completely transparent for the user since they must see what they are signing. So instead of using JSON structure for better readability, I decided to use the same structure as for HTTP headers.
如您所见,该消息对用户来说是完全透明的,因为他们必须看到他们正在签署的内容。因此,我决定使用与 HTTP 标头相同的结构,而不是使用 JSON 结构来提高可读性。
In the body of the message, we see the version of the token and the expire date itself.
在消息正文中,我们看到令牌的版本和到期日期本身。
Next, here’s what the backend (Node.js) does with this token:
接下来,后端 (Node.js) 对此令牌执行的操作如下:
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 });
It’s pretty simple, just one line, and the module takes over all cryptography. We magically obtain the user’s address from the signature and find them in the database using this address. Then , for example , you may grant this user an NFT by his address.
非常简单,只有一行,模块接管了所有的密码学。我们神奇地从签名中获取用户的地址,并使用该地址在数据库中找到它们。然后,例如,您可以通过他的地址授予该用户 NFT。
The result is a very convenient stateless user authentication method, ideal for hybrid dApps. The only drawback is that it is hard to test in Postman 😀
结果是一种非常方便的无状态用户身份验证方法,非常适合混合 dApp。唯一的缺点就是很难在Postman中测试😀
I would really like something like a standard to come out of this, but until then, I am open to criticism (or possibly questions/suggestions) via telegram @bytesbay or mail miroslaw.shpak@gmail.com. I am also currently preparing a series of articles on real-time blockchain game development with NFT, so stay tuned.
我真的很喜欢像一个标准出来的这一点,但在那之前,我愿意通过电报批评(或可能的问题/建议)@bytesbay或邮件miroslaw.shpak@gmail.com。目前我也在准备NFT实时区块链游戏开发系列文章,敬请期待。
Web3 is just around the corner.
Web3 就在眼前。