Coder Social home page Coder Social logo

ruby-u2f's Introduction

Ruby U2F

Gem Version Dependency Status security

Build Status Code Climate Coverage Status

Provides functionality for working with the server side aspects of the U2F protocol as defined in the FIDO specifications. To read more about U2F and how to use a U2F library, visit developers.yubico.com/U2F.

What is U2F?

U2F 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.

Working example application

Check out the example directory for a fully working Padrino server demonstrating U2F.

There is another demo application available using the Cuba framework: cuba-u2f-demo and a blog post explaining the protocol and the implementation.

You'll need Google Chrome 41 or later to use U2F.

Installation

Add the u2f gem to your Gemfile

gem 'u2f'

Usage

The U2F library has two major tasks:

  • Register new devices.
  • Authenticate previously registered devices.

Each 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.

You'll need an instance of U2F::U2F, which is conveniently placed in an instance method on the controller. The initializer takes an App ID as argument.

def u2f
  @u2f ||= U2F::U2F.new(request.base_url)
end

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 for more details.

Registration

Generate the requests which will be sent to the U2F device.

# registrations_controller.rb
def new
  # Generate one for each version of U2F, currently only `U2F_V2`
  @registration_requests = u2f.registration_requests

  # Store challenges. We need them for the verification step
  session[:challenges] = @registration_requests.map(&:challenge)

  # Fetch existing Registrations from your db and generate SignRequests
  key_handles = Registration.map(&:key_handle)
  @sign_requests = u2f.authentication_requests(key_handles)

  render 'registrations/new'
end

Render a form that will be automatically posted when the U2F device reponds.

<!-- registrations/new.html -->
<form action="/registrations" method="post">
  <input type="hidden" name="response">
</form>
// render requests from server into Javascript format
var registerRequests = <%= @registration_requests.as_json.to_json.html_safe %>;
var signRequests = <%= @sign_requests.as_json.to_json.html_safe %>;

u2f.register(registerRequests, signRequests, function(registerResponse) {
  var form, reg;

  if (registerResponse.errorCode) {
    return alert("Registration error: " + registerResponse.errorCode);
  }

  form = document.forms[0];
  response = document.querySelector('[name=response]');

  response.value = JSON.stringify(registerResponse);

  form.submit();
});

Catch the response on your server, verify it, and store a reference to it in your database.

# registrations_controller.rb
def create
  response = U2F::RegisterResponse.load_from_json(params[:response])

  reg = begin
    u2f.register!(session[:challenges], response)
  rescue U2F::Error => e
    return "Unable to register: <%= e.class.name %>"
  ensure
    session.delete(:challenges)
  end

  # save a reference to your database
  Registration.create!(certificate: reg.certificate,
                       key_handle:  reg.key_handle,
                       public_key:  reg.public_key,
                       counter:     reg.counter)

  'Registered!'
end

Authentication

Generate the requests which will be sent to the U2F device.

# authentications_controller.rb
def new
  # Fetch existing Registrations from your db
  key_handles = Registration.map(&:key_handle)
  return 'Need to register first' if key_handles.empty?

  # Generate SignRequests
  @sign_requests = u2f.authentication_requests(key_handles)

  # Store challenges. We need them for the verification step
  session[:challenges] = @sign_requests.map(&:challenge)

  render 'authentications/new'
end

Render a form that will be automatically posted when the U2F device reponds.

<!-- registrations/new.html -->
<form action="/authentications" method="post">
  <input type="hidden" name="response">
</form>
// render requests from server into Javascript format
var signRequests = <%= @sign_requests.as_json.to_json.html_safe %>;

u2f.sign(signRequests, function(signResponse) {
  var form, reg;

  if (signResponse.errorCode) {
    return alert("Authentication error: " + signResponse.errorCode);
  }

  form = document.forms[0];
  response = document.querySelector('[name=response]');

  response.value = JSON.stringify(signResponse);

  form.submit();
});

Catch the response on your server, verify it, and bump the counter in your database reference.

# authentications_controller.rb
def create
  response = U2F::SignResponse.load_from_json(params[:response])

  registration = Registration.first(key_handle: response.key_handle)
  return 'Need to register first' unless registration

  begin
    u2f.authenticate!(session[:challenges], response,
                      Base64.decode64(registration.public_key),
                      registration.counter)
  rescue U2F::Error => e
    return "Unable to authenticate: <%= e.class.name %>"
  ensure
    session.delete(:challenges)
  end

  registration.update(counter: response.counter)

  'Authenticated!'
end

License

MIT License. Copyright (c) 2015 by Johan Brissmyr and Sebastian Wallin

ruby-u2f's People

Contributors

abookyun avatar alexmuller avatar alokmenghrajani avatar badboy avatar bkeepers avatar brissmyr avatar konklone avatar maryamklabib avatar mastahyeti avatar otms61 avatar snood1205 avatar wallin avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.