loqman / two_factor_authentication

Two factor authentication extension for Devise

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Two factor authentication for Devise

Gitter

Build Status Code Climate

This fork is made to work with mongoid.

Features

  • Support for 2 types of OTP codes
  1. Codes delivered directly to the user
  2. TOTP (Google Authenticator) codes based on a shared secret (HMAC)
  • Configurable OTP code digit length
  • Configurable max login attempts
  • Customizable logic to determine if a user needs two factor authentication
  • Configurable period where users won't be asked for 2FA again
  • Option to encrypt the TOTP secret in the database, with iv and salt

Configuration

Initial Setup

In a Rails environment, require the gem in your Gemfile:

gem 'two_factor_authentication'

Once that's done, run:

bundle install

Note that Ruby 2.1 or greater is required.

Installation

Automatic initial setup

Not possible with mongoid for the moment. However, the manual setup is pretty simple!

Manual initial setup

If you prefer to set up the model and migration manually, add the :two_factor_authentication option to your existing devise options, such as:

devise :database_authenticatable, :registerable, :recoverable, :rememberable,
       :trackable, :validatable, :two_factor_authenticatable

Then add the needed fields and index to you model:

# Two factor authenticable
field :second_factor_attempts_count, type: Integer, default: 0
field :last_otp_lock, type: Time
field :encrypted_otp_secret_key
field :encrypted_otp_secret_key_iv
field :encrypted_otp_secret_key_salt
index({ encrypted_otp_secret_key: 1 }, { unique: true })

Complete the setup

Create the index with:

bundle exec rake db:mongoid:create_indexes

Add the following line to your model to fully enable two-factor auth:

has_one_time_password(encrypted: true)

Set config values in config/initializers/devise.rb:

config.max_login_attempts = 3  # Maximum second factor attempts count.
config.allowed_otp_drift_seconds = 30  # Allowed TOTP time drift between client and server.
config.otp_length = 6  # TOTP code length
config.direct_otp_valid_for = 5.minutes  # Time before direct OTP becomes invalid
config.direct_otp_length = 6  # Direct OTP code length
config.remember_otp_session_for_seconds = 30.days  # Time before browser has to perform 2fA again. Default is 0.
config.otp_secret_encryption_key = ENV['OTP_SECRET_ENCRYPTION_KEY']
config.second_factor_resource_id = 'id' # Field or method name used to set value for 2fA remember cookie

The otp_secret_encryption_key must be a random key that is not stored in the DB, and is not checked in to your repo. It is recommended to store it in an environment variable, and you can generate it with bundle exec rake secret.

Override the method in your model in order to send direct OTP codes. This is automatically called when a user logs in unless they have TOTP enabled (see below):

def send_two_factor_authentication_code(code)
  # Send code via SMS, etc.
end

Customisation and Usage

By default, second factor authentication is required for each user. You can change that by overriding the following method in your model:

def need_two_factor_authentication?(request)
  request.ip != '127.0.0.1'
end

In the example above, two factor authentication will not be required for local users.

This gem is compatible with Google Authenticator. To enable this a shared secret must be generated by invoking the following method on your model:

user.generate_totp_secret

This must then be shared via a provisioning uri:

user.provisioning_uri # This assumes a user model with an email attribute

This provisioning uri can then be turned in to a QR code if desired so that users may add the app to Google Authenticator easily. Once this is done, they may retrieve a one-time password directly from the Google Authenticator app.

Overriding the view

The default view that shows the form can be overridden by adding a file named show.html.erb (or show.html.haml if you prefer HAML) inside app/views/devise/two_factor_authentication/ and customizing it. Below is an example using ERB:

<h2>Hi, you received a code by email, please enter it below, thanks!</h2>

<%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
  <%= text_field_tag :code %>
  <%= submit_tag "Log in!" %>
<% end %>

<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>

Upgrading from version 1.X to 2.X

The following database fields are new in version 2.

  • direct_otp
  • direct_otp_sent_at
  • totp_timestamp

To add them, add these fields to your model:

field :direct_otp, type: String
field :direct_otp_sent_at, type: DateTime
field :totp_timestamp, type: Integer

The otp_secret_key is not only required for users who use Google Authentictor, so unless it has been shared with the user it should be set to nil. The following pseudo-code is an example of how this might be done:

User.find_each do |user| do
  if !uses_authentictor_app(user)
    user.otp_secret_key = nil
  end
end

Adding the TOTP encryption option to an existing app

  1. Follow the manual installation
  2. Create and run a task to create users secret keys, such as
    desc 'rake task to update users with otp secret key'
    task update_users_with_otp_secret_key: :environment do
      User.each do |user|
        user.otp_secret_key = ROTP::Base32.random_base32
        user.save!
      end
    end

Example App

TwoFactorAuthenticationExample

About

Two factor authentication extension for Devise

License:MIT License


Languages

Language:Ruby 93.9%Language:HTML 6.1%