Authentication: Ethereum and Smart Contracts Part 2

Towards a Practical Authentication Solution for Ethereum Users

Authentication is what my team does at Auth0, so we teamed up with the guys from GFT's Innovation Team to think of a better way of using Ethereum for this purpose. We came up with a proof of concept which we will share with you in this post. First, let's describe the design goals for our system:
  • It should allow users with an Ethereum address to use that address to log in to a third-party website (that supports this login method).
  • It should be easy to use and reasonably easy to setup.
  • It should not compromise the security of the user's Ethereum account.
  • It should allow users to recover their credentials in case of loss or theft.
  • It should not require knowledge of contracts or manually calling contract methods.
  • It should have reasonable latency for a login system (no more than a couple of seconds).
  • It should not cost users gas (or money) to log in.
  • It should be reasonably easy for developers to implement in their apps.
One of the biggest problems with our initial approach was that writes were necessary. This forced users who wanted to log in to spend Ether to do so. Worse, they had to wait for confirmation of the transaction before the login was successful. On the other hand, this made the login process truly decentralized.
Writes were a requirement for our previous system due to the way Ethereum events work. Events are special operations in the Ethereum network that can be watched by nodes. Internally, events are Ethereum Virtual Machine ops that create data that is added to the transaction when it is mined. Events do not work on read-only (constant) Solidity functions since they are added to a transaction when it is mined. This forces users of our first system to pay to generate aLoginAttempt event.
This limitation forced us to make a compromise: rather than remain entirely decentralized, we added a server to handle authentication requests. In turn, this server relies on data stored in the Ethereum blockchain. However, our system does retain the ability to allow for serverless logins. We will see how that works later on.
Another big limitation of our first system was that the user needed access to his Ethereum wallet to log in. This is impractical for several reasons:
  • Users usually keep a single wallet. In other words, they do not carry around their private keys to easily use them on different devices.
  • If a user loses his or her Ethereum private key, he may never be able to authenticate again with that address to a third party service, not even to switch his main address or recover his information. This poses a problem for long-term use of the system.
  • Requiring a user to use his or her private key for each login can be a security issue for accounts holding big amounts of value. For those accounts, private keys may be stored safely and used only when necessary. Requiring their use for each login is less than ideal.
So some way of using an Ethereum address to login without requiring the private key for that address must be implemented for our new system.

A Login System for Ethereum Users

So, here is what we implemented. Our system relies on three key elements: an authentication server, a mobile application, and the Ethereum network. Here's how they play together.
Architecture
To keep the user's Ethereum address separate from the authentication process, a different, authentication only, Ethereum address is generated by the system. This address is associated with the user's Ethereum address using an Ethereum contract. In other words, a mapping between the user's Ethereum address and the system's login-only address is established. This mapping is stored in Ethereum's blockchain with the help of a contract.
pragma solidity ^0.4.2;
contract Mapper {
    event AddressMapped(address primary, address secondary);
    event Error(uint code, address sender);
    mapping (address => address) public primaryToSecondary;
    mapping (address => bool) public secondaryInUse;
    modifier secondaryAddressMustBeUnique(address secondary) {
        if(secondaryInUse[secondary]) {
            Error(1, msg.sender);
            throw;
        }
        _;
    }
    function mapAddress(address secondary) 
        secondaryAddressMustBeUnique(secondary) {
        // If there is no mapping, this does nothing
        secondaryInUse[primaryToSecondary[msg.sender]] = false;
        primaryToSecondary[msg.sender] = secondary;
        secondaryInUse[secondary] = true;
        AddressMapped(msg.sender, secondary);
    }
}
Although this contract is a bit more complex than we have seen so far, it remains fairly accessible. Let's break it down:
  • There are two events: AddressMapped and Error. The AddressMapped event is generated any time a user's primary Ethereum address is mapped to a secondary, login-only, address. The Error event is only generated in case of errors, such as when a mapping using an existing secondary address already exists.
  • Then two variables are declared: primaryToSecondary and secondaryInUseprimaryToSecondary is a map of addresses: given the primary address, it can tell the secondary address mapped to it. secondaryInUse is a map of addresses to booleans, used to check whether a secondary address is already in use.
  • Next, comes secondaryAddressMustBeUnique. This special function is a modifier. Modifiers in Solidity are special functions that can be attached to contract methods. These runs before the method code and can be used to modify their behavior. In this case, secondaryAddressMustBeUnique uses the secondaryInUse variable to confirm whether the secondary address passed as a parameter is in use. If it is, this is flagged as an error and the Error event is emitted. If it is not in use, then execution continues. The _ placeholder is where the code from the modified function is logically inserted.
  • And lastly, there is the mapAddress method. This method takes a secondary address and maps it to the address of the sender or caller of this method. The semantics of Ethereum make sure that the sender is who he says he is. In other words, only the owner of the private key for an address can make calls as the sender to a Solidity method. This makes sure, without any special check, that only the rightful owner of the primary address can establish a mapping between it and a secondary address used for logins. This is the crux of our system.
In summary, our contract does four things:
  • It establishes a mapping between two Ethereum addresses: one high-value address (the primary address) and a low value, login-only, secondary address.
  • It certifies only the owner of the primary address can establish this mapping.
  • It records this information publicly in the blockchain.
  • It emits events to monitor and react to changes in the data stored in it.
This is all we need to make our system work. Let's go over the full registration and authentication flow to see how it all works together. We assume the user is the rightful owner of an Ethereum account with a certain amount of Ether.

Registration

Registration
This is a one time only step to be performed the first time the user tries to use the system. Once registered, the user can use his or her Ethereum address with any third-party website. In other words, this is a system-wide, one time only step.
To simplify the authentication experience, our implementation uses a mobile application to authorize or deny authentication requests. A user who wants to enable his Ethereum account for use as an authentication factor first registers through the mobile application.
Registration is performed by following these steps:
  1. The user opens the mobile application.
  2. The user enters his or her email address and an unlock pattern.
  3. A new Ethereum address is generated behind the scenes by the mobile application. This is the secondary address. This address is sent to the user to his or her email for convenience.
  4. The user establishes a link between his or her primary Ethereum address and this secondary address. To do so the user can manually call the mapAddress method of the Mapper contract or use a special wallet app developed for this purpose. This step requires the user to spend a minimum amount of gas from his primary account.
  5. Once the link between addresses is established, the mobile application will show a confirmation dialog. If the user confirms, the mapping is established and the process is complete.
One of the added benefits of this approach is that it makes throwaway accounts harder to use. Point 4 forces the user to spend Ether to establish the mapping between his personal Ethereum address and the login-only address. This way, third-parties can be sure that the Ethereum account used by the user is not a throwaway account (i.e. a spam account).

Authentication

Authentication
Whenever a user who has already registered wants to use his or her Ethereum account to login to a third-party website, he or she follows this process:
  1. The user inputs his or her primary Ethereum address or his or her email in an input field and clicks "Login."
  2. The third-party website contacts the authentication server requesting authentication for that address. To do so the third-party website generates a challenge string with a specific format and passes it to the authentication server.
  3. The authentication server checks the Ethereum network for the current secondary address of the user. It then checks the internal database for the necessary data to contact the mobile device associated with that address.
  4. The user receives a mobile push notification to confirm or deny the login request.
  5. If the user accepts, the private key of the secondary address is used to sign the challenge. The signed challenge is then sent back to the authentication server.
  6. The authentication server verifies the signature and if it is valid and the challenge matches, it considers the login successful. It then sends back the signed challenge to the third-party website for optional independent confirmation.
That is all there is to it, really! This scheme separates the signature process from a sensitive primary address, preventing the exposure of a potentially important private key while still giving the third party site confirmation that the user is the rightful owner of that address. Furthermore, although it relies on the authentication server for convenience, it can still work without it and does not require trust to be placed in it (the third-party website can check the signature itself). Thus it remains decentralized in worst-case scenarios (authentication server down) and convenient for the common case.
As an added benefit, this system can easily be adapted to work like "Login with Facebook" or "Login with Google" as well.

Cons

As we have seen so far, our system appears to be more convenient than our initial, simple approach from the "Programmable Blockchain" of this series. However, it does come with a few limitations of its own. Let's take a brief look at them.
Our initial approach sported a key element from blockchain based systems: it was entirely decentralized. Our newer approach relies on an authentication server for convenience. Although it is possible for the system to work without the authentication server, it is not convenient to use it this way. This is by design and must be considered if a convenient decentralized operation is mandatory in all cases. In every case, however, no trust is placed in the server.
Tune in tomorrow for Part 3, where we'll discuss getting your Ethereum Wallet set up and how to use it. 
Share:

No comments:

Post a Comment

Contact Us

Name

Email *

Message *

Recent Posts

Info