Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

An Introduction to Ethereum and Smart Contracts: A Programmable Blockchain, Part 4

DZone's Guide to

An Introduction to Ethereum and Smart Contracts: A Programmable Blockchain, Part 4

Welcome to the fourth and final part of our 'A Programmable Blockchain' series! Read on to learn how to make an actual app using the Ethereum Blockchain.

· Security Zone ·
Free Resource

Mobile is increasingly becoming a part of every consumers’ identity, but the increasing use of this digital channel is escalating the security risks faced by consumers and institutions.

Welcome back! If you missed Part 1, Part 2, or Part 3 of our 'A Programmable Blockchain' series, follow these links to check them out! 

A Simple Login System Using Ethereum

One of the cool things about Ethereum is that addresses are, by definition, systems to prove ownership. Whoever can perform operations with an Ethereum address is the rightful owner of that address. This is, of course, the consequence of the underlying public-key infrastructure used to verify transactions. We can exploit this to create a login system based on Ethereum addresses. Let's see how.

Any login system is mainly concerned with creating a unique identity that can be managed by whoever can pass a certain "login challenge." The login challenge is the method to prove that the same entity that created the account in the first place is the same entity doing operations now. Most systems rely on the classic username + password login challenge: a new user registers by choosing a unique username and a password, then, anytime the system requires proof that the user is in fact who he says he is, it can request the password for that username. This system works. But with Ethereum we already have a system for proving identities: public and private keys!

We'll design a simple contract that can be used by any user to validate his ownership of an address. The login process will be as follows:

  1. A user accesses a website that requires him or her to log in. When the user is not logged in, the website requests the user to enter his or her Ethereum address.
  2. The backend for the website receives the address for the user and creates a challenge string and a JWT. Both of these are sent back to the user.
  3. The user sends the challenge string to the Login contract and stores the JWT for later use locally.
  4. The backend listens for login attempts using the challenge string at the Ethereum network. When an attempt with the challenge string for the right user is seen, it can assume the user has proved his or her identity. The only person that can send a message with an Ethereum address is the holder of the private key, and the only user that knows the challenge string is the user that received the challenge through the login website.
  5. The user gets notified or polls the website backend for confirmation of his or her successful login. The user then proceeds to use the JWT issued in step 2 for accessing the website. Alternatively, a new JWT can be issued after a successful login.

To that end, this is the Ethereum contract we will use:

pragma solidity ^0.4.2;

contract Login {

    event LoginAttempt(address sender, string challenge);

    function login(string challenge) {
        LoginAttempt(msg.sender, challenge);
    }

}


The contract is extremely simple. Events are special elements in Solidity that are mapped to a system in Ethereum that allows special data to be logged. Events are generally watched by clients monitoring the evolution of the blockchain. This allows actions to be taken by clients when events are created. In our case, whenever a user attempts to log in, an event created with the challenge is broadcast. We only care about receiving a call from the rightful owner of the Ethereum address that was passed to the third party website. And, thanks to the way Ethereum works, we can be sure the sender was the one who performed the call.

In addition to the sender's address, the challenge is also broadcast. This means anyone watching the blockchain now knows the challenge. However, this cannot be used on its own to impersonate a user: a user can only interact with the backend through the session JWT. This means an attacker must know three pieces of information to impersonate a user: the Ethereum address, the challenge, AND the JWT issued with the challenge. Since JWTs are signed, an attacker cannot create a valid JWT to impersonate a user, even with access to the challenge.

What follows is our backend code. First, let's see how to watch for Ethereum events:

const LoginContract = require('./login_contract.js');

const loginContract = LoginContract.at(process.env.LOGIN_CONTRACT_ADDRESS || 
                      '0xf7b06365e9012592c8c136b71c7a2475c7a94d71');

// LoginAttempt is the name of the event that signals logins in the 
// Login contract. This is specified in the login.sol file.
const loginAttempt = loginContract.LoginAttempt();

const challenges = {};
const successfulLogins = {};

loginAttempt.watch((error, event) => {
    if(error) {
        console.log(error);
        return;
    }

    console.log(event);

    const sender = event.args.sender.toLowerCase();

    // If the challenge sent through Ethereum matches the one we generated,
    // mark the login attempt as valid, otherwise ignore it.
    if(challenges[sender] === event.args.challenge) {
        successfulLogins[sender] = true;
    }
});

The login_contract.js file contains what is needed to inter-operate with our contract. Let's take a look:

// web3 is an Ethereum client library
const Web3 = require('web3');
const web3 = new Web3();

web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));

// This file is generated by the Solidity compiler to easily interact with
// the contract using the web3 library.
const loginAbi = require('../solidity/build/contracts/Login.json').abi;
const LoginContract = web3.eth.contract(loginAbi);

module.exports = LoginContract;


Web3 is the official client library to interact with Ethereum nodes. An Ethereum node is what actually connects to the rest of the Ethereum network. It performs "mining" (block generation), transaction operations (create and send) and block verification.

The Login.json file is generated by the Solidity contract compiler, part of the standard Ethereum development tools. The Solidity compiler takes Solidity source code and turns it into Ethereum Virtual Machine bytecode and an interface description file that can be used by Web3 to interact with the contract once it is uploaded to the network.

And here are our HTTP endpoints:

app.post('/login', (req, res) => {
    // All Ethereum addresses are 42 characters long
    if(!req.body.address || req.body.address.length !== 42) {
        res.sendStatus(400);
        return;
    }

    req.body.address = req.body.address.toLowerCase();

    const challenge = cuid();
    challenges[req.body.address] = challenge;

    const token = jwt.sign({ 
        address: req.body.address, 
        access: 'finishLogin'
    }, secret);

    res.json({
        challenge: challenge,
        jwt: token
    });
});

app.post('/finishLogin', validateJwt, (req, res) => {
    if(!req.jwt || !req.jwt.address || req.jwt.access !== 'finishLogin') {
        res.sendStatus(400);
        return;
    }

    if(successfulLogins[req.jwt.address]) {
        delete successfulLogins[req.jwt.address];
        delete challenges[req.jwt.address];

        const token = jwt.sign({ 
            address: req.jwt.address, 
            access: 'full'
        }, secret);

        res.json({
            jwt: token,
            address: req.jwt.address
        });
    } else {
        // HTTP Accepted (not completed)
        res.sendStatus(202);
    }
});

app.post('/apiTest', validateJwt, (req, res) => {
    if(req.jwt.access !== 'full') {
        res.sendStatus(401); //Unauthorized
        return;
    }

    res.json({
        message: 'It works!'
    });
});

The /login endpoint receives a login request carrying an Ethereum address for the user that wants to log in. The user must be the owner of such an Ethereum address. It generates a JWT and a challenge. The JWT can only be used to access the /finishLogin endpoint.

Before the user can call the  /finishLogin endpoint he or she must prove his or her identity by making a call to the login method of the Login contract. The login method receives a single parameter: the challenge returned by the /login endpoint. He must perform this call using the same account address that was passed to the /login endpoint. He or she can use any Ethereum wallet or client to do this.

After making the call to the login method of the Login contract, the user can complete the login by using the /finishLogin endpoint. He or she must pass the JWT returned by the /login endpoint to it. If the login is successful, a new JWT with full access is returned. Otherwise, if the login is still pending, an accepted HTTP status (202) is returned signaling proper verification of the login request is still pending. If the JWT passed to /finishLogin is invalid, an unauthorized HTTP status code is returned (401).

After the /finishLogin endpoint is called and the login process is completed, the returned JWT can be used to access other parts of the API. In this case, the /apiTest endpoint is available. It simply returns "It works!" wrapped in a JSON object if the user is logged-in.

Grab the full example.

Running the Example

Building and deploying the example is not as straightforward as it may seem due to the nature of Ethereum and current development tools. Here are the steps we used to test the example above.

1. Get an Ethereum Node Client

There are several Ethereum node clients. A popular one is go-ethereum, a client written in Go. Download it and install it.

Ethereum, as other cryptocurrencies do, has different versions of the blockchain with different parameters. There are essentially two blockchains: the main official blockchain and a test blockchain. The main blockchain never undoes operations once they are confirmed. Since some operations require money, the main blockchain is not ideal for testing. The test blockchain, on the other hand, is much less strict about forks and changes. It is also simpler to mine "Ether," Ethereum's currency.

We could use the test network for our example here. However, running a client node for any of the public networks is problematic for one reason: to be able to start doing transactions, the client must first verify all previous transactions in the blockchain. That means that bootstrapping a new client node takes quite a bit of time. Fortunately, there is an alternative: we can create a new, pristine private Ethereum blockchain to run our tests. To do so, run go-ethereum using the following command line:

./geth --rpc --nat none --dev


2. Create a New Ethereum Account to Mine Some Ether

The geth command can also be used to interact with a running client. Launch an interactive console connected to the running client:

/geth attach ipc:/var/folders/ts/7xznj_p13xb7_5th3w6yjmjm0000gn/T/ethereum_dev_mode/geth.ipc


The IPC file mentioned in the command can be found in the output from running the node in our first step. Look for the line that reads:

IPC endpoint opened: /var/folders/ts/7xznj_p13xb7_5th3w6yjmjm0000gn/T/ethereum_dev_mode/geth.ipc


Now in the Geth console type:

personal.newAccount()


After hitting ENTER a prompt will appear requesting a passphrase. This is the passphrase that will be used to perform any operations using this account. You can think of this as the passphrase required to decrypt the private key used to sign Ethereum transactions. Do not leave the prompt empty, choose a simple passphrase for testing instead. A new Ethereum address will be returned by the function. If at any point you forget this address, you can list accounts by inspecting personal.listAccounts (it's a variable, not a function, so don't add  ()  at the end).

The geth console is a JavaScript interpreter.

3. Start Mining Some Ether

Now it's time to add some Ether to our new account. Ether is required to perform operations in the Ethereum blockchain, so it is necessary to perform this step. Ether can be gathered in two ways: by receiving it from another account or by mining it. Since this is a private network, we will need to mine it. Don't worry, the private network is by default configured to be able to mine Ether easily. Let's do it:

miner.setEtherbase(personal.listAccounts[0]) // Hit ENTER
miner.start() // Hit ENTER


Now wait a few seconds (or minutes depending on your hardware) and then confirm you have some Ether in your account:

eth.getBalance(personal.listAccounts[0]) // Hit ENTER


4. Compile and Deploy Our Login Contract

To simplify the process of compiling and deploying contracts, we will use truffle. Truffle is a development framework for Ethereum, simplifying many common tasks. Install it:

npm install -g truffle


Before using truffle to deploy contracts, it is necessary to "unlock" our account in our Ethereum node client. Unlocking is the process of decrypting the private key and holding it in memory using the passphrase used to create it. This allows any client libraries (such as Truffle) connecting to the node to make operations on behalf of the unlocked account. Go to the geth console and type:

personal.unlockAccount(personal.listAccounts[0]) // Hit ENTER


Now switch to the solidity directory of our sample application. Edit the  truffle.js file and set your newly created address as the from key. Then run:

truffle migrate


The migrate command compiles and deploys the contracts to the Ethereum network on behalf of the account set in truffle.js. As a result, you will get the address of the newly deployed contract. Take note of it.

5. Install an Ethereum Wallet

Ethereum wallets are convenient interfaces for users to interact with the Ethereum network. Sending and receiving Ether, deploying contracts or making calls to them are all operations usually supported by wallets. Mist is the official Ethereum wallet. Download it and install it.

Once installed, we will need to tell Mist to connect to our private network rather than the public main or test networks. To do this, run Mist from the command line like so:

./Ethereum\ Wallet --rpc /var/folders/ts/7xznj_p13xb7_5th3w6yjmjm0000gn/T/ethereum_dev_mode/geth.ipc


The IPC file is the same file used by the geth console and can be gathered from the geth output logs.

6. Tell the Ethereum Wallet of the Contract

Many contracts live in the Ethereum network. Wallets need to know a contract's address and interface before being able to interact with them. Let's tell Mist about our Login contract. Go to Contracts -> Watch Contract (top right, then bottom left).

Watch contract

Complete the fields as follows:

  • Name: Login
  • Contract Address:
  • JSON Interface: the abi from Login.json. For convenience, it is pasted below. Copy and paste it in Mist.


Watch contract

As a test, now try to send some Ether to the Contracts -> Login -> Transfer Ether & Tokens contract:. Send1 Ether or any other amount less than your balance. You will need to provide the passphrase for your account.

Send Ether

7. Deploy the Backend

Go to the backend folder and run:

npm install
node app.js


8. Serve the Frontend

Go to the frontend folder and run:

npm install -g static-serve
static-serve


You may use any other simple static HTTP server such as Python's SimpleHTTPServer. If you do so, make sure to serve the app in port 9080. This is important due to CORS.

9. Test Everything Together!

Open your browser at http://localhost:9080. Now attempt to login by putting your Ethereum address in the input field. A challenge text will be generated. Go to the Mist (Ethereum Wallet) and go to the Login contract. To the right, you will see "WRITE TO CONTRACT." Select the login function and paste the challenge in the text fill that appears there. Then click onExecute. Input your passphrase and send the transaction.

Now switch back to the login page. After a few seconds, the login will be completed and a welcome message will appear. Voilà!

This example shows how a typical Ethereum user can use his existing Ethereum account to log in to any third party website supporting Ethereum. And all of this is done without a central server. Although authentication is not performed by the owner of the website, there is no central authority validating the user: it is the Ethereum network that does so.

Grab the full example.

Conclusion

We have taken a deeper look at Ethereum: a decentralized, blockchain-based framework for developing applications. Applications run on each node, and each state transition produced by them is validated and recorded by the blockchain. The power of the approach extends the concepts of Bitcoin to more than just monetary transactions or simple non-Turing complete contracts. The power of distributed apps is just beginning to be tapped. In the next post in the series, we will take a look at an actual application developed on the Ethereum network: a two-factor authentication system for Ethereum users using a mobile validator application. Stay tuned!

Explore the authentication advancements that are designed to secure accounts and payments—without overburdening consumers with a friction-laden experience.

Topics:
security ,ethereum ,blockchain

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}