{"id":19745161,"url":"https://github.com/castle/ruby-u2f","last_synced_at":"2025-05-14T07:09:44.235Z","repository":{"id":22675549,"uuid":"26019042","full_name":"castle/ruby-u2f","owner":"castle","description":"U2F library in Ruby","archived":false,"fork":false,"pushed_at":"2025-04-14T09:41:00.000Z","size":171,"stargazers_count":259,"open_issues_count":6,"forks_count":26,"subscribers_count":33,"default_branch":"master","last_synced_at":"2025-04-14T15:02:49.127Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/castle.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2014-10-31T14:19:09.000Z","updated_at":"2025-04-14T09:41:05.000Z","dependencies_parsed_at":"2025-04-14T14:05:57.652Z","dependency_job_id":null,"html_url":"https://github.com/castle/ruby-u2f","commit_stats":{"total_commits":130,"total_committers":18,"mean_commits":7.222222222222222,"dds":0.5923076923076923,"last_synced_commit":"e29c24ab0cca35b0518b7b61344de0924a95337d"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/castle%2Fruby-u2f","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/castle%2Fruby-u2f/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/castle%2Fruby-u2f/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/castle%2Fruby-u2f/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/castle","download_url":"https://codeload.github.com/castle/ruby-u2f/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254092788,"owners_count":22013290,"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":[],"created_at":"2024-11-12T02:04:28.899Z","updated_at":"2025-05-14T07:09:39.225Z","avatar_url":"https://github.com/castle.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ruby U2F\n\n[![Gem Version](https://badge.fury.io/rb/u2f.svg)](https://badge.fury.io/rb/u2f)\n[![security](https://hakiri.io/github/castle/ruby-u2f/master.svg)](https://hakiri.io/github/castle/ruby-u2f/master)\n\n[![Build Status](https://travis-ci.org/castle/ruby-u2f.svg?branch=API_v1_1)](https://travis-ci.org/castle/ruby-u2f)\n[![Code Climate](https://codeclimate.com/github/castle/ruby-u2f/badges/gpa.svg)](https://codeclimate.com/github/castle/ruby-u2f)\n[![Coverage Status](https://img.shields.io/coveralls/castle/ruby-u2f.svg)](https://coveralls.io/r/castle/ruby-u2f)\n\nProvides functionality for working with the server side aspects of the U2F\nprotocol as defined in the [FIDO specifications](http://fidoalliance.org/specifications/download). To read more about U2F and how to use a U2F library, visit [developers.yubico.com/U2F](http://developers.yubico.com/U2F).\n\n## What is U2F?\n\nU2F is an open 2-factor authentication standard that enables keychain devices, mobile phones and other devices to securely access any number of web-based services — instantly and with no drivers or client software needed. The U2F specifications were initially developed by Google, with contribution from Yubico and NXP, and are today hosted by the [FIDO Alliance](https://fidoalliance.org/).\n\n## Working example application\n\nCheck out the [example](https://github.com/castle/ruby-u2f/tree/master/example) directory for a fully working Padrino server demonstrating U2F.\n\nThere is another demo application available using the [Cuba](https://github.com/soveran/cuba) framework: [cuba-u2f-demo](https://github.com/badboy/cuba-u2f-demo) and a [blog post explaining the protocol and the implementation](http://fnordig.de/2015/03/06/u2f-demo-application/).\n\nYou'll need Google Chrome 41 or later to use U2F.\n\n## Installation\n\nAdd the `u2f` gem to your `Gemfile`\n\n```ruby\ngem 'u2f'\n```\n\n## Usage\n\nThe U2F library has two major tasks:\n\n- **Register** new devices.\n- **Authenticate** previously registered devices.\n\nEach task starts by generating a challenge on the server, which is rendered to a web view, read by the browser APIs and transmitted to the plugged in U2F devices for verification. The U2F device responds and triggers a callback in the browser, and a form is posted back to your server where you verify the challenge and store the U2F device information to your database.\n\nNote that ordinarily, each user will have one or more U2F registrations (as it's a common usage pattern for users to have more than one U2F device -- for example one for regular use, and a second stored safely as a backup). While it's omitted from examples here for brevity, a new registration should typically be associated with the particular user registering. Likewise, when authenticating, queries over \"all registrations\" should actually be scoped to registrations associated with the particular user being authenticated.\n\nYou'll need an instance of `U2F::U2F`, which is conveniently placed in an [instance method](https://github.com/castle/ruby-u2f/blob/API_v1_1/example/app/helpers/helpers.rb) on the controller. The initializer takes an **App ID** as argument.\n\n```ruby\ndef u2f\n  @u2f ||= U2F::U2F.new(request.base_url)\nend\n```\n\n**Important:** A U2F client (e.g. Chrome) will compare the App ID with the current URI, so make sure it's the right format including schema and port, e.g. `https://demo.example.com:3000`. Check out the [App ID specification](https://developers.yubico.com/U2F/App_ID.html) for more details.\n\n### Registration\n\nGenerate the requests which will be sent to the U2F device.\n\n```ruby\n# registrations_controller.rb\ndef new\n  # Generate one for each version of U2F, currently only `U2F_V2`\n  @registration_requests = u2f.registration_requests\n\n  # Store challenges. We need them for the verification step\n  session[:challenges] = @registration_requests.map(\u0026:challenge)\n\n  # Fetch existing Registrations from your db and generate SignRequests\n  key_handles = Registration.map(\u0026:key_handle)\n  @sign_requests = u2f.authentication_requests(key_handles)\n\n  @app_id = u2f.app_id\n\n  render 'registrations/new'\nend\n```\n\nRender a form that will be automatically posted when the U2F device reponds.\n\n```html\n\u003c!-- registrations/new.html --\u003e\n\u003cform action=\"/registrations\" method=\"post\"\u003e\n  \u003cinput type=\"hidden\" name=\"response\"\u003e\n\u003c/form\u003e\n```\n\n```javascript\n// render requests from server into Javascript format\nvar appId = \u003c%= @app_id.to_json.html_safe %\u003e\nvar registerRequests = \u003c%= @registration_requests.to_json.html_safe %\u003e;\nvar signRequests = \u003c%= @sign_requests.as_json.to_json.html_safe %\u003e;\n\nu2f.register(appId, registerRequests, signRequests, function(registerResponse) {\n  var form, reg;\n\n  if (registerResponse.errorCode) {\n    return alert(\"Registration error: \" + registerResponse.errorCode);\n  }\n\n  form = document.forms[0];\n  response = document.querySelector('[name=response]');\n\n  response.value = JSON.stringify(registerResponse);\n\n  form.submit();\n});\n```\n\nCatch the response on your server, verify it, and store a reference to it in your database.\n\n```ruby\n# registrations_controller.rb\ndef create\n  response = U2F::RegisterResponse.load_from_json(params[:response])\n\n  reg = begin\n    u2f.register!(session[:challenges], response)\n  rescue U2F::Error =\u003e e\n    return \"Unable to register: \u003c%= e.class.name %\u003e\"\n  ensure\n    session.delete(:challenges)\n  end\n\n  # save a reference to your database\n  Registration.create!(certificate: reg.certificate,\n                       key_handle:  reg.key_handle,\n                       public_key:  reg.public_key,\n                       counter:     reg.counter)\n\n  'Registered!'\nend\n```\n\n### Authentication\n\nGenerate the requests which will be sent to the U2F device.\n\n```ruby\n# authentications_controller.rb\ndef new\n  # Fetch existing Registrations from your db\n  key_handles = Registration.map(\u0026:key_handle)\n  return 'Need to register first' if key_handles.empty?\n\n  # Generate SignRequests\n  @app_id = u2f.app_id\n  @sign_requests = u2f.authentication_requests(key_handles)\n  @challenge = u2f.challenge\n\n  # Store challenge. We need it for the verification step\n  session[:challenge] = @challenge\n\n  render 'authentications/new'\nend\n```\n\nRender a form that will be automatically posted when the U2F device reponds.\n\n```html\n\u003c!-- registrations/new.html --\u003e\n\u003cform action=\"/authentications\" method=\"post\"\u003e\n  \u003cinput type=\"hidden\" name=\"response\"\u003e\n\u003c/form\u003e\n```\n\n```javascript\n// render requests from server into Javascript format\nvar signRequests = \u003c%= @sign_requests.to_json.html_safe %\u003e;\nvar challenge = \u003c%= @challenge.to_json.html_safe %\u003e;\nvar appId = \u003c%= @app_id.to_json.html_safe %\u003e;\n\nu2f.sign(appId, challenge, signRequests, function(signResponse) {\n  var form, reg;\n\n  if (signResponse.errorCode) {\n    return alert(\"Authentication error: \" + signResponse.errorCode);\n  }\n\n  form = document.forms[0];\n  response = document.querySelector('[name=response]');\n\n  response.value = JSON.stringify(signResponse);\n\n  form.submit();\n});\n```\n\nCatch the response on your server, verify it, and bump the counter in your database reference.\n\n```ruby\n# authentications_controller.rb\ndef create\n  response = U2F::SignResponse.load_from_json(params[:response])\n\n  registration = Registration.first(key_handle: response.key_handle)\n  return 'Need to register first' unless registration\n\n  begin\n    u2f.authenticate!(session[:challenge], response,\n                      Base64.decode64(registration.public_key),\n                      registration.counter)\n  rescue U2F::Error =\u003e e\n    return \"Unable to authenticate: \u003c%= e.class.name %\u003e\"\n  ensure\n    session.delete(:challenge)\n  end\n\n  registration.update(counter: response.counter)\n\n  'Authenticated!'\nend\n```\n\n## License\n\nMIT License. Copyright (c) 2015 by Johan Brissmyr and Sebastian Wallin\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcastle%2Fruby-u2f","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcastle%2Fruby-u2f","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcastle%2Fruby-u2f/lists"}