two_factor_authentication 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8037c367e1a0f7f7d253be0457b2046488b628fa
4
- data.tar.gz: 024269293da1e0a407ac4e3537dc4e8749ca95a5
3
+ metadata.gz: fb84a503671c99963674feb9a48b9e0b4078c457
4
+ data.tar.gz: e1004e05b1adfb4fc9dbae58638f3091a29cd4c9
5
5
  SHA512:
6
- metadata.gz: 0c5a7deda98812e0a46ced0f99f2c0af64d4cd57531aad76660fecc7a1b351ba776263b24066a3d1f8caa93279f97fba1d415bcf41caf13bb1bc627a3a0c49ba
7
- data.tar.gz: 2c80f6553ff57dbc3257f20c345cdf2217eefcf7d543f7547c0c31f8e57dbfc94ccf27759acbf81ba3937b05a808a8cc42acbb3ce7ee1ee7cb4869379f02bbaf
6
+ metadata.gz: 7f790fd330b74c0bed8199477a23dce4d52d5035c86495a62d2d5e5b0bc77f5d97c4cea8b6b9aee5a6b3b306d9d45664fb95b5a0c3ca89838b3a832302de1590
7
+ data.tar.gz: 48d79bca0d7eb2f51ed606d4c34aea8b7f7d01cf0c2e59e763a0fd6eaad167dbc53c4c7db495bc0931fa6b061c4a095a6d521b868334eeaf77267538861b3cf6
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ rails = case rails_version
9
9
  when "master"
10
10
  {github: "rails/rails"}
11
11
  when "default"
12
- "~> 3.2"
12
+ "~> 4.1"
13
13
  else
14
14
  "~> #{rails_version}"
15
15
  end
data/README.md CHANGED
@@ -38,11 +38,12 @@ Add the following line to your model to fully enable two-factor auth:
38
38
 
39
39
  has_one_time_password
40
40
 
41
- Set config values if desired for maximum second factor attempts count and allowed time drift for one-time passwords:
41
+ Set config values, if desired, for maximum second factor attempts count, allowed time drift, and OTP length.
42
42
 
43
43
  ```ruby
44
44
  config.max_login_attempts = 3
45
45
  config.allowed_otp_drift_seconds = 30
46
+ config.otp_length = 6
46
47
  ```
47
48
 
48
49
  Override the method to send one-time passwords in your model, this is automatically called when a user logs in:
@@ -66,11 +67,12 @@ Add the following line to your model to fully enable two-factor auth:
66
67
 
67
68
  has_one_time_password
68
69
 
69
- Set config values if desired for maximum second factor attempts count and allowed time drift for one-time passwords:
70
+ Set config values, if desired, for maximum second factor attempts count, allowed time drift, and OTP length.
70
71
 
71
72
  ```ruby
72
73
  config.max_login_attempts = 3
73
74
  config.allowed_otp_drift_seconds = 30
75
+ config.otp_length = 6
74
76
  ```
75
77
 
76
78
  Override the method to send one-time passwords in your model, this is automatically called when a user logs in:
@@ -81,6 +83,7 @@ def send_two_factor_authentication_code
81
83
  end
82
84
  ```
83
85
 
86
+
84
87
  ### Customisation and Usage
85
88
 
86
89
  By default second factor authentication enabled for each user, you can change it with this method in your User model:
@@ -99,6 +102,42 @@ This gem is compatible with Google Authenticator (https://support.google.com/acc
99
102
 
100
103
  This provisioning uri can then be turned in to a QR code if desired so that users may add the app to Google Authenticator easily. Once this is done they may retrieve a one-time password directly from the Google Authenticator app as well as through whatever method you define in `send_two_factor_authentication_code`
101
104
 
105
+ #### Overriding the view
106
+
107
+ The default view that shows the form can be overridden by first adding a folder named: "two_factor_authentication" inside "app/views/devise", in here you want to create a "show.html.erb" view.
108
+
109
+ The full path should be "app/views/devise/two_factor_authentication/show.html.erb"
110
+
111
+ ```html
112
+ <h2>Hi, you received a code by email, please enter it below, thanks!</h2>
113
+
114
+ <%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
115
+ <%= text_field_tag :code %>
116
+ <%= submit_tag "Log in!" %>
117
+ <% end %>
118
+
119
+ <%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
120
+
121
+ ```
122
+
123
+ #### Updating existing users with OTP secret key
124
+
125
+ If you have existing users that needs to be provided with a OTP secret key, so they can take benefit of the two factor authentication, create a rake. It could look like this one below:
126
+
127
+ ```ruby
128
+ desc "rake task to update users with otp secret key"
129
+ task :update_users_with_otp_secret_key => :environment do
130
+ users = User.all
131
+
132
+ users.each do |user|
133
+ key = ROTP::Base32.random_base32
134
+ user.update_attributes(:otp_secret_key => key)
135
+ user.save
136
+ puts "Rake[:update_users_with_otp_secret_key] => User '#{user.email}' OTP secret key set to '#{key}'"
137
+ end
138
+ end
139
+ ```
140
+
102
141
  ### Example
103
142
 
104
143
  [TwoFactorAuthenticationExample](https://github.com/Houdini/TwoFactorAuthenticationExample)
@@ -9,7 +9,7 @@ class Devise::TwoFactorAuthenticationController < DeviseController
9
9
  render :show and return if params[:code].nil?
10
10
 
11
11
  if resource.authenticate_otp(params[:code])
12
- warden.session(resource_name)['need_two_factor_authentication'] = false
12
+ warden.session(resource_name)[TwoFactorAuthentication::NEED_AUTHENTICATION] = false
13
13
  sign_in resource_name, resource, :bypass => true
14
14
  set_flash_message :notice, :success
15
15
  redirect_to stored_location_for(resource_name) || :root
@@ -1,6 +1,7 @@
1
1
  ru:
2
2
  devise:
3
3
  two_factor_authentication:
4
+ success: "Двухфакторная авторизация успешно пройдена."
4
5
  attempt_failed: "Неверный код."
5
6
  max_login_attempts_reached: "Доступ заблокирован. Превышено число попыток авторизации"
6
7
  contact_administrator: "Пожалуйста, свяжитесь с системным администратором."
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  source_root File.expand_path("../templates", __FILE__)
7
7
 
8
8
  def copy_two_factor_authentication_migration
9
- migration_template "migration.rb", "db/migrate/two_factor_authentication_add_to_#{table_name}"
9
+ migration_template "migration.rb", "db/migrate/two_factor_authentication_add_to_#{table_name}.rb"
10
10
  end
11
11
 
12
12
  end
@@ -13,9 +13,14 @@ module Devise
13
13
 
14
14
  mattr_accessor :allowed_otp_drift_seconds
15
15
  @@allowed_otp_drift_seconds = 30
16
+
17
+ mattr_accessor :otp_length
18
+ @@otp_length = 6
16
19
  end
17
20
 
18
21
  module TwoFactorAuthentication
22
+ NEED_AUTHENTICATION = 'need_two_factor_authentication'
23
+
19
24
  autoload :Schema, 'two_factor_authentication/schema'
20
25
  module Controllers
21
26
  autoload :Helpers, 'two_factor_authentication/controllers/helpers'
@@ -12,7 +12,7 @@ module TwoFactorAuthentication
12
12
  def handle_two_factor_authentication
13
13
  unless devise_controller?
14
14
  Devise.mappings.keys.flatten.any? do |scope|
15
- if signed_in?(scope) and warden.session(scope)['need_two_factor_authentication']
15
+ if signed_in?(scope) and warden.session(scope)[TwoFactorAuthentication::NEED_AUTHENTICATION]
16
16
  handle_failed_second_factor(scope)
17
17
  end
18
18
  end
@@ -21,7 +21,7 @@ module TwoFactorAuthentication
21
21
 
22
22
  def handle_failed_second_factor(scope)
23
23
  if request.format.present? and request.format.html?
24
- session["#{scope}_return_to"] = request.path if request.get?
24
+ session["#{scope}_return_to"] = "#{request.path}?#{request.query_string}" if request.get?
25
25
  redirect_to two_factor_authentication_path_for(scope)
26
26
  else
27
27
  render nothing: true, status: :unauthorized
@@ -42,7 +42,7 @@ module Devise
42
42
  module Controllers
43
43
  module Helpers
44
44
  def is_fully_authenticated?
45
- !session["warden.user.user.session"].try(:[], 'need_two_factor_authentication')
45
+ !session["warden.user.user.session"].try(:[], TwoFactorAuthentication::NEED_AUTHENTICATION)
46
46
  end
47
47
  end
48
48
  end
@@ -1,6 +1,6 @@
1
1
  Warden::Manager.after_authentication do |user, auth, options|
2
2
  if user.respond_to?(:need_two_factor_authentication?)
3
- if auth.session(options[:scope])['need_two_factor_authentication'] = user.need_two_factor_authentication?(auth.request)
3
+ if auth.session(options[:scope])[TwoFactorAuthentication::NEED_AUTHENTICATION] = user.need_two_factor_authentication?(auth.request)
4
4
  user.send_two_factor_authentication_code
5
5
  end
6
6
  end
@@ -20,19 +20,19 @@ module Devise
20
20
  end
21
21
  end
22
22
  end
23
- ::Devise::Models.config(self, :max_login_attempts, :allowed_otp_drift_seconds)
23
+ ::Devise::Models.config(self, :max_login_attempts, :allowed_otp_drift_seconds, :otp_length)
24
24
  end
25
25
 
26
26
  module InstanceMethodsOnActivation
27
27
  def authenticate_otp(code, options = {})
28
- totp = ROTP::TOTP.new(self.otp_column)
28
+ totp = ROTP::TOTP.new(self.otp_column, { digits: options[:otp_length] || self.class.otp_length })
29
29
  drift = options[:drift] || self.class.allowed_otp_drift_seconds
30
30
 
31
31
  totp.verify_with_drift(code, drift)
32
32
  end
33
33
 
34
- def otp_code(time = Time.now)
35
- ROTP::TOTP.new(self.otp_column).at(time, true)
34
+ def otp_code(time = Time.now, options = {})
35
+ ROTP::TOTP.new(self.otp_column, { digits: options[:otp_length] || self.class.otp_length }).at(time, true)
36
36
  end
37
37
 
38
38
  def provisioning_uri(account = nil, options = {})
@@ -1,3 +1,3 @@
1
1
  module TwoFactorAuthentication
2
- VERSION = "1.1.1".freeze
2
+ VERSION = "1.1.3".freeze
3
3
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  include Warden::Test::Helpers
4
4
 
5
- describe HomeController do
5
+ describe HomeController, :type => :controller do
6
6
  context "passed only 1st factor auth" do
7
7
  let(:user) { create_user }
8
8
 
@@ -11,10 +11,8 @@ describe HomeController do
11
11
  login_as user, scope: :user
12
12
  visit user_two_factor_authentication_path
13
13
 
14
-
15
- controller.is_fully_authenticated?.should be_true
14
+ expect(controller.is_fully_authenticated?).to be_truthy
16
15
  end
17
16
  end
18
-
19
17
  end
20
- end
18
+ end
@@ -11,14 +11,14 @@ feature "User of two factor authentication" do
11
11
  end
12
12
 
13
13
  scenario "sends two factor authentication code after sign in" do
14
- SMSProvider.messages.should be_empty
14
+ expect(SMSProvider.messages).to be_empty
15
15
 
16
16
  visit new_user_session_path
17
17
  complete_sign_in_form_for(user)
18
18
 
19
19
  expect(page).to have_content "Enter your personal code"
20
20
 
21
- SMSProvider.messages.size.should eq(1)
21
+ expect(SMSProvider.messages.size).to eq(1)
22
22
  message = SMSProvider.last_message
23
23
  expect(message.to).to eq(user.phone_number)
24
24
  expect(message.body).to eq(user.otp_code)
@@ -45,7 +45,7 @@ feature "User of two factor authentication" do
45
45
  end
46
46
 
47
47
  scenario "is redirected to TFA when path requires authentication" do
48
- visit dashboard_path
48
+ visit dashboard_path + "?A=param%20a&B=param%20b"
49
49
 
50
50
  expect(page).to_not have_content("Your Personal Dashboard")
51
51
 
@@ -54,6 +54,8 @@ feature "User of two factor authentication" do
54
54
 
55
55
  expect(page).to have_content("Your Personal Dashboard")
56
56
  expect(page).to have_content("You are signed in as Marissa")
57
+ expect(page).to have_content("Param A is param a")
58
+ expect(page).to have_content("Param B is param b")
57
59
  end
58
60
 
59
61
  scenario "is locked out after max failed attempts" do
@@ -21,11 +21,15 @@ describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do
21
21
  subject
22
22
  end
23
23
 
24
+ it "should be configured length" do
25
+ expect(subject.length).to eq(Devise.otp_length)
26
+ end
27
+
24
28
  context "with a known time" do
25
29
  let(:time) { 1392852756 }
26
30
 
27
31
  it "should return a known result" do
28
- expect(subject).to eq('562202')
32
+ expect(subject).to eq("0000000524562202".split(//).last(Devise.otp_length).join)
29
33
  end
30
34
  end
31
35
 
@@ -33,7 +37,7 @@ describe Devise::Models::TwoFactorAuthenticatable, '#otp_code' do
33
37
  let(:time) { 1393065856 }
34
38
 
35
39
  it "should return a known result padded with zeroes" do
36
- expect(subject).to eq('007672')
40
+ expect(subject).to eq("0000001608007672".split(//).last(Devise.otp_length).join)
37
41
  end
38
42
  end
39
43
  end
@@ -146,19 +150,19 @@ describe Devise::Models::TwoFactorAuthenticatable, '#max_login_attempts' do
146
150
 
147
151
  it "returns false as boolean" do
148
152
  instance.second_factor_attempts_count = nil
149
- expect(instance.max_login_attempts?).to be_false
153
+ expect(instance.max_login_attempts?).to be_falsey
150
154
  instance.second_factor_attempts_count = 0
151
- expect(instance.max_login_attempts?).to be_false
155
+ expect(instance.max_login_attempts?).to be_falsey
152
156
  instance.second_factor_attempts_count = 1
153
- expect(instance.max_login_attempts?).to be_false
157
+ expect(instance.max_login_attempts?).to be_falsey
154
158
  instance.second_factor_attempts_count = 2
155
- expect(instance.max_login_attempts?).to be_false
159
+ expect(instance.max_login_attempts?).to be_falsey
156
160
  end
157
161
 
158
162
  it "returns true as boolean after too many attempts" do
159
163
  instance.second_factor_attempts_count = 3
160
- expect(instance.max_login_attempts?).to be_true
164
+ expect(instance.max_login_attempts?).to be_truthy
161
165
  instance.second_factor_attempts_count = 4
162
- expect(instance.max_login_attempts?).to be_true
166
+ expect(instance.max_login_attempts?).to be_truthy
163
167
  end
164
168
  end
@@ -4,4 +4,8 @@
4
4
 
5
5
  <p>Your registered email address is <%= current_user.email %></p>
6
6
 
7
+ <p> Param A is <%= params[:A] %></p>
8
+
9
+ <p> Param B is <%= params[:B] %></p>
10
+
7
11
  <p>You can only see this page after successfully completing two factor authentication</p>
@@ -5,12 +5,13 @@ require 'rspec/rails'
5
5
 
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
7
  RSpec.configure do |config|
8
- config.treat_symbols_as_metadata_keys_with_true_values = true
9
8
  config.run_all_when_everything_filtered = true
10
9
  config.filter_run :focus
11
10
 
12
11
  config.use_transactional_examples = true
13
12
 
13
+ config.include Capybara::DSL
14
+
14
15
  # Run specs in random order to surface order dependencies. If you find an
15
16
  # order dependency and want to debug it, you can fix the order by providing
16
17
  # the seed, which is printed after each run.
@@ -8,7 +8,7 @@ module FeaturesSpecHelper
8
8
  def complete_sign_in_form_for(user)
9
9
  fill_in "Email", with: user.email
10
10
  fill_in "Password", with: 'password'
11
- click_button "Sign in"
11
+ find('.actions input').click # 'Sign in' or 'Log in'
12
12
  end
13
13
  end
14
14
 
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
31
31
 
32
32
  s.add_development_dependency 'bundler'
33
33
  s.add_development_dependency 'rake'
34
- s.add_development_dependency 'rspec-rails'
35
- s.add_development_dependency 'capybara'
34
+ s.add_development_dependency 'rspec-rails', '>= 3.0.1'
35
+ s.add_development_dependency 'capybara', '2.4.1'
36
36
  s.add_development_dependency 'pry'
37
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: two_factor_authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitrii Golub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-31 00:00:00.000000000 Z
11
+ date: 2014-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -100,28 +100,28 @@ dependencies:
100
100
  requirements:
101
101
  - - '>='
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 3.0.1
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '>='
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 3.0.1
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: capybara
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '>='
115
+ - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: 2.4.1
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: 2.4.1
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: pry
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -248,58 +248,5 @@ rubygems_version: 2.2.2
248
248
  signing_key:
249
249
  specification_version: 4
250
250
  summary: Two factor authentication plugin for devise
251
- test_files:
252
- - spec/controllers/two_factor_auth_spec.rb
253
- - spec/features/two_factor_authenticatable_spec.rb
254
- - spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb
255
- - spec/rails_app/.gitignore
256
- - spec/rails_app/README.md
257
- - spec/rails_app/Rakefile
258
- - spec/rails_app/app/assets/javascripts/application.js
259
- - spec/rails_app/app/assets/stylesheets/application.css
260
- - spec/rails_app/app/controllers/application_controller.rb
261
- - spec/rails_app/app/controllers/home_controller.rb
262
- - spec/rails_app/app/helpers/application_helper.rb
263
- - spec/rails_app/app/mailers/.gitkeep
264
- - spec/rails_app/app/models/.gitkeep
265
- - spec/rails_app/app/models/guest_user.rb
266
- - spec/rails_app/app/models/user.rb
267
- - spec/rails_app/app/views/home/dashboard.html.erb
268
- - spec/rails_app/app/views/home/index.html.erb
269
- - spec/rails_app/app/views/layouts/application.html.erb
270
- - spec/rails_app/config.ru
271
- - spec/rails_app/config/application.rb
272
- - spec/rails_app/config/boot.rb
273
- - spec/rails_app/config/database.yml
274
- - spec/rails_app/config/environment.rb
275
- - spec/rails_app/config/environments/development.rb
276
- - spec/rails_app/config/environments/production.rb
277
- - spec/rails_app/config/environments/test.rb
278
- - spec/rails_app/config/initializers/backtrace_silencers.rb
279
- - spec/rails_app/config/initializers/cookies_serializer.rb
280
- - spec/rails_app/config/initializers/devise.rb
281
- - spec/rails_app/config/initializers/inflections.rb
282
- - spec/rails_app/config/initializers/mime_types.rb
283
- - spec/rails_app/config/initializers/secret_token.rb
284
- - spec/rails_app/config/initializers/session_store.rb
285
- - spec/rails_app/config/initializers/wrap_parameters.rb
286
- - spec/rails_app/config/locales/devise.en.yml
287
- - spec/rails_app/config/locales/en.yml
288
- - spec/rails_app/config/routes.rb
289
- - spec/rails_app/db/migrate/20140403184646_devise_create_users.rb
290
- - spec/rails_app/db/migrate/20140407172619_two_factor_authentication_add_to_users.rb
291
- - spec/rails_app/db/migrate/20140407215513_add_nickanme_to_users.rb
292
- - spec/rails_app/db/schema.rb
293
- - spec/rails_app/lib/assets/.gitkeep
294
- - spec/rails_app/lib/sms_provider.rb
295
- - spec/rails_app/public/404.html
296
- - spec/rails_app/public/422.html
297
- - spec/rails_app/public/500.html
298
- - spec/rails_app/public/favicon.ico
299
- - spec/rails_app/script/rails
300
- - spec/spec_helper.rb
301
- - spec/support/authenticated_model_helper.rb
302
- - spec/support/capybara.rb
303
- - spec/support/features_spec_helper.rb
304
- - spec/support/sms_provider.rb
251
+ test_files: []
305
252
  has_rdoc: