[DRE-commits] [ruby-devise-two-factor] 01/04: Imported Upstream version 3.0.0

Praveen Arimbrathodiyil praveen at moszumanska.debian.org
Sat Jun 11 17:48:54 UTC 2016


This is an automated email from the git hooks/post-receive script.

praveen pushed a commit to branch master
in repository ruby-devise-two-factor.

commit 1532e575da34ca62391fab82dff66c77b8017632
Author: Praveen Arimbrathodiyil <praveen at debian.org>
Date:   Sat Jun 11 22:59:07 2016 +0530

    Imported Upstream version 3.0.0
---
 .gitignore                                         |  82 ++++++++++----------
 .travis.yml                                        |   8 +-
 CHANGELOG.md                                       |  42 ++++++++++
 README.md                                          |  72 ++++++++++++-----
 UPGRADING.md                                       |  22 ++++++
 checksums.yaml.gz.sig                              | Bin 0 -> 512 bytes
 data.tar.gz.sig                                    | Bin 0 -> 512 bytes
 demo/.gitignore                                    |  16 ----
 demo/Gemfile                                       |  40 ----------
 demo/README.rdoc                                   |  28 -------
 demo/Rakefile                                      |   6 --
 demo/app/assets/images/.keep                       |   0
 demo/app/assets/javascripts/application.js         |  16 ----
 demo/app/assets/javascripts/home.js.coffee         |   3 -
 demo/app/assets/stylesheets/application.css        |  15 ----
 demo/app/assets/stylesheets/home.css.scss          |   3 -
 demo/app/controllers/application_controller.rb     |  13 ----
 demo/app/controllers/concerns/.keep                |   0
 demo/app/controllers/home_controller.rb            |   4 -
 demo/app/controllers/users_controller.rb           |  14 ----
 demo/app/helpers/application_helper.rb             |   2 -
 demo/app/helpers/home_helper.rb                    |   2 -
 demo/app/mailers/.keep                             |   0
 demo/app/models/.keep                              |   0
 demo/app/models/concerns/.keep                     |   0
 demo/app/models/user.rb                            |  10 ---
 demo/app/views/devise/confirmations/new.html.erb   |  12 ---
 .../mailer/confirmation_instructions.html.erb      |   5 --
 .../mailer/reset_password_instructions.html.erb    |   8 --
 .../devise/mailer/unlock_instructions.html.erb     |   7 --
 demo/app/views/devise/passwords/edit.html.erb      |  16 ----
 demo/app/views/devise/passwords/new.html.erb       |  12 ---
 demo/app/views/devise/registrations/edit.html.erb  |  29 -------
 demo/app/views/devise/registrations/new.html.erb   |  18 -----
 demo/app/views/devise/sessions/new.html.erb        |  20 -----
 demo/app/views/devise/shared/_links.erb            |  25 ------
 demo/app/views/devise/unlocks/new.html.erb         |  12 ---
 demo/app/views/home/index.html.erb                 |  20 -----
 demo/app/views/layouts/application.html.erb        |  14 ----
 demo/bin/bundle                                    |   3 -
 demo/bin/rails                                     |   4 -
 demo/bin/rake                                      |   4 -
 demo/config.ru                                     |   4 -
 demo/config/application.rb                         |  28 -------
 demo/config/boot.rb                                |   4 -
 demo/config/database.yml                           |  85 ---------------------
 demo/config/environment.rb                         |   5 --
 demo/config/environments/development.rb            |  37 ---------
 demo/config/environments/production.rb             |  83 --------------------
 demo/config/environments/test.rb                   |  39 ----------
 demo/config/initializers/backtrace_silencers.rb    |   7 --
 demo/config/initializers/cookies_serializer.rb     |   3 -
 demo/config/initializers/devise.rb                 |  18 -----
 .../initializers/filter_parameter_logging.rb       |   4 -
 demo/config/initializers/inflections.rb            |  16 ----
 demo/config/initializers/mime_types.rb             |   4 -
 demo/config/initializers/session_store.rb          |   3 -
 demo/config/initializers/wrap_parameters.rb        |  14 ----
 demo/config/locales/devise.en.yml                  |  59 --------------
 demo/config/locales/en.yml                         |  23 ------
 demo/config/routes.rb                              |   9 ---
 demo/config/secrets.yml                            |  22 ------
 .../migrate/20140515190128_devise_create_users.rb  |  42 ----------
 ...0140516191259_add_devise_two_factor_to_users.rb |   9 ---
 demo/db/schema.rb                                  |  42 ----------
 demo/db/seeds.rb                                   |   7 --
 demo/lib/assets/.keep                              |   0
 demo/lib/tasks/.keep                               |   0
 demo/log/.keep                                     |   0
 demo/public/404.html                               |  67 ----------------
 demo/public/422.html                               |  67 ----------------
 demo/public/500.html                               |  66 ----------------
 demo/public/favicon.ico                            |   0
 demo/public/robots.txt                             |   5 --
 demo/test/controllers/.keep                        |   0
 demo/test/controllers/home_controller_test.rb      |   9 ---
 demo/test/fixtures/.keep                           |   0
 demo/test/fixtures/users.yml                       |  11 ---
 demo/test/helpers/.keep                            |   0
 demo/test/helpers/home_helper_test.rb              |   4 -
 demo/test/integration/.keep                        |   0
 demo/test/mailers/.keep                            |   0
 demo/test/models/.keep                             |   0
 demo/test/models/user_test.rb                      |   7 --
 demo/test/test_helper.rb                           |  13 ----
 demo/vendor/assets/javascripts/.keep               |   0
 demo/vendor/assets/stylesheets/.keep               |   0
 devise-two-factor.gemspec                          |   6 +-
 lib/devise-two-factor.rb                           |   2 +-
 .../models/two_factor_authenticatable.rb           |  13 +++-
 .../two_factor_authenticatable_shared_examples.rb  |   9 ++-
 lib/devise_two_factor/version.rb                   |   2 +-
 metadata.gz.sig                                    | Bin 0 -> 512 bytes
 .../models/two_factor_authenticatable_spec.rb      |  57 +++++++++++++-
 spec/devise/models/two_factor_backupable_spec.rb   |   6 +-
 95 files changed, 244 insertions(+), 1274 deletions(-)

diff --git a/.gitignore b/.gitignore
index 90aafe9..16c876f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,49 +1,47 @@
-Gemfile.lock
 *.gem
-
-# rcov generated
-coverage
-coverage.data
-
-# yard generated
-doc
-.yardoc
-
-# bundler
-.bundle
-
-# jeweler generated
-pkg
-
-# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
+*.rbc
+/.config
+/coverage/
+/InstalledFiles
+/pkg/
+/spec/reports/
+/spec/examples.txt
+/test/tmp/
+/test/version_tmp/
+/tmp/
+
+## Specific to RubyMotion:
+.dat*
+.repl_history
+build/
+*.bridgesupport
+build-iPhoneOS/
+build-iPhoneSimulator/
+
+## Specific to RubyMotion (use of CocoaPods):
 #
-# * Create a file at ~/.gitignore
-# * Include files you want ignored
-# * Run: git config --global core.excludesfile ~/.gitignore
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
 #
-# After doing this, these files will be ignored in all your git projects,
-# saving you from having to 'pollute' every project you touch with them
-#
-# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
-#
-# For MacOS:
-#
-#.DS_Store
+# vendor/Pods/
 
-# For TextMate
-#*.tmproj
-#tmtags
+## Documentation cache and generated files:
+/.yardoc/
+/_yardoc/
+/doc/
+/rdoc/
 
-# For emacs:
-#*~
-#\#*
-#.\#*
+## Environment normalization:
+/.bundle/
+/vendor/bundle
+/lib/bundler/man/
 
-# For vim:
-#*.swp
-
-# For redcar:
-#.redcar
+# for a library or gem, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+Gemfile.lock
+.ruby-version
+.ruby-gemset
 
-# For rubinius:
-#*.rbc
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
diff --git a/.travis.yml b/.travis.yml
index b432a33..fe69e12 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,10 @@
+sudo: false
 language: ruby
 cache: bundler
+before_install:
+  - gem update --system
+  - gem update bundler
 rvm:
-  - "1.9.3"
-  - "2.0.0"
   - "2.1"
   - "2.2"
-  - jruby-19mode # JRuby in 1.9 mode
+  - "2.3.0"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..b2124c5
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,42 @@
+# CHANGELOG
+
+## Unreleased
+
+## 3.0.0
+See `UPGRADING.md` for specific help with breaking changes from 2.x to 3.0.0.
+
+- Adds support for Devise 4.
+- Relax dependencies to allow attr_encrypted 3.x.
+- Blocks the use of attr_encrypted 2.x. There was a significant vulnerability in the encryption implementation in attr_encrypted 2.x, and that version of the gem should not be used.
+
+## 2.2.0
+- Use 192 bits, not 1024, as a secret key length. RFC 4226 recommends a minimum length of 128 bits and a recommended length of 160 bits. Google Authenticator doesn't accept 160 bit keys.
+
+## 2.1.0
+- Return false if OTP value is nil, instead of an ROTP exception.
+
+## 2.0.1
+No user-facing changes.
+
+## 2.0.0
+See `UPGRADING.md` for specific help with breaking changes from 1.x to 2.0.0.
+
+- Replace `valid_otp?` method with `validate_and_consume_otp!`.
+- Disallow subsequent OTPs once validated via timesteps.
+
+## 1.1.0
+- Removes runtimez activemodel dependency.
+- Uses `Devise::Encryptor` instead of `Devise.bcrypt`, which is deprecated.
+- Bump `rotp` dependency to 2.x.
+
+## 1.0.2
+- Makes Railties the only requirement for Rails generators.
+- Explicitly check that the `otp_attempt` param is not nil in order to avoid 'ROTP only verifies strings' exceptions.
+- Adding warning about recoverable devise strategy and automatic `sign_in` after a password reset.
+- Loosen dependency version requirements for rotp, devise, and attr_encrypted.
+
+## 1.0.1
+- Add version requirements for dependencies.
+
+## 1.0.0
+- Initial release.
diff --git a/README.md b/README.md
index 7340ee5..38649eb 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ By [Tinfoil Security](http://tinfoilsecurity.com/)
 
 [![Build Status](https://travis-ci.org/tinfoil/devise-two-factor.svg?branch=master)](https://travis-ci.org/tinfoil/devise-two-factor)
 
-Devise-two-factor is a minimalist extension to Devise which offers support for two-factor authentication, through the [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) scheme. It:
+Devise-Two-Factor is a minimalist extension to Devise which offers support for two-factor authentication, through the [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) scheme. It:
 
 * Allows you to incorporate two-factor authentication into your existing models
 * Is opinionated about security, so you don't have to be
@@ -11,18 +11,24 @@ Devise-two-factor is a minimalist extension to Devise which offers support for t
 * Is extensible, and includes two-factor backup codes as an example of how plugins can be structured
 
 ## Example App
-An example Rails 4 application is provided in demo/. It showcases a minimal example of devise-two-factor in action, and can act as a reference for integrating the gem into your own application.
+An example Rails 4 application is provided in the `demo` directory. It showcases a minimal example of Devise-Two-Factor in action, and can act as a reference for integrating the gem into your own application.
 
-For the demo app to work, create an encryption key and store it as an environment variable. One way to do this is to create a file named `local_env.yml` in the application root. Set the value of 'ENCRYPTION_KEY' in the YML file. That value will be loaded into the application environment by `application.rb`.
+For the demo app to work, create an encryption key and store it as an environment variable. One way to do this is to create a file named `local_env.yml` in the application root. Set the value of `ENCRYPTION_KEY` in the YML file. That value will be loaded into the application environment by `application.rb`.
 
 ## Getting Started
-Devise-two-factor doesn't require much to get started, but there are a few prerequisites before you can start using it in your application.
+Devise-Two-Factor doesn't require much to get started, but there are a few prerequisites before you can start using it in your application.
 
 First, you'll need a Rails application setup with Devise. Visit the Devise [homepage](https://github.com/plataformatec/devise) for instructions.
 
-Next, since devise-two-factor encrypts its secrets before storing them in the database, you'll need to generate an encryption key, and store it in an environment variable of your choice. Set the encryption key in the model that uses devise:
+You can add Devise-Two-Factor to your Gemfile with:
 
+```ruby
+gem 'devise-two-factor'
 ```
+
+Next, since Devise-Two-Factor encrypts its secrets before storing them in the database, you'll need to generate an encryption key, and store it in an environment variable of your choice. Set the encryption key in the model that uses Devise:
+
+```ruby
   devise :two_factor_authenticatable,
          :otp_secret_encryption_key => ENV['YOUR_ENCRYPTION_KEY_HERE']
 
@@ -34,20 +40,21 @@ Finally, you can automate all of the required setup by simply running:
 rails generate devise_two_factor MODEL ENVIRONMENT_VARIABLE
 ```
 
-Where MODEL is the name of the model you wish to add two-factor functionality to, and ENVIRONMENT_VARIABLE is the name of the variable you're storing your encryption key in.
+Where `MODEL` is the name of the model you wish to add two-factor functionality to (for example `user`), and `ENVIRONMENT_VARIABLE` is the name of the variable you're storing your encryption key in.
 
 This generator will add a few columns to the specified model:
 
 * encrypted_otp_secret
 * encrypted_otp_secret_iv
 * encrypted_otp_secret_salt
+* consumed_timestep
 * otp_required_for_login
 
-It also adds the :two_factor_authenticatable directive to your model, and sets up your encryption key. If present, it will remove :database_authenticatable from the model, as the two strategies are incompatible. Lastly, the generator will add a Warden config block to your Devise initializer, which enables the strategies required for two-factor authenticatation.
+It also adds the :two_factor_authenticatable directive to your model, and sets up your encryption key. If present, it will remove :database_authenticatable from the model, as the two strategies are incompatible. Lastly, the generator will add a Warden config block to your Devise initializer, which enables the strategies required for two-factor authentication.
 
 If you're running Rails 3, or do not have strong parameters enabled, the generator will also setup the required mass-assignment security options in your model.
 
-If you're running Rails 4, you'll also need to whitelist `:otp_attempt` as a permitted parameter in Devise `:sign_in` controller. You can do this by adding the following to your `application_controller.rb`
+If you're running Rails 4, you'll also need to whitelist `:otp_attempt` as a permitted parameter in Devise `:sign_in` controller. You can do this by adding the following to your `application_controller.rb`:
 
 ```ruby
 before_action :configure_permitted_parameters, if: :devise_controller?
@@ -61,10 +68,24 @@ def configure_permitted_parameters
 end
 ```
 
-**After running the generator, verify that :database_authenticatable is not being loaded by your model. The generator will try to remove it, but if you have a non-standard Devise setup, this step may fail. Loading both :database_authenticatable and :two_factor_authenticatable in a model will allow users to bypass two-factor authenticatable due to the way Warden handles cascading strategies.**
+If you're running Devise 4.0.0 or above, you'll want to use `.permit` instead:
+
+```ruby
+before_action :configure_permitted_parameters, if: :devise_controller?
+
+...
+
+protected
+
+def configure_permitted_parameters
+  devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt])
+end
+```
+
+**After running the generator, verify that :database_authenticatable is not being loaded by your model. The generator will try to remove it, but if you have a non-standard Devise setup, this step may fail. Loading both :database_authenticatable and `:two_factor_authenticatable` in a model will allow users to bypass two-factor authenticatable due to the way Warden handles cascading strategies.**
 
 ## Designing Your Workflow
-Devise-two-factor only worries about the backend, leaving the details of the integration up to you. This means that you're responsible for building the UI that drives the gem. While there is an example Rails application included in the gem, it is important to remember that this gem is intentionally very open-ended, and you should build a user experience which fits your individual application.
+Devise-Two-Factor only worries about the backend, leaving the details of the integration up to you. This means that you're responsible for building the UI that drives the gem. While there is an example Rails application included in the gem, it is important to remember that this gem is intentionally very open-ended, and you should build a user experience which fits your individual application.
 
 There are two key workflows you'll have to think about:
 
@@ -75,7 +96,7 @@ We chose to keep things as simple as possible, and our implementation can be fou
 
 
 ### Logging In
-Logging in with two-factor authentication works extremely similarly to regular database authentication in Devise. The TwoFactorAuthenticatable strategy accepts three parameters:
+Logging in with two-factor authentication works extremely similarly to regular database authentication in Devise. The `TwoFactorAuthenticatable` strategy accepts three parameters:
 
 1. email
 2. password
@@ -84,9 +105,9 @@ Logging in with two-factor authentication works extremely similarly to regular d
 These parameters can be submitted to the standard Devise login route, and the strategy will handle the authentication of the user for you.
 
 ### Disabling Automatic Login After Password Resets
-If you use the Devise ```recoverable``` strategy, the default behavior after a password reset is to automatically authenticate the user and log them in. This is obviously a problem if a user has two-factor authentication enabled, as resetting the password would get around the 2FA requirement.
+If you use the Devise ```recoverable``` strategy, the default behavior after a password reset is to automatically authenticate the user and log them in. This is obviously a problem if a user has two-factor authentication enabled, as resetting the password would get around the two-factor requirement.
 
-Because of this, you need to set `sign_in_after_reset_password` to false (either globally in your Devise initializer or via `devise_for`)
+Because of this, you need to set `sign_in_after_reset_password` to `false` (either globally in your Devise initializer or via `devise_for`).
 
 ### Enabling Two-Factor Authentication
 Enabling two-factor authentication for a user is easy. For example, if my user model were named User, I could do the following:
@@ -107,26 +128,33 @@ If you instead to decide to send the one-time password to the user directly, suc
 current_user.current_otp
 ```
 
-The generated code will be valid for the duration specified by otp_allowed_drift.
+The generated code will be valid for the duration specified by `otp_allowed_drift`.
 
 However you decide to handle enrollment, there are a few important considerations to be made:
 
-* Whether you'll force the use of two-factor authentication, and if so, how you'll migrate existing users to system, and what your onboarding experience will look like
+* Whether you'll force the use of two-factor authentication, and if so, how you'll migrate existing users to system, and what your on-boarding experience will look like
 * If you authenticate using SMS, you'll want to verify the user's ownership of the phone, in much the same way you're probably verifying their email address
-* How you'll handle device revocation in the event that a user loses access to their device, or that device is rendered temporarily unavailable (This gem includes TwoFactorBackupable as an example extension meant to solve this problem)
+* How you'll handle device revocation in the event that a user loses access to their device, or that device is rendered temporarily unavailable (This gem includes `TwoFactorBackupable` as an example extension meant to solve this problem)
 
 It sounds like a lot of work, but most of these problems have been very elegantly solved by other people. We recommend taking a look at the excellent workflows used by Heroku and Google for inspiration.
 
+### Filtering sensitive parameters from the logs
+To prevent two-factor authentication codes from leaking if your application logs get breached, you'll want to filter sensitive parameters from the Rails logs. Add the following to `config/initializers/filter_parameter_logging.rb`:
+
+```ruby
+Rails.application.config.filter_parameters += [:otp_attempt]
+```
+
 ## Backup Codes
-Devise-two-factor is designed with extensibility in mind. One such extension, TwoFactorBackupable, is included and serves as a good example of how to extend this gem. This plugin allows you to add the ability to generate single-use backup codes for a user, which they may use to bypass two-factor authentication, in the event that they lose access to their device.
+Devise-Two-Factor is designed with extensibility in mind. One such extension, `TwoFactorBackupable`, is included and serves as a good example of how to extend this gem. This plugin allows you to add the ability to generate single-use backup codes for a user, which they may use to bypass two-factor authentication, in the event that they lose access to their device.
 
-To install it, you need to add the :two_factor_backupable directive to your model.
+To install it, you need to add the `:two_factor_backupable` directive to your model.
 
 ```ruby
 devise :two_factor_backupable
 ```
 
-You'll also be required to enable the :two_factor_backupable strategy, by adding the following line to your Warden config in your Devise initializer, substituting :user for the name of your Devise scope.
+You'll also be required to enable the `:two_factor_backupable` strategy, by adding the following line to your Warden config in your Devise initializer, substituting :user for the name of your Devise scope.
 
 ```ruby
 manager.default_strategies(:scope => :user).unshift :two_factor_backupable
@@ -137,6 +165,7 @@ The final installation step is dependent on your version of Rails. If you're not
 ```ruby
 class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration
   def change
+    # Change type from :string to :text if using MySQL database
     add_column :users, :otp_backup_codes, :string, array: true
   end
 end
@@ -160,13 +189,14 @@ devise :two_factor_backupable, otp_backup_code_length:     32,
 ```
 
 ### Help! I'm not using Rails 4.0!
-Don't worry! TwoFactorBackupable stores the backup codes as an array of strings in the database. In Rails 4.0 this is supported natively, but in earlier versions you can use a gem to emulate this behaviour: we recommend [activerecord-postgres-array](https://github.com/tlconnor/activerecord-postgres-array).
+Don't worry! `TwoFactorBackupable` stores the backup codes as an array of strings in the database. In Rails 4.0 this is supported natively, but in earlier versions you can use a gem to emulate this behavior: we recommend [activerecord-postgres-array](https://github.com/tlconnor/activerecord-postgres-array).
 
 You'll then simply have to create a migration to add an array named `otp_backup_codes` to your model. If you use the above gem, this migration might look like:
 
 ```ruby
 class AddTwoFactorBackupCodesToUsers < ActiveRecord::Migration
   def change
+    # Change type from :string_array to :text_array if using MySQL database
     add_column :users, :otp_backup_codes, :string_array
   end
 end
@@ -175,7 +205,7 @@ end
 Now just continue with the setup in the previous section, skipping the generator step.
 
 ## Testing
-Devise-two-factor includes shared-examples for both TwoFactorAuthenticatable and TwoFactorBackupable. Adding the following two lines to the specs for your two-factor enabled models will allow you to test your models for two-factor functionality:
+Devise-Two-Factor includes shared-examples for both `TwoFactorAuthenticatable` and `TwoFactorBackupable`. Adding the following two lines to the specs for your two-factor enabled models will allow you to test your models for two-factor functionality:
 
 ```ruby
 require 'devise_two_factor/spec_helpers'
diff --git a/UPGRADING.md b/UPGRADING.md
index a5c9ea0..74f3e53 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -1,3 +1,23 @@
+# Guide to upgrading from 2.x to 3.x
+
+Pull request #76 allows for compatibility with `attr_encrypted` 3.0, which should be used due to a security vulnerability discovered in 2.0.
+
+Pull request #73 allows for compatibility with `attr_encrypted` 2.0. This version changes many of the defaults which must be taken into account to avoid corrupted OTP secrets on your model.
+
+Due to new security practices in `attr_encrypted` an encryption key with insufficient length will cause an error. If you run into this, you may set `insecure_mode: true` in the `attr_encrypted` options.
+
+You should initially add compatibility by specifying the `attr_encrypted` attribute in your model (`User` for these examples) with the old default encryption algorithm before invoking `devise :two_factor_authenticatable`:
+```ruby
+class User < ActiveRecord::Base
+  attr_encrypted :otp_secret,
+    :key       => self.otp_secret_encryption_key,
+    :mode      => :per_attribute_iv_and_salt,
+    :algorithm => 'aes-256-cbc'
+
+  devise :two_factor_authenticatable,
+         :otp_secret_encryption_key => ENV['DEVISE_TWO_FACTOR_ENCRYPTION_KEY']
+```
+
 # Guide to upgrading from 1.x to 2.x
 
 Pull request #43 added a new field to protect against "shoulder-surfing" attacks. If upgrading, you'll need to add the `:consumed_timestep` column to your `Users` model.
@@ -9,3 +29,5 @@ class AddConsumedTimestepToUsers < ActiveRecord::Migration
   end
 end
 ```
+
+All uses of the `valid_otp?` method should be switched to `validate_and_consume_otp!`
diff --git a/checksums.yaml.gz.sig b/checksums.yaml.gz.sig
new file mode 100644
index 0000000..524b939
Binary files /dev/null and b/checksums.yaml.gz.sig differ
diff --git a/data.tar.gz.sig b/data.tar.gz.sig
new file mode 100644
index 0000000..439fdb5
Binary files /dev/null and b/data.tar.gz.sig differ
diff --git a/demo/.gitignore b/demo/.gitignore
deleted file mode 100644
index 6a502e9..0000000
--- a/demo/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# See https://help.github.com/articles/ignoring-files for more about ignoring files.
-#
-# If you find yourself ignoring temporary files generated by your text editor
-# or operating system, you probably want to add a global ignore instead:
-#   git config --global core.excludesfile '~/.gitignore_global'
-
-# Ignore bundler config.
-/.bundle
-
-# Ignore the default SQLite database.
-/db/*.sqlite3
-/db/*.sqlite3-journal
-
-# Ignore all logfiles and tempfiles.
-/log/*.log
-/tmp
diff --git a/demo/Gemfile b/demo/Gemfile
deleted file mode 100644
index db80dc6..0000000
--- a/demo/Gemfile
+++ /dev/null
@@ -1,40 +0,0 @@
-source 'https://rubygems.org'
-
-
-# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
-gem 'rails', '4.1.0'
-# Use postgresql as the database for Active Record
-gem 'pg'
-# Use SCSS for stylesheets
-gem 'sass-rails', '~> 4.0.3'
-# Use Uglifier as compressor for JavaScript assets
-gem 'uglifier', '>= 1.3.0'
-# Use CoffeeScript for .js.coffee assets and views
-gem 'coffee-rails', '~> 4.0.0'
-# See https://github.com/sstephenson/execjs#readme for more supported runtimes
-# gem 'therubyracer',  platforms: :ruby
-
-# Use jquery as the JavaScript library
-gem 'jquery-rails'
-# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
-gem 'turbolinks'
-# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
-gem 'jbuilder', '~> 2.0'
-# bundle exec rake doc:rails generates the API under doc/api.
-gem 'sdoc', '~> 0.4.0',          group: :doc
-
-gem 'devise'
-gem 'devise-two-factor', :path => '../'
-gem 'rqrcode-rails3'
-
-# Use ActiveModel has_secure_password
-# gem 'bcrypt', '~> 3.1.7'
-
-# Use unicorn as the app server
-# gem 'unicorn'
-
-# Use Capistrano for deployment
-# gem 'capistrano-rails', group: :development
-
-# Use debugger
-# gem 'debugger', group: [:development, :test]
diff --git a/demo/README.rdoc b/demo/README.rdoc
deleted file mode 100644
index dd4e97e..0000000
--- a/demo/README.rdoc
+++ /dev/null
@@ -1,28 +0,0 @@
-== README
-
-This README would normally document whatever steps are necessary to get the
-application up and running.
-
-Things you may want to cover:
-
-* Ruby version
-
-* System dependencies
-
-* Configuration
-
-* Database creation
-
-* Database initialization
-
-* How to run the test suite
-
-* Services (job queues, cache servers, search engines, etc.)
-
-* Deployment instructions
-
-* ...
-
-
-Please feel free to use a different markup language if you do not plan to run
-<tt>rake doc:app</tt>.
diff --git a/demo/Rakefile b/demo/Rakefile
deleted file mode 100644
index ba6b733..0000000
--- a/demo/Rakefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# Add your own tasks in files placed in lib/tasks ending in .rake,
-# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-
-require File.expand_path('../config/application', __FILE__)
-
-Rails.application.load_tasks
diff --git a/demo/app/assets/images/.keep b/demo/app/assets/images/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/app/assets/javascripts/application.js b/demo/app/assets/javascripts/application.js
deleted file mode 100644
index d6925fa..0000000
--- a/demo/app/assets/javascripts/application.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// This is a manifest file that'll be compiled into application.js, which will include all the files
-// listed below.
-//
-// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
-// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
-//
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// compiled file.
-//
-// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
-// about supported directives.
-//
-//= require jquery
-//= require jquery_ujs
-//= require turbolinks
-//= require_tree .
diff --git a/demo/app/assets/javascripts/home.js.coffee b/demo/app/assets/javascripts/home.js.coffee
deleted file mode 100644
index 24f83d1..0000000
--- a/demo/app/assets/javascripts/home.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/demo/app/assets/stylesheets/application.css b/demo/app/assets/stylesheets/application.css
deleted file mode 100644
index a443db3..0000000
--- a/demo/app/assets/stylesheets/application.css
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * This is a manifest file that'll be compiled into application.css, which will include all the files
- * listed below.
- *
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
- * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
- *
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
- * compiled file so the styles you add here take precedence over styles defined in any styles
- * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
- * file per style scope.
- *
- *= require_tree .
- *= require_self
- */
diff --git a/demo/app/assets/stylesheets/home.css.scss b/demo/app/assets/stylesheets/home.css.scss
deleted file mode 100644
index f0ddc68..0000000
--- a/demo/app/assets/stylesheets/home.css.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-// Place all the styles related to the home controller here.
-// They will automatically be included in application.css.
-// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/demo/app/controllers/application_controller.rb b/demo/app/controllers/application_controller.rb
deleted file mode 100644
index 6254b6d..0000000
--- a/demo/app/controllers/application_controller.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class ApplicationController < ActionController::Base
-  # Prevent CSRF attacks by raising an exception.
-  # For APIs, you may want to use :null_session instead.
-  protect_from_forgery with: :exception
-
-  before_action :configure_permitted_parameters, if: :devise_controller?
-
-  protected
-
-  def configure_permitted_parameters
-    devise_parameter_sanitizer.for(:sign_in) << :otp_attempt
-  end
-end
diff --git a/demo/app/controllers/concerns/.keep b/demo/app/controllers/concerns/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/app/controllers/home_controller.rb b/demo/app/controllers/home_controller.rb
deleted file mode 100644
index 95f2992..0000000
--- a/demo/app/controllers/home_controller.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-class HomeController < ApplicationController
-  def index
-  end
-end
diff --git a/demo/app/controllers/users_controller.rb b/demo/app/controllers/users_controller.rb
deleted file mode 100644
index 951bf6a..0000000
--- a/demo/app/controllers/users_controller.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class UsersController < ApplicationController
-  def disable_otp
-    current_user.otp_required_for_login = false
-    current_user.save!
-    redirect_to home_index_path
-  end
-
-  def enable_otp
-    current_user.otp_secret = User.generate_otp_secret
-    current_user.otp_required_for_login = true
-    current_user.save!
-    redirect_to home_index_path
-  end
-end
diff --git a/demo/app/helpers/application_helper.rb b/demo/app/helpers/application_helper.rb
deleted file mode 100644
index de6be79..0000000
--- a/demo/app/helpers/application_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module ApplicationHelper
-end
diff --git a/demo/app/helpers/home_helper.rb b/demo/app/helpers/home_helper.rb
deleted file mode 100644
index 23de56a..0000000
--- a/demo/app/helpers/home_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module HomeHelper
-end
diff --git a/demo/app/mailers/.keep b/demo/app/mailers/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/app/models/.keep b/demo/app/models/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/app/models/concerns/.keep b/demo/app/models/concerns/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/app/models/user.rb b/demo/app/models/user.rb
deleted file mode 100644
index 064db6a..0000000
--- a/demo/app/models/user.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class User < ActiveRecord::Base
-  devise :two_factor_authenticatable,
-         :otp_secret_encryption_key => ENV['ENCRYPTION_KEY']  # Set a unique encryption key for your app.
-                                                                        # Store your key as an ENV variable and
-                                                                        # remember to add it to .gitignore 
-                                                                        # if you plan to share your code publicly.
-
-  devise :registerable,
-         :recoverable, :rememberable, :trackable, :validatable
-end
diff --git a/demo/app/views/devise/confirmations/new.html.erb b/demo/app/views/devise/confirmations/new.html.erb
deleted file mode 100644
index 65ba288..0000000
--- a/demo/app/views/devise/confirmations/new.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-<h2>Resend confirmation instructions</h2>
-
-<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
-  <%= devise_error_messages! %>
-
-  <div><%= f.label :email %><br />
-  <%= f.email_field :email, autofocus: true %></div>
-
-  <div><%= f.submit "Resend confirmation instructions" %></div>
-<% end %>
-
-<%= render "devise/shared/links" %>
diff --git a/demo/app/views/devise/mailer/confirmation_instructions.html.erb b/demo/app/views/devise/mailer/confirmation_instructions.html.erb
deleted file mode 100644
index dc55f64..0000000
--- a/demo/app/views/devise/mailer/confirmation_instructions.html.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-<p>Welcome <%= @email %>!</p>
-
-<p>You can confirm your account email through the link below:</p>
-
-<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
diff --git a/demo/app/views/devise/mailer/reset_password_instructions.html.erb b/demo/app/views/devise/mailer/reset_password_instructions.html.erb
deleted file mode 100644
index f667dc1..0000000
--- a/demo/app/views/devise/mailer/reset_password_instructions.html.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-<p>Hello <%= @resource.email %>!</p>
-
-<p>Someone has requested a link to change your password. You can do this through the link below.</p>
-
-<p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
-
-<p>If you didn't request this, please ignore this email.</p>
-<p>Your password won't change until you access the link above and create a new one.</p>
diff --git a/demo/app/views/devise/mailer/unlock_instructions.html.erb b/demo/app/views/devise/mailer/unlock_instructions.html.erb
deleted file mode 100644
index 41e148b..0000000
--- a/demo/app/views/devise/mailer/unlock_instructions.html.erb
+++ /dev/null
@@ -1,7 +0,0 @@
-<p>Hello <%= @resource.email %>!</p>
-
-<p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
-
-<p>Click the link below to unlock your account:</p>
-
-<p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>
diff --git a/demo/app/views/devise/passwords/edit.html.erb b/demo/app/views/devise/passwords/edit.html.erb
deleted file mode 100644
index 5535098..0000000
--- a/demo/app/views/devise/passwords/edit.html.erb
+++ /dev/null
@@ -1,16 +0,0 @@
-<h2>Change your password</h2>
-
-<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
-  <%= devise_error_messages! %>
-  <%= f.hidden_field :reset_password_token %>
-
-  <div><%= f.label :password, "New password" %><br />
-    <%= f.password_field :password, autofocus: true, autocomplete: "off" %></div>
-
-  <div><%= f.label :password_confirmation, "Confirm new password" %><br />
-    <%= f.password_field :password_confirmation, autocomplete: "off" %></div>
-
-  <div><%= f.submit "Change my password" %></div>
-<% end %>
-
-<%= render "devise/shared/links" %>
diff --git a/demo/app/views/devise/passwords/new.html.erb b/demo/app/views/devise/passwords/new.html.erb
deleted file mode 100644
index ea1d46e..0000000
--- a/demo/app/views/devise/passwords/new.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-<h2>Forgot your password?</h2>
-
-<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
-  <%= devise_error_messages! %>
-
-  <div><%= f.label :email %><br />
-  <%= f.email_field :email, autofocus: true %></div>
-
-  <div><%= f.submit "Send me reset password instructions" %></div>
-<% end %>
-
-<%= render "devise/shared/links" %>
diff --git a/demo/app/views/devise/registrations/edit.html.erb b/demo/app/views/devise/registrations/edit.html.erb
deleted file mode 100644
index 808d62c..0000000
--- a/demo/app/views/devise/registrations/edit.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-<h2>Edit <%= resource_name.to_s.humanize %></h2>
-
-<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
-  <%= devise_error_messages! %>
-
-  <div><%= f.label :email %><br />
-  <%= f.email_field :email, autofocus: true %></div>
-
-  <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
-    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
-  <% end %>
-
-  <div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
-    <%= f.password_field :password, autocomplete: "off" %></div>
-
-  <div><%= f.label :password_confirmation %><br />
-    <%= f.password_field :password_confirmation, autocomplete: "off" %></div>
-
-  <div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
-    <%= f.password_field :current_password, autocomplete: "off" %></div>
-
-  <div><%= f.submit "Update" %></div>
-<% end %>
-
-<h3>Cancel my account</h3>
-
-<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
-
-<%= link_to "Back", :back %>
diff --git a/demo/app/views/devise/registrations/new.html.erb b/demo/app/views/devise/registrations/new.html.erb
deleted file mode 100644
index 234de91..0000000
--- a/demo/app/views/devise/registrations/new.html.erb
+++ /dev/null
@@ -1,18 +0,0 @@
-<h2>Sign up</h2>
-
-<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
-  <%= devise_error_messages! %>
-
-  <div><%= f.label :email %><br />
-  <%= f.email_field :email, autofocus: true %></div>
-
-  <div><%= f.label :password %><br />
-    <%= f.password_field :password, autocomplete: "off" %></div>
-
-  <div><%= f.label :password_confirmation %><br />
-    <%= f.password_field :password_confirmation, autocomplete: "off" %></div>
-
-  <div><%= f.submit "Sign up" %></div>
-<% end %>
-
-<%= render "devise/shared/links" %>
diff --git a/demo/app/views/devise/sessions/new.html.erb b/demo/app/views/devise/sessions/new.html.erb
deleted file mode 100644
index 42ae056..0000000
--- a/demo/app/views/devise/sessions/new.html.erb
+++ /dev/null
@@ -1,20 +0,0 @@
-<h2>Sign in</h2>
-
-<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
-  <div><%= f.label :email %><br />
-  <%= f.email_field :email, autofocus: true %></div>
-
-  <div><%= f.label :password %><br />
-    <%= f.password_field :password, autocomplete: "off" %></div>
-
-  <div><%= f.label :otp_attempt %><br />
-    <%= f.text_field :otp_attempt %> </div>
-
-  <% if devise_mapping.rememberable? -%>
-    <div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
-  <% end -%>
-
-  <div><%= f.submit "Sign in" %></div>
-<% end %>
-
-<%= render "devise/shared/links" %>
diff --git a/demo/app/views/devise/shared/_links.erb b/demo/app/views/devise/shared/_links.erb
deleted file mode 100644
index d84bdde..0000000
--- a/demo/app/views/devise/shared/_links.erb
+++ /dev/null
@@ -1,25 +0,0 @@
-<%- if controller_name != 'sessions' %>
-  <%= link_to "Sign in", new_session_path(resource_name) %><br />
-<% end -%>
-
-<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
-  <%= link_to "Sign up", new_registration_path(resource_name) %><br />
-<% end -%>
-
-<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
-  <%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
-<% end -%>
-
-<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
-  <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
-<% end -%>
-
-<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
-  <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
-<% end -%>
-
-<%- if devise_mapping.omniauthable? %>
-  <%- resource_class.omniauth_providers.each do |provider| %>
-    <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %><br />
-  <% end -%>
-<% end -%>
diff --git a/demo/app/views/devise/unlocks/new.html.erb b/demo/app/views/devise/unlocks/new.html.erb
deleted file mode 100644
index 6fb5612..0000000
--- a/demo/app/views/devise/unlocks/new.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-<h2>Resend unlock instructions</h2>
-
-<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
-  <%= devise_error_messages! %>
-
-  <div><%= f.label :email %><br />
-  <%= f.email_field :email, autofocus: true %></div>
-
-  <div><%= f.submit "Resend unlock instructions" %></div>
-<% end %>
-
-<%= render "devise/shared/links" %>
diff --git a/demo/app/views/home/index.html.erb b/demo/app/views/home/index.html.erb
deleted file mode 100644
index 4271863..0000000
--- a/demo/app/views/home/index.html.erb
+++ /dev/null
@@ -1,20 +0,0 @@
-<% if !current_user %>
-  <%= link_to "Sign up", new_user_registration_path %>
-  <%= link_to "Login", new_user_session_path %>
-<% end %>
-
-<% if current_user %>
-  <% if !current_user.otp_required_for_login %>
-    <%= button_to "Enable 2FA", users_enable_otp_path, :method => :post %>
-  <% end %>
-
-  <% if current_user.otp_required_for_login %>
-    <%= button_to "Disable 2FA", users_disable_otp_path, :method => :post %>
-    <%= raw RQRCode::render_qrcode(current_user.otp_provisioning_uri(current_user.email, issuer: "Devise-Two-Factor-Demo"),
-                                   :svg,
-                                   :level => :l,
-                                   :unit => 2) %>
-    <br>
-  <% end %>
-  <%= link_to "Log out", destroy_user_session_path, :method => :delete %>
-<% end %>
diff --git a/demo/app/views/layouts/application.html.erb b/demo/app/views/layouts/application.html.erb
deleted file mode 100644
index 4b64d8d..0000000
--- a/demo/app/views/layouts/application.html.erb
+++ /dev/null
@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>DeviseTwoFactorDemo</title>
-  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
-  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
-  <%= csrf_meta_tags %>
-</head>
-<body>
-
-<%= yield %>
-
-</body>
-</html>
diff --git a/demo/bin/bundle b/demo/bin/bundle
deleted file mode 100755
index 66e9889..0000000
--- a/demo/bin/bundle
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-load Gem.bin_path('bundler', 'bundle')
diff --git a/demo/bin/rails b/demo/bin/rails
deleted file mode 100755
index 728cd85..0000000
--- a/demo/bin/rails
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env ruby
-APP_PATH = File.expand_path('../../config/application',  __FILE__)
-require_relative '../config/boot'
-require 'rails/commands'
diff --git a/demo/bin/rake b/demo/bin/rake
deleted file mode 100755
index 1724048..0000000
--- a/demo/bin/rake
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env ruby
-require_relative '../config/boot'
-require 'rake'
-Rake.application.run
diff --git a/demo/config.ru b/demo/config.ru
deleted file mode 100644
index 5bc2a61..0000000
--- a/demo/config.ru
+++ /dev/null
@@ -1,4 +0,0 @@
-# This file is used by Rack-based servers to start the application.
-
-require ::File.expand_path('../config/environment',  __FILE__)
-run Rails.application
diff --git a/demo/config/application.rb b/demo/config/application.rb
deleted file mode 100644
index 9d470b1..0000000
--- a/demo/config/application.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require File.expand_path('../boot', __FILE__)
-
-require 'rails/all'
-
-# Require the gems listed in Gemfile, including any gems
-# you've limited to :test, :development, or :production.
-Bundler.require(*Rails.groups)
-
-module DeviseTwoFactorDemo
-  class Application < Rails::Application
-    # Settings in config/environments/* take precedence over those specified here.
-    # Application configuration should go into files in config/initializers
-    # -- all .rb files in that directory are automatically loaded.
-
-    env_file = File.join(Rails.root, 'config', 'local_env.yml')
-    YAML.load(File.open(env_file)).each do |key, value| 
-      ENV[key.to_s] = value 
-    end if File.exists?(env_file)
-
-    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
-    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
-    # config.time_zone = 'Central Time (US & Canada)'
-
-    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
-    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
-    # config.i18n.default_locale = :de
-  end
-end
diff --git a/demo/config/boot.rb b/demo/config/boot.rb
deleted file mode 100644
index 5e5f0c1..0000000
--- a/demo/config/boot.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# Set up gems listed in the Gemfile.
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-
-require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
diff --git a/demo/config/database.yml b/demo/config/database.yml
deleted file mode 100644
index 15bf679..0000000
--- a/demo/config/database.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-# PostgreSQL. Versions 8.2 and up are supported.
-#
-# Install the pg driver:
-#   gem install pg
-# On OS X with Homebrew:
-#   gem install pg -- --with-pg-config=/usr/local/bin/pg_config
-# On OS X with MacPorts:
-#   gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
-# On Windows:
-#   gem install pg
-#       Choose the win32 build.
-#       Install PostgreSQL and put its /bin directory on your path.
-#
-# Configure Using Gemfile
-# gem 'pg'
-#
-default: &default
-  adapter: postgresql
-  encoding: unicode
-  # For details on connection pooling, see rails configuration guide
-  # http://guides.rubyonrails.org/configuring.html#database-pooling
-  pool: 5
-
-development:
-  <<: *default
-  database: DeviseTwoFactorDemo_development
-
-  # The specified database role being used to connect to postgres.
-  # To create additional roles in postgres see `$ createuser --help`.
-  # When left blank, postgres will use the default role. This is
-  # the same name as the operating system user that initialized the database.
-  #username: DeviseTwoFactorDemo
-
-  # The password associated with the postgres role (username).
-  #password:
-
-  # Connect on a TCP socket. Omitted by default since the client uses a
-  # domain socket that doesn't need configuration. Windows does not have
-  # domain sockets, so uncomment these lines.
-  #host: localhost
-
-  # The TCP port the server listens on. Defaults to 5432.
-  # If your server runs on a different port number, change accordingly.
-  #port: 5432
-
-  # Schema search path. The server defaults to $user,public
-  #schema_search_path: myapp,sharedapp,public
-
-  # Minimum log levels, in increasing order:
-  #   debug5, debug4, debug3, debug2, debug1,
-  #   log, notice, warning, error, fatal, and panic
-  # Defaults to warning.
-  #min_messages: notice
-
-# Warning: The database defined as "test" will be erased and
-# re-generated from your development database when you run "rake".
-# Do not set this db to the same as development or production.
-test:
-  <<: *default
-  database: DeviseTwoFactorDemo_test
-
-# As with config/secrets.yml, you never want to store sensitive information,
-# like your database password, in your source code. If your source code is
-# ever seen by anyone, they now have access to your database.
-#
-# Instead, provide the password as a unix environment variable when you boot
-# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
-# for a full rundown on how to provide these environment variables in a
-# production deployment.
-#
-# On Heroku and other platform providers, you may have a full connection URL
-# available as an environment variable. For example:
-#
-#   DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
-#
-# You can use this database configuration with:
-#
-#   production:
-#     url: <%= ENV['DATABASE_URL'] %>
-#
-production:
-  <<: *default
-  database: DeviseTwoFactorDemo_production
-  username: DeviseTwoFactorDemo
-  password: <%= ENV['DEVISETWOFACTORDEMO_DATABASE_PASSWORD'] %>
diff --git a/demo/config/environment.rb b/demo/config/environment.rb
deleted file mode 100644
index ee8d90d..0000000
--- a/demo/config/environment.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# Load the Rails application.
-require File.expand_path('../application', __FILE__)
-
-# Initialize the Rails application.
-Rails.application.initialize!
diff --git a/demo/config/environments/development.rb b/demo/config/environments/development.rb
deleted file mode 100644
index ddf0e90..0000000
--- a/demo/config/environments/development.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-Rails.application.configure do
-  # Settings specified here will take precedence over those in config/application.rb.
-
-  # In the development environment your application's code is reloaded on
-  # every request. This slows down response time but is perfect for development
-  # since you don't have to restart the web server when you make code changes.
-  config.cache_classes = false
-
-  # Do not eager load code on boot.
-  config.eager_load = false
-
-  # Show full error reports and disable caching.
-  config.consider_all_requests_local       = true
-  config.action_controller.perform_caching = false
-
-  # Don't care if the mailer can't send.
-  config.action_mailer.raise_delivery_errors = false
-
-  # Print deprecation notices to the Rails logger.
-  config.active_support.deprecation = :log
-
-  # Raise an error on page load if there are pending migrations.
-  config.active_record.migration_error = :page_load
-
-  # Debug mode disables concatenation and preprocessing of assets.
-  # This option may cause significant delays in view rendering with a large
-  # number of complex assets.
-  config.assets.debug = true
-
-  # Adds additional error checking when serving assets at runtime.
-  # Checks for improperly declared sprockets dependencies.
-  # Raises helpful error messages.
-  config.assets.raise_runtime_errors = true
-
-  # Raises error for missing translations
-  # config.action_view.raise_on_missing_translations = true
-end
diff --git a/demo/config/environments/production.rb b/demo/config/environments/production.rb
deleted file mode 100644
index 47d3553..0000000
--- a/demo/config/environments/production.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-Rails.application.configure do
-  # Settings specified here will take precedence over those in config/application.rb.
-
-  # Code is not reloaded between requests.
-  config.cache_classes = true
-
-  # Eager load code on boot. This eager loads most of Rails and
-  # your application in memory, allowing both threaded web servers
-  # and those relying on copy on write to perform better.
-  # Rake tasks automatically ignore this option for performance.
-  config.eager_load = true
-
-  # Full error reports are disabled and caching is turned on.
-  config.consider_all_requests_local       = false
-  config.action_controller.perform_caching = true
-
-  # Enable Rack::Cache to put a simple HTTP cache in front of your application
-  # Add `rack-cache` to your Gemfile before enabling this.
-  # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
-  # config.action_dispatch.rack_cache = true
-
-  # Disable Rails's static asset server (Apache or nginx will already do this).
-  config.serve_static_assets = false
-
-  # Compress JavaScripts and CSS.
-  config.assets.js_compressor = :uglifier
-  # config.assets.css_compressor = :sass
-
-  # Do not fallback to assets pipeline if a precompiled asset is missed.
-  config.assets.compile = false
-
-  # Generate digests for assets URLs.
-  config.assets.digest = true
-
-  # Version of your assets, change this if you want to expire all your assets.
-  config.assets.version = '1.0'
-
-  # Specifies the header that your server uses for sending files.
-  # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
-  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
-
-  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
-  # config.force_ssl = true
-
-  # Set to :debug to see everything in the log.
-  config.log_level = :info
-
-  # Prepend all log lines with the following tags.
-  # config.log_tags = [ :subdomain, :uuid ]
-
-  # Use a different logger for distributed setups.
-  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
-
-  # Use a different cache store in production.
-  # config.cache_store = :mem_cache_store
-
-  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
-  # config.action_controller.asset_host = "http://assets.example.com"
-
-  # Precompile additional assets.
-  # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
-  # config.assets.precompile += %w( search.js )
-
-  # Ignore bad email addresses and do not raise email delivery errors.
-  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
-  # config.action_mailer.raise_delivery_errors = false
-
-  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
-  # the I18n.default_locale when a translation cannot be found).
-  config.i18n.fallbacks = true
-
-  # Send deprecation notices to registered listeners.
-  config.active_support.deprecation = :notify
-
-  # Disable automatic flushing of the log to improve performance.
-  # config.autoflush_log = false
-
-  # Use default logging formatter so that PID and timestamp are not suppressed.
-  config.log_formatter = ::Logger::Formatter.new
-
-  # Do not dump schema after migrations.
-  config.active_record.dump_schema_after_migration = false
-end
diff --git a/demo/config/environments/test.rb b/demo/config/environments/test.rb
deleted file mode 100644
index 053f5b6..0000000
--- a/demo/config/environments/test.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-Rails.application.configure do
-  # Settings specified here will take precedence over those in config/application.rb.
-
-  # The test environment is used exclusively to run your application's
-  # test suite. You never need to work with it otherwise. Remember that
-  # your test database is "scratch space" for the test suite and is wiped
-  # and recreated between test runs. Don't rely on the data there!
-  config.cache_classes = true
-
-  # Do not eager load code on boot. This avoids loading your whole application
-  # just for the purpose of running a single test. If you are using a tool that
-  # preloads Rails for running tests, you may have to set it to true.
-  config.eager_load = false
-
-  # Configure static asset server for tests with Cache-Control for performance.
-  config.serve_static_assets  = true
-  config.static_cache_control = 'public, max-age=3600'
-
-  # Show full error reports and disable caching.
-  config.consider_all_requests_local       = true
-  config.action_controller.perform_caching = false
-
-  # Raise exceptions instead of rendering exception templates.
-  config.action_dispatch.show_exceptions = false
-
-  # Disable request forgery protection in test environment.
-  config.action_controller.allow_forgery_protection = false
-
-  # Tell Action Mailer not to deliver emails to the real world.
-  # The :test delivery method accumulates sent emails in the
-  # ActionMailer::Base.deliveries array.
-  config.action_mailer.delivery_method = :test
-
-  # Print deprecation notices to the stderr.
-  config.active_support.deprecation = :stderr
-
-  # Raises error for missing translations
-  # config.action_view.raise_on_missing_translations = true
-end
diff --git a/demo/config/initializers/backtrace_silencers.rb b/demo/config/initializers/backtrace_silencers.rb
deleted file mode 100644
index 59385cd..0000000
--- a/demo/config/initializers/backtrace_silencers.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
-# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
-
-# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
-# Rails.backtrace_cleaner.remove_silencers!
diff --git a/demo/config/initializers/cookies_serializer.rb b/demo/config/initializers/cookies_serializer.rb
deleted file mode 100644
index 7a06a89..0000000
--- a/demo/config/initializers/cookies_serializer.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-Rails.application.config.action_dispatch.cookies_serializer = :json
\ No newline at end of file
diff --git a/demo/config/initializers/devise.rb b/demo/config/initializers/devise.rb
deleted file mode 100644
index 43543eb..0000000
--- a/demo/config/initializers/devise.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-Devise.setup do |config|
-  config.warden do |manager|
-    manager.default_strategies(:scope => :user).unshift :two_factor_authenticatable
-  end
-
-  config.mailer_sender = 'please-change-me-at-config-initializers-devise at example.com'
-
-  require 'devise/orm/active_record'
-
-  config.case_insensitive_keys = [ :email ]
-  config.strip_whitespace_keys = [ :email ]
-  config.skip_session_storage = [:http_auth]
-  config.stretches = Rails.env.test? ? 1 : 10
-  config.reconfirmable = true
-  config.password_length = 8..128
-  config.reset_password_within = 6.hours
-  config.sign_out_via = :delete
-end
diff --git a/demo/config/initializers/filter_parameter_logging.rb b/demo/config/initializers/filter_parameter_logging.rb
deleted file mode 100644
index 4a994e1..0000000
--- a/demo/config/initializers/filter_parameter_logging.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Configure sensitive parameters which will be filtered from the log file.
-Rails.application.config.filter_parameters += [:password]
diff --git a/demo/config/initializers/inflections.rb b/demo/config/initializers/inflections.rb
deleted file mode 100644
index ac033bf..0000000
--- a/demo/config/initializers/inflections.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Add new inflection rules using the following format. Inflections
-# are locale specific, and you may define rules for as many different
-# locales as you wish. All of these examples are active by default:
-# ActiveSupport::Inflector.inflections(:en) do |inflect|
-#   inflect.plural /^(ox)$/i, '\1en'
-#   inflect.singular /^(ox)en/i, '\1'
-#   inflect.irregular 'person', 'people'
-#   inflect.uncountable %w( fish sheep )
-# end
-
-# These inflection rules are supported but not enabled by default:
-# ActiveSupport::Inflector.inflections(:en) do |inflect|
-#   inflect.acronym 'RESTful'
-# end
diff --git a/demo/config/initializers/mime_types.rb b/demo/config/initializers/mime_types.rb
deleted file mode 100644
index dc18996..0000000
--- a/demo/config/initializers/mime_types.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Add new mime types for use in respond_to blocks:
-# Mime::Type.register "text/richtext", :rtf
diff --git a/demo/config/initializers/session_store.rb b/demo/config/initializers/session_store.rb
deleted file mode 100644
index 80fcd5c..0000000
--- a/demo/config/initializers/session_store.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-Rails.application.config.session_store :cookie_store, key: '_DeviseTwoFactorDemo_session'
diff --git a/demo/config/initializers/wrap_parameters.rb b/demo/config/initializers/wrap_parameters.rb
deleted file mode 100644
index 33725e9..0000000
--- a/demo/config/initializers/wrap_parameters.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# This file contains settings for ActionController::ParamsWrapper which
-# is enabled by default.
-
-# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
-ActiveSupport.on_load(:action_controller) do
-  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
-end
-
-# To enable root element in JSON for ActiveRecord objects.
-# ActiveSupport.on_load(:active_record) do
-#  self.include_root_in_json = true
-# end
diff --git a/demo/config/locales/devise.en.yml b/demo/config/locales/devise.en.yml
deleted file mode 100644
index abccdb0..0000000
--- a/demo/config/locales/devise.en.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-# Additional translations at https://github.com/plataformatec/devise/wiki/I18n
-
-en:
-  devise:
-    confirmations:
-      confirmed: "Your account was successfully confirmed."
-      send_instructions: "You will receive an email with instructions about how to confirm your account in a few minutes."
-      send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes."
-    failure:
-      already_authenticated: "You are already signed in."
-      inactive: "Your account is not activated yet."
-      invalid: "Invalid email or password."
-      locked: "Your account is locked."
-      last_attempt: "You have one more attempt before your account will be locked."
-      not_found_in_database: "Invalid email or password."
-      timeout: "Your session expired. Please sign in again to continue."
-      unauthenticated: "You need to sign in or sign up before continuing."
-      unconfirmed: "You have to confirm your account before continuing."
-    mailer:
-      confirmation_instructions:
-        subject: "Confirmation instructions"
-      reset_password_instructions:
-        subject: "Reset password instructions"
-      unlock_instructions:
-        subject: "Unlock Instructions"
-    omniauth_callbacks:
-      failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
-      success: "Successfully authenticated from %{kind} account."
-    passwords:
-      no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
-      send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
-      send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
-      updated: "Your password was changed successfully. You are now signed in."
-      updated_not_active: "Your password was changed successfully."
-    registrations:
-      destroyed: "Bye! Your account was successfully cancelled. We hope to see you again soon."
-      signed_up: "Welcome! You have signed up successfully."
-      signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
-      signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
-      signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please open the link to activate your account."
-      update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address."
-      updated: "You updated your account successfully."
-    sessions:
-      signed_in: "Signed in successfully."
-      signed_out: "Signed out successfully."
-    unlocks:
-      send_instructions: "You will receive an email with instructions about how to unlock your account in a few minutes."
-      send_paranoid_instructions: "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes."
-      unlocked: "Your account has been unlocked successfully. Please sign in to continue."
-  errors:
-    messages:
-      already_confirmed: "was already confirmed, please try signing in"
-      confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
-      expired: "has expired, please request a new one"
-      not_found: "not found"
-      not_locked: "was not locked"
-      not_saved:
-        one: "1 error prohibited this %{resource} from being saved:"
-        other: "%{count} errors prohibited this %{resource} from being saved:"
diff --git a/demo/config/locales/en.yml b/demo/config/locales/en.yml
deleted file mode 100644
index 0653957..0000000
--- a/demo/config/locales/en.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-# Files in the config/locales directory are used for internationalization
-# and are automatically loaded by Rails. If you want to use locales other
-# than English, add the necessary files in this directory.
-#
-# To use the locales, use `I18n.t`:
-#
-#     I18n.t 'hello'
-#
-# In views, this is aliased to just `t`:
-#
-#     <%= t('hello') %>
-#
-# To use a different locale, set it with `I18n.locale`:
-#
-#     I18n.locale = :es
-#
-# This would use the information in config/locales/es.yml.
-#
-# To learn more, please read the Rails Internationalization guide
-# available at http://guides.rubyonrails.org/i18n.html.
-
-en:
-  hello: "Hello world"
diff --git a/demo/config/routes.rb b/demo/config/routes.rb
deleted file mode 100644
index bcd765e..0000000
--- a/demo/config/routes.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-Rails.application.routes.draw do
-  get 'home/index'
-  post 'users/enable_otp'
-  post 'users/disable_otp'
-
-  devise_for :users
-
-  root to: "home#index", via: [:get, :post]
-end
diff --git a/demo/config/secrets.yml b/demo/config/secrets.yml
deleted file mode 100644
index 3d12a2a..0000000
--- a/demo/config/secrets.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Your secret key is used for verifying the integrity of signed cookies.
-# If you change this key, all old signed cookies will become invalid!
-
-# Make sure the secret is at least 30 characters and all random,
-# no regular words or you'll be exposed to dictionary attacks.
-# You can use `rake secret` to generate a secure secret key.
-
-# Make sure the secrets in this file are kept private
-# if you're sharing your code publicly.
-
-development:
-  secret_key_base: c8c845b85a69d2b67ec99c1912ddcde63f0fcd05a74052f396daf68a2688d576c203bdc239c9423a0cf88218e201d30616959d5eeceea00eedf915677ecd373b
-
-test:
-  secret_key_base: df468dd66c4b0f143354d216d96cdb8542ffe215afd78ebf977f7a8bfdb93010db134320cb738d27385bd7884cd73f3f22f90b147d4d176c3c8d0d63d5427fa9
-
-# Do not keep production secrets in the repository,
-# instead read values from the environment.
-production:
-  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/demo/db/migrate/20140515190128_devise_create_users.rb b/demo/db/migrate/20140515190128_devise_create_users.rb
deleted file mode 100644
index cf497c2..0000000
--- a/demo/db/migrate/20140515190128_devise_create_users.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-class DeviseCreateUsers < ActiveRecord::Migration
-  def change
-    create_table(:users) do |t|
-      ## Database authenticatable
-      t.string :email,              null: false, default: ""
-      t.string :encrypted_password, null: false, default: ""
-
-      ## Recoverable
-      t.string   :reset_password_token
-      t.datetime :reset_password_sent_at
-
-      ## Rememberable
-      t.datetime :remember_created_at
-
-      ## Trackable
-      t.integer  :sign_in_count, default: 0, null: false
-      t.datetime :current_sign_in_at
-      t.datetime :last_sign_in_at
-      t.string   :current_sign_in_ip
-      t.string   :last_sign_in_ip
-
-      ## Confirmable
-      # t.string   :confirmation_token
-      # t.datetime :confirmed_at
-      # t.datetime :confirmation_sent_at
-      # t.string   :unconfirmed_email # Only if using reconfirmable
-
-      ## Lockable
-      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
-      # t.string   :unlock_token # Only if unlock strategy is :email or :both
-      # t.datetime :locked_at
-
-
-      t.timestamps
-    end
-
-    add_index :users, :email,                unique: true
-    add_index :users, :reset_password_token, unique: true
-    # add_index :users, :confirmation_token,   unique: true
-    # add_index :users, :unlock_token,         unique: true
-  end
-end
diff --git a/demo/db/migrate/20140516191259_add_devise_two_factor_to_users.rb b/demo/db/migrate/20140516191259_add_devise_two_factor_to_users.rb
deleted file mode 100644
index 61fe494..0000000
--- a/demo/db/migrate/20140516191259_add_devise_two_factor_to_users.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class AddDeviseTwoFactorToUsers < ActiveRecord::Migration
-  def change
-    add_column :users, :encrypted_otp_secret, :string
-    add_column :users, :encrypted_otp_secret_iv, :string
-    add_column :users, :encrypted_otp_secret_salt, :string
-    add_column :users, :consumed_timestep, :integer
-    add_column :users, :otp_required_for_login, :boolean
-  end
-end
diff --git a/demo/db/schema.rb b/demo/db/schema.rb
deleted file mode 100644
index b08c69a..0000000
--- a/demo/db/schema.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# encoding: UTF-8
-# This file is auto-generated from the current state of the database. Instead
-# of editing this file, please use the migrations feature of Active Record to
-# incrementally modify your database, and then regenerate this schema definition.
-#
-# Note that this schema.rb definition is the authoritative source for your
-# database schema. If you need to create the application database on another
-# system, you should be using db:schema:load, not running all the migrations
-# from scratch. The latter is a flawed and unsustainable approach (the more migrations
-# you'll amass, the slower it'll run and the greater likelihood for issues).
-#
-# It's strongly recommended that you check this file into your version control system.
-
-ActiveRecord::Schema.define(version: 20140516191259) do
-
-  # These are extensions that must be enabled in order to support this database
-  enable_extension "plpgsql"
-
-  create_table "users", force: true do |t|
-    t.string   "email",                     default: "", null: false
-    t.string   "encrypted_password",        default: "", null: false
-    t.string   "reset_password_token"
-    t.datetime "reset_password_sent_at"
-    t.datetime "remember_created_at"
-    t.integer  "sign_in_count",             default: 0,  null: false
-    t.datetime "current_sign_in_at"
-    t.datetime "last_sign_in_at"
-    t.string   "current_sign_in_ip"
-    t.string   "last_sign_in_ip"
-    t.datetime "created_at"
-    t.datetime "updated_at"
-    t.string   "encrypted_otp_secret"
-    t.string   "encrypted_otp_secret_iv"
-    t.string   "encrypted_otp_secret_salt"
-    t.integer  "consumed_timestep"
-    t.boolean  "otp_required_for_login"
-  end
-
-  add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
-  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
-
-end
diff --git a/demo/db/seeds.rb b/demo/db/seeds.rb
deleted file mode 100644
index 4edb1e8..0000000
--- a/demo/db/seeds.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file should contain all the record creation needed to seed the database with its default values.
-# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
-#
-# Examples:
-#
-#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
-#   Mayor.create(name: 'Emanuel', city: cities.first)
diff --git a/demo/lib/assets/.keep b/demo/lib/assets/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/lib/tasks/.keep b/demo/lib/tasks/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/log/.keep b/demo/log/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/public/404.html b/demo/public/404.html
deleted file mode 100644
index b612547..0000000
--- a/demo/public/404.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>The page you were looking for doesn't exist (404)</title>
-  <meta name="viewport" content="width=device-width,initial-scale=1">
-  <style>
-  body {
-    background-color: #EFEFEF;
-    color: #2E2F30;
-    text-align: center;
-    font-family: arial, sans-serif;
-    margin: 0;
-  }
-
-  div.dialog {
-    width: 95%;
-    max-width: 33em;
-    margin: 4em auto 0;
-  }
-
-  div.dialog > div {
-    border: 1px solid #CCC;
-    border-right-color: #999;
-    border-left-color: #999;
-    border-bottom-color: #BBB;
-    border-top: #B00100 solid 4px;
-    border-top-left-radius: 9px;
-    border-top-right-radius: 9px;
-    background-color: white;
-    padding: 7px 12% 0;
-    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
-  }
-
-  h1 {
-    font-size: 100%;
-    color: #730E15;
-    line-height: 1.5em;
-  }
-
-  div.dialog > p {
-    margin: 0 0 1em;
-    padding: 1em;
-    background-color: #F7F7F7;
-    border: 1px solid #CCC;
-    border-right-color: #999;
-    border-left-color: #999;
-    border-bottom-color: #999;
-    border-bottom-left-radius: 4px;
-    border-bottom-right-radius: 4px;
-    border-top-color: #DADADA;
-    color: #666;
-    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
-  }
-  </style>
-</head>
-
-<body>
-  <!-- This file lives in public/404.html -->
-  <div class="dialog">
-    <div>
-      <h1>The page you were looking for doesn't exist.</h1>
-      <p>You may have mistyped the address or the page may have moved.</p>
-    </div>
-    <p>If you are the application owner check the logs for more information.</p>
-  </div>
-</body>
-</html>
diff --git a/demo/public/422.html b/demo/public/422.html
deleted file mode 100644
index a21f82b..0000000
--- a/demo/public/422.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>The change you wanted was rejected (422)</title>
-  <meta name="viewport" content="width=device-width,initial-scale=1">
-  <style>
-  body {
-    background-color: #EFEFEF;
-    color: #2E2F30;
-    text-align: center;
-    font-family: arial, sans-serif;
-    margin: 0;
-  }
-
-  div.dialog {
-    width: 95%;
-    max-width: 33em;
-    margin: 4em auto 0;
-  }
-
-  div.dialog > div {
-    border: 1px solid #CCC;
-    border-right-color: #999;
-    border-left-color: #999;
-    border-bottom-color: #BBB;
-    border-top: #B00100 solid 4px;
-    border-top-left-radius: 9px;
-    border-top-right-radius: 9px;
-    background-color: white;
-    padding: 7px 12% 0;
-    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
-  }
-
-  h1 {
-    font-size: 100%;
-    color: #730E15;
-    line-height: 1.5em;
-  }
-
-  div.dialog > p {
-    margin: 0 0 1em;
-    padding: 1em;
-    background-color: #F7F7F7;
-    border: 1px solid #CCC;
-    border-right-color: #999;
-    border-left-color: #999;
-    border-bottom-color: #999;
-    border-bottom-left-radius: 4px;
-    border-bottom-right-radius: 4px;
-    border-top-color: #DADADA;
-    color: #666;
-    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
-  }
-  </style>
-</head>
-
-<body>
-  <!-- This file lives in public/422.html -->
-  <div class="dialog">
-    <div>
-      <h1>The change you wanted was rejected.</h1>
-      <p>Maybe you tried to change something you didn't have access to.</p>
-    </div>
-    <p>If you are the application owner check the logs for more information.</p>
-  </div>
-</body>
-</html>
diff --git a/demo/public/500.html b/demo/public/500.html
deleted file mode 100644
index 061abc5..0000000
--- a/demo/public/500.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>We're sorry, but something went wrong (500)</title>
-  <meta name="viewport" content="width=device-width,initial-scale=1">
-  <style>
-  body {
-    background-color: #EFEFEF;
-    color: #2E2F30;
-    text-align: center;
-    font-family: arial, sans-serif;
-    margin: 0;
-  }
-
-  div.dialog {
-    width: 95%;
-    max-width: 33em;
-    margin: 4em auto 0;
-  }
-
-  div.dialog > div {
-    border: 1px solid #CCC;
-    border-right-color: #999;
-    border-left-color: #999;
-    border-bottom-color: #BBB;
-    border-top: #B00100 solid 4px;
-    border-top-left-radius: 9px;
-    border-top-right-radius: 9px;
-    background-color: white;
-    padding: 7px 12% 0;
-    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
-  }
-
-  h1 {
-    font-size: 100%;
-    color: #730E15;
-    line-height: 1.5em;
-  }
-
-  div.dialog > p {
-    margin: 0 0 1em;
-    padding: 1em;
-    background-color: #F7F7F7;
-    border: 1px solid #CCC;
-    border-right-color: #999;
-    border-left-color: #999;
-    border-bottom-color: #999;
-    border-bottom-left-radius: 4px;
-    border-bottom-right-radius: 4px;
-    border-top-color: #DADADA;
-    color: #666;
-    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
-  }
-  </style>
-</head>
-
-<body>
-  <!-- This file lives in public/500.html -->
-  <div class="dialog">
-    <div>
-      <h1>We're sorry, but something went wrong.</h1>
-    </div>
-    <p>If you are the application owner check the logs for more information.</p>
-  </div>
-</body>
-</html>
diff --git a/demo/public/favicon.ico b/demo/public/favicon.ico
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/public/robots.txt b/demo/public/robots.txt
deleted file mode 100644
index 3c9c7c0..0000000
--- a/demo/public/robots.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
-#
-# To ban all spiders from the entire site uncomment the next two lines:
-# User-agent: *
-# Disallow: /
diff --git a/demo/test/controllers/.keep b/demo/test/controllers/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/test/controllers/home_controller_test.rb b/demo/test/controllers/home_controller_test.rb
deleted file mode 100644
index 0d9bb47..0000000
--- a/demo/test/controllers/home_controller_test.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'test_helper'
-
-class HomeControllerTest < ActionController::TestCase
-  test "should get index" do
-    get :index
-    assert_response :success
-  end
-
-end
diff --git a/demo/test/fixtures/.keep b/demo/test/fixtures/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/test/fixtures/users.yml b/demo/test/fixtures/users.yml
deleted file mode 100644
index 937a0c0..0000000
--- a/demo/test/fixtures/users.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
-
-# This model initially had no columns defined.  If you add columns to the
-# model remove the '{}' from the fixture names and add the columns immediately
-# below each fixture, per the syntax in the comments below
-#
-one: {}
-# column: value
-#
-two: {}
-#  column: value
diff --git a/demo/test/helpers/.keep b/demo/test/helpers/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/test/helpers/home_helper_test.rb b/demo/test/helpers/home_helper_test.rb
deleted file mode 100644
index 4740a18..0000000
--- a/demo/test/helpers/home_helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class HomeHelperTest < ActionView::TestCase
-end
diff --git a/demo/test/integration/.keep b/demo/test/integration/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/test/mailers/.keep b/demo/test/mailers/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/test/models/.keep b/demo/test/models/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/test/models/user_test.rb b/demo/test/models/user_test.rb
deleted file mode 100644
index 82f61e0..0000000
--- a/demo/test/models/user_test.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'test_helper'
-
-class UserTest < ActiveSupport::TestCase
-  # test "the truth" do
-  #   assert true
-  # end
-end
diff --git a/demo/test/test_helper.rb b/demo/test/test_helper.rb
deleted file mode 100644
index bf95f81..0000000
--- a/demo/test/test_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-ENV['RAILS_ENV'] ||= 'test'
-require File.expand_path('../../config/environment', __FILE__)
-require 'rails/test_help'
-
-class ActiveSupport::TestCase
-  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
-  #
-  # Note: You'll currently still have to declare fixtures explicitly in integration tests
-  # -- they do not yet inherit this setting
-  fixtures :all
-
-  # Add more helper methods to be used by all tests here...
-end
diff --git a/demo/vendor/assets/javascripts/.keep b/demo/vendor/assets/javascripts/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/demo/vendor/assets/stylesheets/.keep b/demo/vendor/assets/stylesheets/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/devise-two-factor.gemspec b/devise-two-factor.gemspec
index d6b08d2..a951b00 100644
--- a/devise-two-factor.gemspec
+++ b/devise-two-factor.gemspec
@@ -26,9 +26,9 @@ Gem::Specification.new do |s|
 
   s.add_runtime_dependency 'railties'
   s.add_runtime_dependency 'activesupport'
-  s.add_runtime_dependency 'attr_encrypted', '~> 1.3.2'
-  s.add_runtime_dependency 'devise',         '~> 3.5.0'
-  s.add_runtime_dependency 'rotp',           '~> 2'
+  s.add_runtime_dependency 'attr_encrypted', '>= 1.3', '< 4', '!= 2'
+  s.add_runtime_dependency 'devise',         '~> 4.0'
+  s.add_runtime_dependency 'rotp',           '~> 2.0'
 
   s.add_development_dependency 'activemodel'
   s.add_development_dependency 'bundler',    '> 1.0'
diff --git a/lib/devise-two-factor.rb b/lib/devise-two-factor.rb
index cf58701..f864e9b 100644
--- a/lib/devise-two-factor.rb
+++ b/lib/devise-two-factor.rb
@@ -5,7 +5,7 @@ require 'devise_two_factor/strategies'
 module Devise
   # The length of generated OTP secrets
   mattr_accessor :otp_secret_length
-  @@otp_secret_length = 128
+  @@otp_secret_length = 24
 
   # The number of seconds before and after the current
   # time for which codes will be accepted
diff --git a/lib/devise_two_factor/models/two_factor_authenticatable.rb b/lib/devise_two_factor/models/two_factor_authenticatable.rb
index d5f1e64..7c16024 100644
--- a/lib/devise_two_factor/models/two_factor_authenticatable.rb
+++ b/lib/devise_two_factor/models/two_factor_authenticatable.rb
@@ -8,8 +8,15 @@ module Devise
       include Devise::Models::DatabaseAuthenticatable
 
       included do
-        attr_encrypted :otp_secret, :key  => self.otp_secret_encryption_key,
-                                    :mode => :per_attribute_iv_and_salt unless self.attr_encrypted?(:otp_secret)
+        unless singleton_class.ancestors.include?(AttrEncrypted)
+          extend AttrEncrypted
+        end
+
+        unless attr_encrypted?(:otp_secret)
+          attr_encrypted :otp_secret,
+            :key  => self.otp_secret_encryption_key,
+            :mode => :per_attribute_iv_and_salt unless self.attr_encrypted?(:otp_secret)
+        end
 
         attr_accessor :otp_attempt
       end
@@ -22,7 +29,7 @@ module Devise
       # If this hasn't been generated yet, pass a secret as an option
       def validate_and_consume_otp!(code, options = {})
         otp_secret = options[:otp_secret] || self.otp_secret
-        return false unless otp_secret.present?
+        return false unless code.present? && otp_secret.present?
 
         totp = self.otp(otp_secret)
         return consume_otp! if totp.verify_with_drift(code, self.class.otp_allowed_drift)
diff --git a/lib/devise_two_factor/spec_helpers/two_factor_authenticatable_shared_examples.rb b/lib/devise_two_factor/spec_helpers/two_factor_authenticatable_shared_examples.rb
index b2f28cf..43a907b 100644
--- a/lib/devise_two_factor/spec_helpers/two_factor_authenticatable_shared_examples.rb
+++ b/lib/devise_two_factor/spec_helpers/two_factor_authenticatable_shared_examples.rb
@@ -1,6 +1,7 @@
 shared_examples 'two_factor_authenticatable' do
   before :each do
     subject.otp_secret = subject.class.generate_otp_secret
+    subject.consumed_timestep = nil
   end
 
   describe 'required_fields' do
@@ -70,6 +71,11 @@ shared_examples 'two_factor_authenticatable' do
       expect(subject.validate_and_consume_otp!(otp)).to be true
     end
 
+    it 'fails a nil OTP value' do
+      otp = nil
+      expect(subject.validate_and_consume_otp!(otp)).to be false
+    end
+
     it 'validates an OTP within the allowed drift' do
       otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift, true)
       expect(subject.validate_and_consume_otp!(otp)).to be true
@@ -96,7 +102,8 @@ shared_examples 'two_factor_authenticatable' do
     end
 
     it 'should return uri with issuer option' do
-      expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{account}\?secret=\w{#{otp_secret_length}}&issuer=#{issuer}$})
+      expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{account}\?.*secret=\w{#{otp_secret_length}}(&|$)})
+      expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{account}\?.*issuer=#{issuer}(&|$)})
     end
   end
 end
diff --git a/lib/devise_two_factor/version.rb b/lib/devise_two_factor/version.rb
index 1379dec..431a060 100644
--- a/lib/devise_two_factor/version.rb
+++ b/lib/devise_two_factor/version.rb
@@ -1,3 +1,3 @@
 module DeviseTwoFactor
-   VERSION = '2.0.0'.freeze
+   VERSION = '3.0.0'.freeze
 end
diff --git a/metadata.gz.sig b/metadata.gz.sig
new file mode 100644
index 0000000..eee5e73
Binary files /dev/null and b/metadata.gz.sig differ
diff --git a/spec/devise/models/two_factor_authenticatable_spec.rb b/spec/devise/models/two_factor_authenticatable_spec.rb
index e6a1e6e..e89dad5 100644
--- a/spec/devise/models/two_factor_authenticatable_spec.rb
+++ b/spec/devise/models/two_factor_authenticatable_spec.rb
@@ -2,10 +2,38 @@ require 'spec_helper'
 require 'active_model'
 
 class TwoFactorAuthenticatableDouble
+  extend ::ActiveModel::Callbacks
   include ::ActiveModel::Validations::Callbacks
   extend  ::Devise::Models
 
-  devise :two_factor_authenticatable, :otp_secret_encryption_key => 'test-key'
+  define_model_callbacks :update
+
+  devise :two_factor_authenticatable, :otp_secret_encryption_key => 'test-key'*4
+
+  attr_accessor :consumed_timestep
+
+  def save(validate)
+    # noop for testing
+    true
+  end
+end
+
+class TwoFactorAuthenticatableWithCustomizeAttrEncryptedDouble
+  extend ::ActiveModel::Callbacks
+  include ::ActiveModel::Validations::Callbacks
+
+  # like https://github.com/tinfoil/devise-two-factor/blob/cf73e52043fbe45b74d68d02bc859522ad22fe73/UPGRADING.md#guide-to-upgrading-from-2x-to-3x
+  extend ::AttrEncrypted
+  attr_encrypted :otp_secret,
+                  :key       => 'test-key'*8,
+                  :mode      => :per_attribute_iv_and_salt,
+                  :algorithm => 'aes-256-cbc'
+
+  extend  ::Devise::Models
+
+  define_model_callbacks :update
+
+  devise :two_factor_authenticatable, :otp_secret_encryption_key => 'test-key'*4
 
   attr_accessor :consumed_timestep
 
@@ -22,3 +50,30 @@ describe ::Devise::Models::TwoFactorAuthenticatable do
     it_behaves_like 'two_factor_authenticatable'
   end
 end
+
+describe ::Devise::Models::TwoFactorAuthenticatable do
+  context 'When included in a class' do
+    subject { TwoFactorAuthenticatableWithCustomizeAttrEncryptedDouble.new }
+
+    it_behaves_like 'two_factor_authenticatable'
+
+    before :each do
+      subject.otp_secret = subject.class.generate_otp_secret
+      subject.consumed_timestep = nil
+    end
+
+    describe 'otp_secret options' do
+      it 'should be of the key' do
+        expect(subject.encrypted_attributes[:otp_secret][:key]).to eq('test-key'*8)
+      end
+
+      it 'should be of the mode' do
+        expect(subject.encrypted_attributes[:otp_secret][:mode]).to eq(:per_attribute_iv_and_salt)
+      end
+
+      it 'should be of the mode' do
+        expect(subject.encrypted_attributes[:otp_secret][:algorithm]).to eq('aes-256-cbc')
+      end
+    end
+  end
+end
diff --git a/spec/devise/models/two_factor_backupable_spec.rb b/spec/devise/models/two_factor_backupable_spec.rb
index 64c23fd..6548b62 100644
--- a/spec/devise/models/two_factor_backupable_spec.rb
+++ b/spec/devise/models/two_factor_backupable_spec.rb
@@ -1,11 +1,15 @@
 require 'spec_helper'
+require 'active_model'
 
 class TwoFactorBackupableDouble
+  extend ::ActiveModel::Callbacks
   include ::ActiveModel::Validations::Callbacks
   extend  ::Devise::Models
 
+  define_model_callbacks :update
+
   devise :two_factor_authenticatable, :two_factor_backupable,
-         :otp_secret_encryption_key => 'test-key'
+         :otp_secret_encryption_key => 'test-key'*4
 
   attr_accessor :otp_backup_codes
 end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-devise-two-factor.git



More information about the Pkg-ruby-extras-commits mailing list