ethereum-on-rails
ethereum-on-rails copied to clipboard
ethereum on rails (template): connect metamask to ruby on rails.
README
Ethereum on Rails MVP: allow sign-up and log-in with Ethereum wallet (e.g., MetaMask browser extension).
:heavy_check_mark: If you are here to learn Ethereum-account authentication, welcome! Feel free to read the following article that is based on this code:
:warning: If you are here to build Sign-in with Ethereum for production, you should use the SIWE libraries instead:

Required versions
- Ruby
^3.0.0 - Rails
^7.0.0 - Node
^17.4.0
System dependencies
- Ruby, Gems, Rails, SQLite3, Bundler, NodeJS, NPM, Yarn
pacman -S ruby rubygems sqlite nvm
nvm install stable
npm install --global npm yarn
gem install bundler rails
Run dev server
nvm use stable
bundle install
bin/rails webpacker:install
bin/rails db:migrate
bin/rails server
How it works
Model: User
The user contains three attributes: username, eth_address, eth_nonce.
- The Name provides a pretty identity.
- The Address is the unique identifier used for authentiation.
- The Nonce is a random
UUIDthat has to be signed for authentiation.
In this MVP, all three fields are mandatory and have to be unique.
Controller: Users{#new,#create}
The users controller is solely used for creating new users.
- It generates an initial random nonce with
SecureRandom.uuid. - It ensures the user picks a name.
- It takes the
eth_addressfrom the sign-up view (see below). - It ensures the
eth_addressis a valid Ethereum address. - It creates a new user and saves it to the database with the given attributes.
View: Users#new
The Users#new view is the sign-up page to create a new account.
- It contains a field for the user to choose a name.
- It contains a read-only, hidden field that will be populated with the user's Ethereum address.
- It contains a
Connectbutton to establish a connection with the Ethereum provider.
The JavaScript pack users_new.js contains the frontend logic to establish a connection with an Ethereum wallet.
- It hides read-only fields.
- It ensures an Ethereum context is available.
- It adds an click-event listener to the connect button.
- It requests accounts from the available Ethereum wallet:
method: 'eth_requestAccounts' - It adds the
eth_addressto the form and submits it.
Controller: Sessions{#new,#create,#destroy}
The sessions controller manages the user authentication (login/logout).
- It finds the user by
eth_addressprovided by Ethereum wallet. - It ensures user exists in database.
- It ensures user signed a message to authenticate.
- It ensures the signature is not expired (older than 5 minutes).
- It ensures the signed nonce matches with our database.
- It recovers the pubkey and address from the signature.
- It ensures the recovered address matches the address in the database.
- It logs the user in if all the above is true.
- If generates a new nonce for future logins if all of the above is true.
It also handles destroying sessions to log users out.
The JavaScript pack sessions_new.js contains the frontend logic to authenticate a user with an Ethereum account.
- It hides all the read-only fields.
- It ensures an Ethereum context is available.
- It adds a click-event listener to the connect button.
- It requests accounts from the available Ethereum wallet:
method: 'eth_requestAccounts' - It requests the nonce belonging to the account from the API/v1 (see below):
fetch("/api/v1/users/" + account) - It generates a message containing the site's title, the request time, and the nonce from the API.
- It requests the user to sign the message:
method: 'personal_sign', params: [ message, account ] - It populates the form with address, message, and signature and submits it.
API: /api/v1/users{#index,#show}
To prevent signature spoofing, the user needs to sign a specific piece of information we can verify in the backend rather than a random message. The User model contains an eth_nonce field that gets filled with a random UUID on first sign-up and gets rotated on every successful login.
The #index controller explicitly returns nil to prevent accessing the full set of users from the database.
- GET
/api/v1/users, returnsnull
The #show controller gets a user by eth_address from the database and returns the eth_nonce or nil if it does not exist.
- GET
/api/v1/users/${eth_account} - It ensures the
eth_accountparameter is a valid Ethereum address to filter out seemingly random requests. - It finds a user in database by
eth_accountkey. - It returns only the
eth_nonceas JSON. - It returns
nullif it fails in any step above.
Credits
The Ethereum-on-Rails template was written by @q9f can be found and used on Github directly: github/q9f/ethereum-on-rails
This Rails application template implements the logic described by Amaury Martiny in One-click Login with Blockchain: A MetaMask Tutorial - brilliant, though slightly outdated, resource! Thanks for that.