I build a lot of proofs of concept at for various clients and usually they want something that leverages the blockchain to solve some business use case. Strangely, these systems often get designed with standard web logins (i.e. with a username and ). ConsenSys Ethereum password I always ask myself why I’m designing things this way since, after all, this is an annoying aspect of every web application that Ethereum can solve .So I’m finally putting my foot down and designing that solution. today JSON Web Tokens One very popular way to log into a standard web system (and/or use its API) is to submit a password (which is hashed client side) to an authentication endpoint and receive a token in return. This is (usually) called a JSON Web Token and is typically valid for some finite period of time (minutes to days). is a nice tutorial on a standard implementation. Here JSON Web Tokens are nice and dandy, but I got to thinking that it’s very easy to authenticate yourself on the blockchain. In fact, when you use Ethereum, you are constantly doing it. If you think of an Ethereum address (which is just a sha3 hash of your public key) as an account on a website, it is very easy to prove you own that account by signing a piece of data with your private key. This data is arbitrary and can be any random string that the website’s API serves up. Thus, we can use an address as a username and bypass the need for a password. In fact, we don’t even need to use the blockchain to do this. Here’s what it looks like using Express: First we need to do an elliptic curve signature with a private key: var ethUtil = require(‘ethereumjs-util’); // >=5.1.1 var data = ‘i am a string’;// Elliptic curve signature must be done on the Keccak256 Sha3 hash of a piece of data.var message = ethUtil.toBuffer(data);var msgHash = ethUtil.hashPersonalMessage(message);var sig = ethUtil.ecsign(msgHash, privateKey);var serialized = ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s))return serialized Don’t worry too much about what these parameters are yet. There is some cryptography going on here and I encourage you to read up on elliptic curve signatures. The is a decent place to start. Bitcoin wiki Anyway, once we have our signature components, we can package them along with the user’s address and send it all to an authentication endpoint. POST /Authenticate var jwt = require(‘jsonwebtoken’);var ethUtil = require('ethereumjs-util'); function checkSig(req, res) {var sig = req.sig;var owner = req.owner; // Same data as beforevar data = ‘i am a string’; var message = ethUtil.toBuffer(data)var msgHash = ethUtil.hashPersonalMessage(message) // Get the address of whoever signed this messagevar signature = ethUtil.toBuffer(sig)var sigParams = ethUtil.fromRpcSig(signature)var publicKey = ethUtil.ecrecover(msgHash, sigParams.v, sigParams.r, sigParams.s)var sender = ethUtil.publicToAddress(publicKey)var addr = ethUtil.bufferToHex(sender) // Determine if it is the same address as 'owner'var match = false;if (addr == owner) { match = true; } if (match) {// If the signature matches the owner supplied, create a// JSON web token for the owner that expires in 24 hours.var token = jwt.sign({user: req.body.addr}, ‘i am another string’, { expiresIn: “1d” });res.send(200, { success: 1, token: token })} else {// If the signature doesn’t match, error outres.send(500, { err: ‘Signature did not match.’});} } So basically, given some piece of data, an address, and the components of an EC signature, we can cryptographically prove that the address belongs to the person who signed the data. Pretty cool, huh? Once we are satisfied the signature and address match, we can sign a JSON Web Token for that address server side. In this case, the token is valid for 1 day. Now we just need to put in some middleware to guard any routes that would be serving or modifying protected information. middleware/auth.js function auth(req, res, next) {jwt.verify(req.body.token, ‘i am another string’, function(err, decoded) {if (err) { res.send(500, { error: ‘Failed to authenticate token.’}); }else {req.user = decoded.user;next();};});} app.js // Routesapp.post(‘/UpdateData’, auth, Routes.UpdateData);… If the provided token corresponds to the user who sent the request, we continue to requested route. Note that the middleware . It is this new `user` parameter that we need to reference because we know it got set in our middleware. modifies the request POST /UpdateData function UpdateData(req, res) {// Only use the user that was set in req by auth middleware!var user = req.user;updateYourData(user, req.body.data);...} And there we have it! Your user has literally in, but didn’t need a password. signed UI stuff But how does a user actually sign this data in the browser? to the rescue! is a neat chrome extension that into your browser window. Metamask Metamask injects web3 mycomponent.jsx makeSig(dispatch) { function toHex(s) {var hex = ‘’;for(var i=0;i<s.length;i++) { hex += ‘’+s.charCodeAt(i).toString(16); }return `0x${hex}`;} var data = toHex(‘i am a string’);web3.currentProvider.sendAsync({ id: 1, method: 'personal_sign', params: [web3.eth.accounts[0], data] },function(err, result) {let sig = result.result;dispatch(exchange.authenticate(sig, user))})}} render(){let { dispatch, _main: { sig } } = this.props;if (Object.keys(sig).length == 0) { this.makeSig(dispatch); }return (<p>I am a webpage</p>);} This will trigger Metamask to pop up a window asking the user to sign the message: Once the callback is invoked, it will call the following action: authenticate(sig, user) {return (dispatch) => {fetch(`${this.api}/Authenticate`, {method: 'POST',body: JSON.stringify({ owner: user, sig: sig}),headers: { "Content-Type": "application/json" }}).then((res) => { return res.text(); }).then((body) => {var token = JSON.parse(body).token;dispatch({ type: 'SET_AUTH_TOKEN', result: token})})}} And once you have the auth token saved in your reducer, you can call your authenticated endpoints. And there we have it! Note that , , and values must be recovered from the signature. Metamask has a that shows how the signature is constructed. It can be deconstructed like this: v r s signature util module var solidity_sha3 = require('solidity-sha3').default; let hash = solidity_sha3(data);let sig = result.result.substr(2, result.result.length);let r = sig.substr(0, 64);let s = sig.substr(64, 64);let v = parseInt(sig.substr(128, 2)); where will be parsed as either or . Note also that this uses the to make sure this hashing algorithm is the same one being used as solidity’s native hashing method (we are hashing the hex-string that was signed earlier). r 0 1 solidity-sha3 module Production ready I can’t emphasize enough that web application using JSON web tokens could easily take advantage of this . Any user with a Metamask extension could simply bypass the login screen with arguably better security than whatever you’re currently using to manage logins. This means fewer forgotten passwords, less wasted time, and a happier user base. every today And, you know, if you want your users to pay each other (or you, or users on on any other system that uses this) without an intermediary or if you want to take advantage of Ethereum’s million other , this sets you up to do that too. features Make the switch today. us Ethereum folk and conquer the world. Join — — — — The views expressed by the author above do not necessarily represent the views of Consensus Systems LLC DBA Consensys. ConsenSys is a decentralized community with ConsenSys Media being a platform for members to freely express their diverse ideas and perspectives. To learn more about ConsenSys and Ethereum, please visit our website. And if you liked this piece, sign up here for our weekly newsletter.
Share Your Thoughts