Download Source Code


# Terminal
bundle add devise_fido_usf
rails g devise_fido_usf:install
rails g devise_fido_usf:views
rails g devise:views
yarn add u2f-api

# models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :fido_usf_registerable, :fido_usf_authenticatable

# views/layouts/application.html.erb
<%= javascript_include_tag 'u2f-api.js', 'data-turbolinks-track': 'reload' %>

# config/initializers/assets.rb
Rails.application.config.assets.paths << Rails.root.join('node_modules')
Rails.application.config.assets.precompile += %w( u2f-api.js )

# db/migrate/20210627014642_create_fido_usf_devices.rb
class CreateFidoUsfDevices < ActiveRecord::Migration[6.1]
  def change
    create_table :fido_usf_devices do |t|
      t.references  :user,                  null: false, polymorphic: true, index: true
      t.string      :name,                  null: false, default: ''
      t.string      :key_handle,            null: false, limit: 255, default: ''
      t.binary      :public_key,            null: false, limit: 10.kilobytes
      t.binary      :certificate,           null: false, limit: 1.megabyte
      t.integer     :counter,               null: false, default: 0
      t.timestamp   :last_authenticated_at, null: false
    add_index :fido_usf_devices, :key_handle
    add_index :fido_usf_devices, :last_authenticated_at

# views/devise/registrations/edit.html.erb
<h2>Hardware Keys</h2>
<% if current_user.fido_usf_devices.empty? %>
  <p>No devices registered</p>
<% else %>
  <%= render 'devise/fido_usf_registrations/devices' %>
  <p class="text-muted">
    Sign in will always require a second
    factor during authentication as active
    as long as at least one FIDO U2F device
    is registered for your account!
    Delete all of aboves FIDO U2F devices
    to disable second factor authentication.
<% end %>
<p><%= link_to t('devise.registrations.register_2fa'),
  new_user_fido_usf_registration_path, class: 'btn btn-primary' %></p>

# application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception, prepend: true

# views/devise/fido_usf_registrations/destroy.js.erb
// $('#device_<%= @fade_out_id %>').fadeOut(400, function () {
//   $('#devices').html('<%= j(render(partial: "devise/fido_usf_registrations/device", collection: @devices)) %>')
// } );

elements = document.getElementById('devices')
elements.innerHTML = "<%= j render(partial: 'devise/fido_usf_registrations/device', collection: @devices) %>"