Mohammad Shahbaz Alam

Mohammad Shahbaz Alam

Demystifying Login with Ethereum

Geth is an Ethereum client written in Go. This means running Geth turns a computer into an Ethereum node. Ethereum is a peer-to-peer network where information is shared directly between nodes rather than being managed by a central server. Nodes compete to generate new blocks of transactions to send to its peers because they are rewarded for doing so in Ethereum’s native token, ether (ETH). On receiving a new block, each node checks that it is valid and adds it to their database. The sequence of discrete blocks is called a “blockchain”. The information provided in each block is used by Geth to update its “state” - the ether balance of each account on Ethereum. There are two types of account: externally-owned accounts (EOAs) and contract accounts. Contract accounts execute contract code when they receive transactions. EOAs are accounts that users manage locally in order to sign and submit transactions. Each EOA is a public-private key pair, where the public key is used to derive a unique address for the user and the private key is used to protect the account and securely sign messages. Therefore, in order to use Ethereum, it is first necessary to generate an EOA (hereafter, “account”). This tutorial will guide the user through creating an account, funding it with ether and sending some to another address.

Read more about Ethereum accounts here.

Install Geth

There are several ways to install Geth, including via a package manager, downloading a pre-built bundle, running as a docker container or building from downloaded source code.

The easiest way to install go-ethereum on MacOS is to use the Geth Homebrew tap. The first step is to check that Homebrew is installed. The following command should return a version number.

brew -v

If a version number is returned, then Homebrew is installed. If not, Homebrew can be installed by following the instructions here. With Homebrew installed, the following commands add the Geth tap and install Geth:

brew tap ethereum/ethereum
brew install ethereum

The above command will installs the latest stable release.

Check whether geth is installed or not by running the below command in your terminal:

geth --help

It should output command-line options.

Start Geth over HTTP Server

Run the below command to start Geth over HTTP, enables access from any origin and exposes it's api's.

geth --http --http.corsdomain "*" --http.api personal,eth,web3
  • --http flag enables the HTTP server.
  • --http.corsdomain "*" enables access from any origin.
  • --http.api personal,eth,web3 enable access to APIs like "personal" which is by default not whitelisted.

Create a Node JS application to interact with Geth[ETH]

npm init -y

Install Web3.js

npm install web3

Create an index.js file and paste the following:

var Web3 = require('web3')
var provider = 'http://localhost:8545'
var web3Provider = new Web3.providers.HttpProvider(provider)
var web3 = new Web3(web3Provider)

var msg = {
  msg_value: 'This is a Secret Message',
}
var secret = '!@supersecret' // In real world app, this will be replaced with the private key.

web3.eth.personal.newAccount(secret).then(pub_address => {
  console.log('ETH Public Address: ')
  console.log(pub_address)
  msg = {...msg, pub_address: pub_address.toLowerCase()}
  web3.eth.personal.sign(msg, pub_address, secret).then(signature => {
    console.log('Signature: ')
    console.log(signature)
    web3.eth.personal.ecRecover(msg, signature).then(recover_public_address => {
      console.log('Recovered ETH Public Address: ')
      console.log(recover_public_address)
      if (recover_public_address == msg.pub_address) {
        console.log('Matched')
        // Access allowed
      }
    })
  })
})

Let me explain, what's going on here.

These lines are making a connecting between ETH blockchain (Geth running locally) and Web3.js library.

var Web3 = require('web3')
var provider = 'http://localhost:8545'
var web3Provider = new Web3.providers.HttpProvider(provider)
var web3 = new Web3(web3Provider)

This is the msg we want to sign using secret/private key.

var msg = {
  msg_value: 'This is a Secret Message',
}
var secret = '!@supersecret'

We are calling personal.getAccount() to get an ETH public address.

web3.eth.personal.newAccount(secret).then(pub_address => {
  console.log('ETH Public Address: ')
  console.log(pub_address)
})

We are updating the msg to include the ETH public address, so that later when we ecRecover the signature to get the public address, we can check the ETH public address in the message with the retrieved public address from ecRecover().

In the following lines, we are signing the message(msg, that also includes the user's eth public address) using user's ETH public address, and secret/private key.

web3.eth.personal.sign(msg, pub_address, secret).then(signature => {
  console.log('Signature: ')
  console.log(signature)
})

The above code will return a signature, that looks like: 0x30755ed65396facf86c53e6217c52b4daebe72aa4941d89635409de4c9c7f9466d4e9aaec7977f05e923889b33c0d0dd27d7226b6e6f56ce737465c5cfd04be400

The above signature is passed to ecRecover along with the original message returing the public address used to sign the message.

web3.eth.personal.ecRecover(msg, signature).then(recover_public_address => {
  console.log('Recovered ETH Public Address: ')
  console.log(recover_public_address)
})

If the recovered_public_address is same as the public_address received in the message, it is fair to assure, the user has signed the message and access may be granted.

if (recover_public_address == msg.pub_address) {
  console.log('Matched')
}

An Auth flow using ETH. An Auth flow using ETH.

All this is possible because of the Elliptic Curve Digital Signature Algorithm, or ECDSA.

Importance of Cryptographic signatures in Blockchain

Cryptographic signatures are a key part of the blockchain. They are used to prove ownership of an address without exposing its private key.

This is based on mathematical formulas. We take an input message, a private key and a (usually) random secret, and we get a number as output, which is the signature. Using another mathematical formula, this process can be reversed in such a way that the private key and random secret are unknown but can be verified.

Ethereum SECP256k1 elliptic curve. Ethereum SECP256k1 elliptic curve.

Using elliptic curve point manipulation, we can derive a value from the private key, which is not reversible. This way we can create signatures that are safe and tamperproof. The functions that derive the values are called “trapdoor functions”:

Possible Attack

For example, if user A signs a message(M) and sends the signature(S), user B can copy that signed message[S(M)] and original msg and send it to gain access.

And using our current example code, since the msg includes the public address used to receive the signature. When using ecRecover with the signature and msg(containing public address), it will give back the eth address allowing access to the resource.

Possible Solution,

How about adding challenge to the msg or encoding the msg to get a JWT token, using another set of secret? These's a lot of possible solution to add challenge at this step to identify the original user and authenticate securely.

Web3.js Functions

newAccount

Creates a new account.

web3.eth.personal.newAccount(password, [callback])

Note: Never call this function over a unsecured Websocket or HTTP provider, as your password will be sent in plain text!

Read more here.

Example

web3.eth.personal.newAccount('!@superpassword')
.then(console.log);
> '0x1234567891011121314151617181920212223456'

sign

The sign method calculates an Ethereum specific signature.

web3.eth.personal.sign(dataToSign, address, password [, callback])

Read more here.

Example

web3.eth.personal.sign("Hello world", "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", "test password!")
.then(console.log);
> "0x30755ed65396facf86c53e6217c52b4daebe72aa4941d89635409de4c9c7f9466d4e9aaec7977f05e923889b33c0d0dd27d7226b6e6f56ce737465c5cfd04be400"

ecRecover

Recovers the account that signed the data.

web3.eth.personal.ecRecover(dataThatWasSigned, signature [, callback])

Read more here.

Example

web3.eth.personal.ecRecover("Hello world", "0x30755ed65396facf86c53e6217c52b4daebe72aa4941d89635409de4c9c7f9466d4e9aaec7977f05e923889b33c0d0dd27d7226b6e6f56ce737465c5cfd04be400").then(console.log);
> "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe"

Though this approach to authenticate a user using blockchain is way secure than the centralised authentication where private key 🔑 sits in the centralised server. Any breach to centralized server will expose the user's private key.

The approch used in this guide is self-custodial, here, user's are responsible of keeping the secret/private key secure. Which again is a flawed system, user's are prone to attacks too and lossing the private key will be loosing the digital identity and assets.

There's a better approach, that is non-custodial and uses Multi Party Computation. MPC enables multiple parties – each holding their own private data – to evaluate a computation without ever revealing any of the private data held by each party (or any otherwise related secret information).

In my next post, I will talk about how different companies are protecting your Private Key and how's future looks like for true Self-Sovereign Identity without any compromises.