Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Example Rails TFA application using Authlogic and Google Authenticator

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 app
Octocat-spinner-32 config
Octocat-spinner-32 db
Octocat-spinner-32 doc
Octocat-spinner-32 lib
Octocat-spinner-32 log
Octocat-spinner-32 public
Octocat-spinner-32 script
Octocat-spinner-32 spec
Octocat-spinner-32 vendor
Octocat-spinner-32 .gitignore
Octocat-spinner-32 .rspec
Octocat-spinner-32 Gemfile
Octocat-spinner-32 Gemfile.lock
Octocat-spinner-32 Guardfile
Octocat-spinner-32 LICENSE
Octocat-spinner-32 README.markdown
Octocat-spinner-32 Rakefile
Octocat-spinner-32 config.ru
README.markdown

Two Factor Authentication Example

A bare bones example Rails 3.2 application and test suite demonstrating the use of the Authlogic gem and custom two-factor authentication (TFA) with Google authenticator support.

Typical Use Case

Intranet application with username/password authentication for LAN users. Remote users are required to use TFA.

Demo Features

  • Authlogic handles authentication, there are no changes to Authlogic, TFA is completely separate and invoked after Authlogic authorization.
  • Google Authenticator QR code secret entry without generating images (RQRCode) or sending secrets to the charting services.
  • TFA confirmation will expire with the session but can expire before the session if required.
  • TFA confirmation code brute force protection applies to the user, not just the session. Lockout after 5 failures, requires manual reset.

Demo Limitations

Correctable in a production application

  • Production implementations must use SSL otherwise this implementation, and Authlogic itself, is vulnerable to session hijacking. See below for configuration options.
  • TFA secret and TFA failure count reset is not implemented
  • Users can view other user secrets, they should be scoped to the current user, i.e. a 'My Account' page.
  • TFA Google Authenticator secret setup requires viewing the user record, the demo has all IP addresses including the localhost address restricted by default. This can be changed. See configuration options below.

Example Application Usage

git clone http://github.com/robertwahler/two_factor_authentication_example
cd two_factor_authentication_example

bundle install

bundle exec rake db:migrate
bundle exec rake db:seed

bundle exec rails server

login creditials for the admin user

login: admin
password: admin
Google Authenticator time based two_factor_secret (spaces are optional): v6na sf4k fe45 qxbq

run the app

firefox http://localhost:3000

run the RSpec test suite

bundle exec rake db:test:prepare

bundle exec rspec

for development, start-up the Spork process via Guard

bundle exec guard

Demo Configuration Options

TFA configuration

Change TFA brute force failure count in app/models/user.rb

def two_factor_failure_count_exceeded?
  self.two_factor_failure_count >= 5
end

Change length of time the TFA confirmation is valid in app/models/user.rb

def two_factor_confirmed_at_valid_for
  12.hours
end

Change the sliding window width from the default of one 30 second window in app/controllers/user_sessions_controller.rb

# Use a sliding time window to validate tokens.  System clock inaccuracy can
# be tolerated at the expense a small decrease in security.   A value of 0
# will disable the sliding window
#
# A value of 2 will check tokens in two windows before and after the current
# 30 second window. ie. +/- 60 seconds surrounding the current window.
#
# @return [Integer] width of the window in 30 second increments
def sliding_window_width
  1
end

Excluding IP Ranges from TFA

Change ApplicationController to allow all logins to bypass TFA

  def two_factor_excluded_ip_addresses
    [IPAddress.parse("0.0.0.0/0")]
  end

Change ApplicationController to allow a localhost subnet to access without TFA

  def two_factor_excluded_ip_addresses
    [IPAddress.parse("127.0.0.1/24")]
  end

Change ApplicationController to allow LAN subnet to access without TFA

  def two_factor_excluded_ip_addresses
    [IPAddress.parse("10.0.0.1/24")]
  end

Change ApplicationController to allow both localhost and LAN subnet to access without TFA

  def two_factor_excluded_ip_addresses
    [IPAddress.parse("127.0.0.1/24"), IPAddress.parse("10.0.0.1/24")]
  end

Forcing SSL in Production

config/environments/production.rb

# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true

app/models/user_session.rb

# Should the cookie be set as httponly?  If true, the cookie will not be
# accessable from javascript
httponly true

# Should the cookie be set as secure?  If true, the cookie will only be sent
# over SSL connections
#
# Secure the cookie when the session_store is secure (production SSL)
secure true

config/initializers/session_store.rb

add these options to the session_store

:httponly => true,
:secure => Rails.env.production?

Dependencies

Runtime

Development

Additional References

Example Application Generation

Initial generation

rails version

rails -v

    Rails 3.2.1

generate basic rails application

rails new two_factor_authentication_example --skip-bundle -T

Gemfile

gem "authlogic", "~> 3.1.0"
gem "rotp", "~> 1.3.2"
gem "rqrcode", "~> 0.4.2"
gem "ipaddress", "~> 0.8.0"
gem 'uuidtools', "~> 2.1.2"

group :test, :development do
  gem "ruby-debug"
  gem "rspec-rails", "~> 2.8"
  gem "factory_girl_rails", "~> 1.6"
  gem "capybara", "~> 1.1"
  gem "database_cleaner", "~> 0.7.1"
  gem "timecop", "= 0.3.5"
end

Gemfile.lock

bundle update

basic user scaffold and manual authlogic configuration

rails generate scaffold user email:string first_name:string last_name:string login:string --fixture-replacement

add admin user seed

 rake db:seed

Future Rails updates

rails new .

Copyright

Copyright (c) 2012-2013 GearheadForHire, LLC. See LICENSE for details.

Something went wrong with that request. Please try again.