{"id":15640772,"url":"https://github.com/q9f/ethereum-on-rails","last_synced_at":"2025-04-30T08:59:44.324Z","repository":{"id":43400623,"uuid":"401651230","full_name":"q9f/ethereum-on-rails","owner":"q9f","description":"ethereum on rails (template): connect metamask to ruby on rails.","archived":false,"fork":false,"pushed_at":"2022-05-30T09:30:28.000Z","size":894,"stargazers_count":76,"open_issues_count":0,"forks_count":20,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-30T08:59:23.090Z","etag":null,"topics":["authentication","eip191","ethereum","metamask","rails","ruby","user-management"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/q9f.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-31T09:47:49.000Z","updated_at":"2024-12-01T17:09:59.000Z","dependencies_parsed_at":"2022-08-23T00:20:56.992Z","dependency_job_id":null,"html_url":"https://github.com/q9f/ethereum-on-rails","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q9f%2Fethereum-on-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q9f%2Fethereum-on-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q9f%2Fethereum-on-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q9f%2Fethereum-on-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/q9f","download_url":"https://codeload.github.com/q9f/ethereum-on-rails/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251674576,"owners_count":21625644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","eip191","ethereum","metamask","rails","ruby","user-management"],"created_at":"2024-10-03T11:39:59.822Z","updated_at":"2025-04-30T08:59:44.277Z","avatar_url":"https://github.com/q9f.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# README\n\nEthereum on Rails MVP: allow sign-up and log-in with Ethereum wallet (e.g., MetaMask browser extension).\n\n[![volkswagen status](https://auchenberg.github.io/volkswagen/volkswargen_ci.svg?v=1)](https://github.com/auchenberg/volkswagen)\n[![Visitors](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fq9f%2Fethereum-on-rails\u0026count_bg=%2379C83D\u0026title_bg=%23555555\u0026icon=rubygems.svg\u0026icon_color=%23FF0000\u0026title=visitors\u0026edge_flat=false)](https://hits.seeyoufarm.com)\n[![Top Language](https://img.shields.io/github/languages/top/q9f/ethereum-on-rails?color=red)](https://github.com/q9f/ethereum-on-rails/pulse)\n[![Open-Source License](https://img.shields.io/github/license/q9f/ethereum-on-rails)](LICENSE)\n\n: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:\n* [Finally authenticating Rails users with MetaMask](https://dev.to/q9/finally-authenticating-rails-users-with-metamask-3fj)\n\n:warning: If you are here to **build Sign-in with Ethereum for production**, you should use the SIWE libraries instead:\n* [Sign-In with Ethereum - Ruby Library and Rails Examples Release](https://blog.spruceid.com/sign-in-with-ethereum-ruby-library-release-and-rails-examples/)\n\n![Screenshot of the log-in page](./img/screenshot.png)\n\n### Required versions\n\n* Ruby `^3.0.0`\n* Rails `^7.0.0`\n* Node `^17.4.0`\n\n### System dependencies\n\n* Ruby, Gems, Rails, SQLite3, Bundler, NodeJS, NPM, Yarn\n\n```bash\npacman -S ruby rubygems sqlite nvm\nnvm install stable\nnpm install --global npm yarn\ngem install bundler rails\n```\n\n### Run dev server\n\n```bash\nnvm use stable\nbundle install\nbin/rails webpacker:install\nbin/rails db:migrate\nbin/rails server\n```\n\n### How it works\n\n##### Model: `User`\n\nThe user contains three attributes: `username`, `eth_address`, `eth_nonce`.\n- The Name provides a pretty identity.\n- The Address is the unique identifier used for authentiation.\n- The Nonce is a random `UUID` that has to be signed for authentiation.\n\nIn this MVP, all three fields are mandatory and have to be unique.\n\n##### Controller: `Users{#new,#create}`\n\nThe users controller is solely used for creating new users.\n- It generates an initial random nonce with `SecureRandom.uuid`.\n- It ensures the user picks a name.\n- It takes the `eth_address` from the sign-up view (see below).\n- It ensures the `eth_address` is a valid Ethereum address.\n- It creates a new user and saves it to the database with the given attributes.\n\n##### View: `Users#new`\n\nThe `Users#new` view is the sign-up page to create a new account.\n- It contains a field for the user to choose a name.\n- It contains a read-only, hidden field that will be populated with the user's Ethereum address.\n- It contains a `Connect` button to establish a connection with the Ethereum provider.\n\nThe JavaScript pack `users_new.js` contains the frontend logic to establish a connection with an Ethereum wallet.\n- It hides read-only fields.\n- It ensures an Ethereum context is available.\n- It adds an click-event listener to the connect button.\n- It requests accounts from the available Ethereum wallet: `method: 'eth_requestAccounts'`\n- It adds the `eth_address` to the form and submits it.\n\n##### Controller: `Sessions{#new,#create,#destroy}`\n\nThe sessions controller manages the user authentication (login/logout).\n- It finds the user by `eth_address` provided by Ethereum wallet.\n- It ensures user exists in database.\n- It ensures user signed a message to authenticate.\n- It ensures the signature is not expired (older than 5 minutes).\n- It ensures the signed nonce matches with our database.\n- It recovers the pubkey and address from the signature.\n- It ensures the recovered address matches the address in the database.\n- It logs the user in if all the above is true.\n- If generates a new nonce for future logins if all of the above is true.\n\nIt also handles destroying sessions to log users out.\n\nThe JavaScript pack `sessions_new.js` contains the frontend logic to authenticate a user with an Ethereum account.\n- It hides all the read-only fields.\n- It ensures an Ethereum context is available.\n- It adds a click-event listener to the connect button.\n- It requests accounts from the available Ethereum wallet: `method: 'eth_requestAccounts'`\n- It requests the nonce belonging to the account from the API/v1 (see below): `fetch(\"/api/v1/users/\" + account)`\n- It generates a message containing the site's title, the request time, and the nonce from the API.\n- It requests the user to sign the message: `method: 'personal_sign', params: [ message, account ]`\n- It populates the form with address, message, and signature and submits it.\n\n##### API: `/api/v1/users{#index,#show}`\n\nTo 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.\n\nThe `#index` controller explicitly returns `nil` to prevent accessing the full set of users from the database.\n- GET `/api/v1/users`, returns `null`\n\nThe `#show` controller gets a user by `eth_address` from the database and returns the `eth_nonce` or `nil` if it does not exist.\n- GET `/api/v1/users/${eth_account}`\n- It ensures the `eth_account` parameter is a valid Ethereum address to filter out seemingly random requests.\n- It finds a user in database by `eth_account` key.\n- It returns _only_ the `eth_nonce` as JSON.\n- It returns `null` if it fails in any step above.\n\n### Credits\n\nThe Ethereum-on-Rails template was written by [@q9f](https://github.com/q9f) can be found and used on Github directly: [github/q9f/ethereum-on-rails](https://github.com/q9f/ethereum-on-rails/)\n\nThis Rails application template implements the logic described by Amaury Martiny in [One-click Login with Blockchain: A MetaMask Tutorial](https://www.toptal.com/ethereum/one-click-login-flows-a-metamask-tutorial) - brilliant, though slightly outdated, resource! Thanks for that.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fq9f%2Fethereum-on-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fq9f%2Fethereum-on-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fq9f%2Fethereum-on-rails/lists"}