devise-otp 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +36 -0
  3. data/.gitignore +0 -0
  4. data/Gemfile +1 -22
  5. data/LICENSE.txt +0 -0
  6. data/README.md +43 -66
  7. data/Rakefile +0 -0
  8. data/app/assets/javascripts/devise-otp.js +1 -0
  9. data/app/assets/javascripts/qrcode.js +609 -0
  10. data/app/controllers/devise_otp/devise/credentials_controller.rb +102 -0
  11. data/app/controllers/devise_otp/devise/tokens_controller.rb +112 -0
  12. data/app/views/devise/credentials/refresh.html.erb +19 -0
  13. data/app/views/devise/credentials/show.html.erb +31 -0
  14. data/app/views/devise/tokens/_token_secret.html.erb +23 -0
  15. data/app/views/devise/tokens/_trusted_devices.html.erb +12 -0
  16. data/app/views/devise/tokens/recovery.html.erb +21 -0
  17. data/app/views/devise/tokens/recovery_codes.text.erb +3 -0
  18. data/app/views/devise/tokens/show.html.erb +21 -0
  19. data/config/locales/en.yml +10 -10
  20. data/devise-otp.gemspec +14 -9
  21. data/docs/QR_CODES.md +48 -0
  22. data/lib/devise-otp/version.rb +1 -1
  23. data/lib/devise-otp.rb +22 -14
  24. data/lib/devise_otp_authenticatable/controllers/helpers.rb +29 -16
  25. data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +6 -9
  26. data/lib/devise_otp_authenticatable/engine.rb +22 -13
  27. data/lib/devise_otp_authenticatable/hooks/sessions.rb +8 -7
  28. data/lib/devise_otp_authenticatable/hooks.rb +1 -1
  29. data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +28 -28
  30. data/lib/devise_otp_authenticatable/routes.rb +9 -10
  31. data/lib/generators/active_record/devise_otp_generator.rb +1 -1
  32. data/lib/generators/active_record/templates/migration.rb +1 -2
  33. data/lib/generators/devise_otp/devise_otp_generator.rb +0 -0
  34. data/lib/generators/devise_otp/install_generator.rb +30 -5
  35. data/lib/generators/devise_otp/views_generator.rb +2 -3
  36. data/test/dummy/README.rdoc +0 -0
  37. data/test/dummy/Rakefile +0 -0
  38. data/test/dummy/app/assets/config/manifest.js +2 -0
  39. data/test/dummy/app/assets/javascripts/application.js +1 -0
  40. data/test/dummy/app/assets/stylesheets/application.css +0 -0
  41. data/test/dummy/app/controllers/application_controller.rb +1 -1
  42. data/test/dummy/app/controllers/posts_controller.rb +2 -0
  43. data/test/dummy/app/helpers/application_helper.rb +0 -0
  44. data/test/dummy/app/helpers/posts_helper.rb +0 -0
  45. data/test/dummy/app/mailers/.gitkeep +0 -0
  46. data/test/dummy/app/models/post.rb +0 -0
  47. data/test/dummy/app/models/user.rb +1 -1
  48. data/test/dummy/app/views/layouts/application.html.erb +0 -0
  49. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  50. data/test/dummy/app/views/posts/edit.html.erb +0 -0
  51. data/test/dummy/app/views/posts/index.html.erb +0 -0
  52. data/test/dummy/app/views/posts/new.html.erb +0 -0
  53. data/test/dummy/app/views/posts/show.html.erb +0 -0
  54. data/test/dummy/config/application.rb +2 -1
  55. data/test/dummy/config/boot.rb +0 -0
  56. data/test/dummy/config/database.yml +1 -1
  57. data/test/dummy/config/environment.rb +0 -0
  58. data/test/dummy/config/environments/development.rb +0 -7
  59. data/test/dummy/config/environments/production.rb +0 -4
  60. data/test/dummy/config/environments/test.rb +0 -0
  61. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -0
  62. data/test/dummy/config/initializers/devise.rb +0 -0
  63. data/test/dummy/config/initializers/inflections.rb +0 -0
  64. data/test/dummy/config/initializers/mime_types.rb +0 -0
  65. data/test/dummy/config/initializers/secret_token.rb +0 -0
  66. data/test/dummy/config/initializers/session_store.rb +0 -0
  67. data/test/dummy/config/initializers/wrap_parameters.rb +0 -0
  68. data/test/dummy/config/locales/en.yml +0 -0
  69. data/test/dummy/config/routes.rb +0 -0
  70. data/test/dummy/config.ru +0 -0
  71. data/test/dummy/db/migrate/20130125101430_create_users.rb +1 -1
  72. data/test/dummy/db/migrate/20130131092406_add_devise_to_users.rb +1 -1
  73. data/test/dummy/db/migrate/20130131142320_create_posts.rb +1 -1
  74. data/test/dummy/db/migrate/20130131160351_devise_otp_add_to_users.rb +2 -2
  75. data/test/dummy/db/test.sqlite3-journal +0 -0
  76. data/test/dummy/lib/assets/.gitkeep +0 -0
  77. data/test/dummy/public/404.html +0 -0
  78. data/test/dummy/public/422.html +0 -0
  79. data/test/dummy/public/500.html +0 -0
  80. data/test/dummy/public/favicon.ico +0 -0
  81. data/test/integration/persistence_test.rb +81 -0
  82. data/test/integration/refresh_test.rb +2 -18
  83. data/test/integration/sign_in_test.rb +14 -4
  84. data/test/integration/token_test.rb +31 -0
  85. data/test/integration_tests_helper.rb +19 -2
  86. data/test/model_tests_helper.rb +0 -0
  87. data/test/models/otp_authenticatable_test.rb +14 -9
  88. data/test/orm/active_record.rb +3 -1
  89. data/test/test_helper.rb +71 -2
  90. metadata +135 -24
  91. data/.travis.yml +0 -11
  92. data/app/controllers/devise_otp/credentials_controller.rb +0 -106
  93. data/app/controllers/devise_otp/tokens_controller.rb +0 -105
  94. data/app/views/devise_otp/credentials/refresh.html.erb +0 -20
  95. data/app/views/devise_otp/credentials/show.html.erb +0 -23
  96. data/app/views/devise_otp/tokens/_token_secret.html.erb +0 -17
  97. data/app/views/devise_otp/tokens/recovery.html.erb +0 -21
  98. data/app/views/devise_otp/tokens/show.html.erb +0 -31
  99. data/lib/devise_otp_authenticatable/mapping.rb +0 -19
@@ -1,9 +1,7 @@
1
1
  module DeviseOtpAuthenticatable
2
-
3
2
  module Controllers
4
3
  module Helpers
5
4
 
6
-
7
5
  def authenticate_scope!
8
6
  send(:"authenticate_#{resource_name}!", :force => true)
9
7
  self.resource = send("current_#{resource_name}")
@@ -18,17 +16,19 @@ module DeviseOtpAuthenticatable
18
16
  options[:default] = Array(options[:default]).unshift(kind.to_sym)
19
17
  options[:resource_name] = resource_name
20
18
  options = devise_i18n_options(options) if respond_to?(:devise_i18n_options, true)
21
- message = I18n.t("#{options[:resource_name]}.#{kind}", options)
19
+ message = I18n.t("#{options[:resource_name]}.#{kind}", **options)
22
20
  flash[key] = message if message.present?
23
21
  end
24
22
 
25
23
  def otp_t()
26
-
27
24
  end
28
25
 
26
+ def trusted_devices_enabled?
27
+ resource.class.otp_trust_persistence && (resource.class.otp_trust_persistence > 0)
28
+ end
29
29
 
30
30
  def recovery_enabled?
31
- resource_class.recovery_tokens && (resource_class.recovery_tokens > 0)
31
+ resource_class.otp_recovery_tokens && (resource_class.otp_recovery_tokens > 0)
32
32
  end
33
33
 
34
34
  #
@@ -40,9 +40,7 @@ module DeviseOtpAuthenticatable
40
40
  end
41
41
  end
42
42
 
43
-
44
43
  # fixme do cookies and persistence need to be scoped? probably
45
-
46
44
  #
47
45
  # check if the resource needs a credentials refresh. IE, they need to be asked a password again to access
48
46
  # this resource.
@@ -62,15 +60,14 @@ module DeviseOtpAuthenticatable
62
60
  session[otp_scoped_refresh_property] = (Time.now + resource.class.otp_credentials_refresh)
63
61
  end
64
62
 
65
-
66
63
  #
67
64
  # is the current browser trusted?
68
65
  #
69
- def is_otp_trusted_device_for?(resource)
66
+ def is_otp_trusted_browser_for?(resource)
67
+ return false unless resource.class.otp_trust_persistence
70
68
  if cookies[otp_scoped_persistence_cookie].present?
71
69
  cookies.signed[otp_scoped_persistence_cookie] ==
72
- [resource.class.serialize_into_cookie(resource), resource.otp_persistence_seed].tap do
73
- end
70
+ [resource.to_key, resource.authenticatable_salt, resource.otp_persistence_seed]
74
71
  else
75
72
  false
76
73
  end
@@ -80,10 +77,11 @@ module DeviseOtpAuthenticatable
80
77
  # make the current browser trusted
81
78
  #
82
79
  def otp_set_trusted_device_for(resource)
80
+ return unless resource.class.otp_trust_persistence
83
81
  cookies.signed[otp_scoped_persistence_cookie] = {
84
82
  :httponly => true,
85
- :expires => 30.days.from_now,
86
- :value => [resource.class.serialize_into_cookie(resource), resource.otp_persistence_seed]
83
+ :expires => Time.now + resource.class.otp_trust_persistence,
84
+ :value => [resource.to_key, resource.authenticatable_salt, resource.otp_persistence_seed]
87
85
  }
88
86
  end
89
87
 
@@ -115,7 +113,6 @@ module DeviseOtpAuthenticatable
115
113
  cookies.delete(otp_scoped_persistence_cookie)
116
114
  end
117
115
 
118
-
119
116
  #
120
117
  # clears the persistence list for this kind of resource
121
118
  #
@@ -128,11 +125,27 @@ module DeviseOtpAuthenticatable
128
125
  # returns the URL for the QR Code to initialize the Authenticator device
129
126
  #
130
127
  def otp_authenticator_token_image(resource)
131
- otp_authenticator_token_image_google(resource.otp_provisioning_uri)
128
+ otp_authenticator_token_image_js(resource.otp_provisioning_uri)
132
129
  end
133
130
 
134
131
  private
135
132
 
133
+ def otp_authenticator_token_image_js(otp_url)
134
+ content_tag(:div, :class => 'qrcode-container') do
135
+ tag(:div, :id => 'qrcode', :class => 'qrcode') +
136
+ javascript_tag(%Q[
137
+ new QRCode("qrcode", {
138
+ text: "#{otp_url}",
139
+ width: 256,
140
+ height: 256,
141
+ colorDark : "#000000",
142
+ colorLight : "#ffffff",
143
+ correctLevel : QRCode.CorrectLevel.H
144
+ });
145
+ ]) + tag("/div")
146
+ end
147
+ end
148
+
136
149
  def otp_authenticator_token_image_google(otp_url)
137
150
  otp_url = Rack::Utils.escape(otp_url)
138
151
  url = "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{otp_url}"
@@ -141,4 +154,4 @@ module DeviseOtpAuthenticatable
141
154
 
142
155
  end
143
156
  end
144
- end
157
+ end
@@ -1,35 +1,32 @@
1
1
  module DeviseOtpAuthenticatable
2
2
  module Controllers
3
-
4
3
  module UrlHelpers
5
4
 
6
-
7
5
  def recovery_otp_token_for(resource_or_scope, opts = {})
8
- scope = Devise::Mapping.find_scope!(resource_or_scope)
6
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
9
7
  send("recovery_#{scope}_otp_token_path", opts)
10
8
  end
11
9
 
12
-
13
10
  def refresh_otp_credential_path_for(resource_or_scope, opts = {})
14
- scope = Devise::Mapping.find_scope!(resource_or_scope)
11
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
15
12
  send("refresh_#{scope}_otp_credential_path", opts)
16
13
  end
17
14
 
18
15
  def persistence_otp_token_path_for(resource_or_scope, opts = {})
19
- scope = Devise::Mapping.find_scope!(resource_or_scope)
16
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
20
17
  send("persistence_#{scope}_otp_token_path", opts)
21
18
  end
22
19
 
23
20
  def otp_token_path_for(resource_or_scope, opts = {})
24
- scope = Devise::Mapping.find_scope!(resource_or_scope)
21
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
25
22
  send("#{scope}_otp_token_path", opts)
26
23
  end
27
24
 
28
25
  def otp_credential_path_for(resource_or_scope, opts = {})
29
- scope = Devise::Mapping.find_scope!(resource_or_scope)
26
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
30
27
  send("#{scope}_otp_credential_path", opts)
31
28
  end
32
29
 
33
30
  end
34
31
  end
35
- end
32
+ end
@@ -1,23 +1,32 @@
1
1
  module DeviseOtpAuthenticatable
2
2
  class Engine < ::Rails::Engine
3
3
 
4
- ActiveSupport.on_load(:action_controller) do
5
- include DeviseOtpAuthenticatable::Controllers::UrlHelpers
6
- include DeviseOtpAuthenticatable::Controllers::Helpers
7
- end
8
- ActiveSupport.on_load(:action_view) do
9
- include DeviseOtpAuthenticatable::Controllers::UrlHelpers
10
- include DeviseOtpAuthenticatable::Controllers::Helpers
11
- end
12
-
13
4
  # We use to_prepare instead of after_initialize here because Devise is a Rails engine;
14
5
  config.to_prepare do
15
6
  DeviseOtpAuthenticatable::Hooks.apply
16
7
  end
17
8
 
18
- # extend mapping with after_initialize because is not reloaded
19
- config.after_initialize do
20
- Devise::Mapping.send :include, DeviseOtpAuthenticatable::Mapping
9
+ initializer "devise-otp", group: :all do |app|
10
+ ActiveSupport.on_load(:devise_controller) do
11
+ include DeviseOtpAuthenticatable::Controllers::UrlHelpers
12
+ include DeviseOtpAuthenticatable::Controllers::Helpers
13
+ end
14
+
15
+ ActiveSupport.on_load(:action_view) do
16
+ include DeviseOtpAuthenticatable::Controllers::UrlHelpers
17
+ include DeviseOtpAuthenticatable::Controllers::Helpers
18
+ end
19
+
20
+ # See: https://guides.rubyonrails.org/engines.html#separate-assets-and-precompiling
21
+ # check if Rails api mode
22
+ if app.config.respond_to?(:assets)
23
+ if defined?(Sprockets) && Sprockets::VERSION >= "4"
24
+ app.config.assets.precompile << "devise-otp.js"
25
+ else
26
+ # use a proc instead of a string
27
+ app.config.assets.precompile << proc { |path| path == "devise-otp.js" }
28
+ end
29
+ end
21
30
  end
22
31
  end
23
- end
32
+ end
@@ -4,36 +4,36 @@ module DeviseOtpAuthenticatable::Hooks
4
4
  include DeviseOtpAuthenticatable::Controllers::UrlHelpers
5
5
 
6
6
  included do
7
- alias_method_chain :create, :otp
7
+ alias_method :create, :create_with_otp
8
8
  end
9
9
 
10
10
  #
11
11
  # replaces Devise::SessionsController#create
12
12
  #
13
13
  def create_with_otp
14
-
15
14
  resource = warden.authenticate!(auth_options)
16
15
 
16
+ devise_stored_location = stored_location_for(resource) # Grab the current stored location before it gets lost by warden.logout
17
+ store_location_for(resource, devise_stored_location) # Restore it since #stored_location_for removes it
18
+
17
19
  otp_refresh_credentials_for(resource)
18
20
 
19
21
  if otp_challenge_required_on?(resource)
20
22
  challenge = resource.generate_otp_challenge!
21
23
  warden.logout
24
+ store_location_for(resource, devise_stored_location) # restore the stored location
22
25
  respond_with resource, :location => otp_credential_path_for(resource, {:challenge => challenge})
23
-
24
26
  elsif otp_mandatory_on?(resource) # if mandatory, log in user but send him to the must activate otp
25
27
  set_flash_message(:notice, :signed_in_but_otp) if is_navigational_format?
26
28
  sign_in(resource_name, resource)
27
29
  respond_with resource, :location => otp_token_path_for(resource)
28
30
  else
29
-
30
31
  set_flash_message(:notice, :signed_in) if is_navigational_format?
31
32
  sign_in(resource_name, resource)
32
33
  respond_with resource, :location => after_sign_in_path_for(resource)
33
34
  end
34
35
  end
35
36
 
36
-
37
37
  private
38
38
 
39
39
  #
@@ -41,7 +41,8 @@ module DeviseOtpAuthenticatable::Hooks
41
41
  #
42
42
  def otp_challenge_required_on?(resource)
43
43
  return false unless resource.respond_to?(:otp_enabled) && resource.respond_to?(:otp_auth_secret)
44
- resource.otp_enabled && !is_otp_trusted_device_for?(resource)
44
+
45
+ resource.otp_enabled && !is_otp_trusted_browser_for?(resource)
45
46
  end
46
47
 
47
48
  #
@@ -54,4 +55,4 @@ module DeviseOtpAuthenticatable::Hooks
54
55
  resource.otp_mandatory && !resource.otp_enabled
55
56
  end
56
57
  end
57
- end
58
+ end
@@ -5,7 +5,7 @@ module DeviseOtpAuthenticatable
5
5
 
6
6
  class << self
7
7
  def apply
8
- Devise::SessionsController.send(:include, Hooks::Sessions)
8
+ ::Devise::SessionsController.send(:include, Hooks::Sessions)
9
9
  end
10
10
  end
11
11
 
@@ -11,8 +11,9 @@ module Devise::Models
11
11
  end
12
12
 
13
13
  module ClassMethods
14
- ::Devise::Models.config(self, :otp_authentication_timeout, :otp_drift_window,
15
- :otp_mandatory, :otp_credentials_refresh, :otp_uri_application, :recovery_tokens)
14
+ ::Devise::Models.config(self, :otp_authentication_timeout, :otp_drift_window, :otp_trust_persistence,
15
+ :otp_mandatory, :otp_credentials_refresh, :otp_issuer, :otp_recovery_tokens,
16
+ :otp_controller_path)
16
17
 
17
18
  def find_valid_otp_challenge(challenge)
18
19
  with_valid_otp_challenge(Time.now).where(:otp_session_challenge => challenge).first
@@ -20,7 +21,7 @@ module Devise::Models
20
21
  end
21
22
 
22
23
  def time_based_otp
23
- @time_based_otp ||= ROTP::TOTP.new(otp_auth_secret)
24
+ @time_based_otp ||= ROTP::TOTP.new(otp_auth_secret, issuer: "#{self.class.otp_issuer || Rails.application.class.module_parent_name}")
24
25
  end
25
26
 
26
27
  def recovery_otp
@@ -32,7 +33,7 @@ module Devise::Models
32
33
  end
33
34
 
34
35
  def otp_provisioning_identifier
35
- "#{email}/#{self.class.otp_uri_application || Rails.application.class.parent_name}"
36
+ email
36
37
  end
37
38
 
38
39
 
@@ -41,9 +42,9 @@ module Devise::Models
41
42
  @recovery_otp = nil
42
43
  generate_otp_auth_secret
43
44
  reset_otp_persistence
44
- update_columns(:otp_enabled => false, :otp_time_drift => 0,
45
- :otp_session_challenge => nil, :otp_challenge_expires => nil,
46
- :otp_recovery_counter => 0)
45
+ update!(:otp_enabled => false,
46
+ :otp_session_challenge => nil, :otp_challenge_expires => nil,
47
+ :otp_recovery_counter => 0)
47
48
  end
48
49
 
49
50
  def reset_otp_credentials!
@@ -51,7 +52,6 @@ module Devise::Models
51
52
  save!
52
53
  end
53
54
 
54
-
55
55
  def reset_otp_persistence
56
56
  generate_otp_persistence_seed
57
57
  end
@@ -61,9 +61,21 @@ module Devise::Models
61
61
  save!
62
62
  end
63
63
 
64
+ def enable_otp!
65
+ if otp_persistence_seed.nil?
66
+ reset_otp_credentials!
67
+ end
68
+
69
+ update!(:otp_enabled => true, :otp_enabled_on => Time.now)
70
+ end
71
+
72
+ def disable_otp!
73
+ update!(:otp_enabled => false, :otp_enabled_on => nil)
74
+ end
75
+
64
76
  def generate_otp_challenge!(expires = nil)
65
- update_columns(:otp_session_challenge => SecureRandom.hex,
66
- :otp_challenge_expires => DateTime.now + (expires || self.class.otp_authentication_timeout))
77
+ update!(:otp_session_challenge => SecureRandom.hex,
78
+ :otp_challenge_expires => DateTime.now + (expires || self.class.otp_authentication_timeout))
67
79
  otp_session_challenge
68
80
  end
69
81
 
@@ -82,16 +94,12 @@ module Devise::Models
82
94
  alias_method :valid_otp_token?, :validate_otp_token
83
95
 
84
96
  def validate_otp_time_token(token)
85
- if drift = validate_otp_token_with_drift(token)
86
- update_column(:otp_time_drift, drift)
87
- true
88
- else
89
- false
90
- end
97
+ return false if token.blank?
98
+ validate_otp_token_with_drift(token)
91
99
  end
92
100
  alias_method :valid_otp_time_token?, :validate_otp_time_token
93
101
 
94
- def next_otp_recovery_tokens(number = 5)
102
+ def next_otp_recovery_tokens(number = self.class.otp_recovery_tokens)
95
103
  (otp_recovery_counter..otp_recovery_counter + number).inject({}) do |h, index|
96
104
  h[index] = recovery_otp.at(index)
97
105
  h
@@ -108,21 +116,13 @@ module Devise::Models
108
116
 
109
117
 
110
118
 
111
-
112
-
113
119
  private
114
120
 
115
- #
116
- # refactor me, I suck
117
- #
118
121
  def validate_otp_token_with_drift(token)
119
- # valid_vals << ROTP::TOTP.new(otp_auth_secret).at(Time.now)
120
122
 
121
123
  # should be centered around saved drift
122
- (-self.class.otp_drift_window..self.class.otp_drift_window).each do |drift|
123
- return drift if(time_based_otp.verify(token, Time.now.ago(30 * drift)))
124
- end
125
- false
124
+ (-self.class.otp_drift_window..self.class.otp_drift_window).any? {|drift|
125
+ (time_based_otp.verify(token, at: Time.now.ago(30 * drift))) }
126
126
  end
127
127
 
128
128
  def generate_otp_persistence_seed
@@ -135,4 +135,4 @@ module Devise::Models
135
135
  end
136
136
 
137
137
  end
138
- end
138
+ end
@@ -1,30 +1,29 @@
1
1
  module ActionDispatch::Routing
2
2
  class Mapper
3
3
 
4
-
5
4
  protected
6
- #########
7
5
 
8
6
  def devise_otp(mapping, controllers)
9
-
10
7
  namespace :otp, :module => :devise_otp do
11
8
  resource :token, :only => [:show, :update, :destroy],
12
- :path => mapping.path_names[:token], :controller => controllers[:otp_tokens] do
9
+ :path => mapping.path_names[:token], :controller => controllers[:tokens] do
13
10
 
14
- get :persistence, :action => 'get_persistence'
15
- post :persistence, :action => 'clear_persistence'
16
- delete :persistence, :action => 'delete_persistence'
11
+ if Devise.otp_trust_persistence
12
+ get :persistence, :action => 'get_persistence'
13
+ post :persistence, :action => 'clear_persistence'
14
+ delete :persistence, :action => 'delete_persistence'
15
+ end
17
16
 
18
17
  get :recovery
19
18
  end
20
19
 
21
20
  resource :credential, :only => [:show, :update],
22
- :path => mapping.path_names[:credentials], :controller => controllers[:otp_credentials] do
21
+ :path => mapping.path_names[:credentials], :controller => controllers[:credentials] do
23
22
 
24
- get :refresh, :action => 'get_refresh'
23
+ get :refresh, :action => 'get_refresh'
25
24
  put :refresh, :action => 'set_refresh'
26
25
  end
27
26
  end
28
27
  end
29
28
  end
30
- end
29
+ end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  source_root File.expand_path("../templates", __FILE__)
7
7
 
8
8
  def copy_devise_migration
9
- migration_template "migration.rb", "db/migrate/devise_otp_add_to_#{table_name}"
9
+ migration_template "migration.rb", "db/migrate/devise_otp_add_to_#{table_name}.rb"
10
10
  end
11
11
  end
12
12
  end
@@ -6,7 +6,6 @@ class DeviseOtpAddTo<%= table_name.camelize %> < ActiveRecord::Migration
6
6
  t.boolean :otp_enabled, :default => false, :null => false
7
7
  t.boolean :otp_mandatory, :default => false, :null => false
8
8
  t.datetime :otp_enabled_on
9
- t.integer :otp_time_drift, :default => 0, :null => false
10
9
  t.integer :otp_failed_attempts, :default => 0, :null => false
11
10
  t.integer :otp_recovery_counter, :default => 0, :null => false
12
11
  t.string :otp_persistence_seed
@@ -21,7 +20,7 @@ class DeviseOtpAddTo<%= table_name.camelize %> < ActiveRecord::Migration
21
20
  def self.down
22
21
  change_table :<%= table_name %> do |t|
23
22
  t.remove :otp_auth_secret, :otp_recovery_secret, :otp_enabled, :otp_mandatory, :otp_enabled_on, :otp_session_challenge,
24
- :otp_challenge_expires, :otp_time_drift, :otp_failed_attempts, :otp_recovery_counter, :otp_persistence_seed
23
+ :otp_challenge_expires, :otp_failed_attempts, :otp_recovery_counter, :otp_persistence_seed
25
24
 
26
25
  end
27
26
  end
File without changes
@@ -13,11 +13,36 @@ content = <<-CONTENT
13
13
  # ==> Devise OTP Extension
14
14
  # Configure OTP extension for devise
15
15
 
16
- # How long should the user have to enter their token. To change the default, uncomment and change the below:
17
- #config.otp_authentication_timeout = 3.minutes
16
+ # OTP is mandatory, users are going to be asked to
17
+ # enroll OTP the next time they sign in, before they can successfully complete the session establishment.
18
+ # This is the global value, can also be set on each user.
19
+ #config.otp_mandatory = false
20
+
21
+ # Drift: a window which provides allowance for drift between a user's token device clock
22
+ # (and therefore their OTP tokens) and the authentication server's clock.
23
+ # Expressed in minutes centered at the current time. (Note: it's a number, *NOT* 3.minutes )
24
+ #config.otp_drift_window = 3
25
+
26
+ # Users that have logged in longer than this time ago, are going to be asked their password
27
+ # (and an OTP challenge, if enabled) before they can see or change their otp informations.
28
+ #config.otp_credentials_refresh = 15.minutes
29
+
30
+ # Users are given a list of one-time recovery tokens, for emergency access
31
+ # set to false to disable giving recovery tokens.
32
+ #config.otp_recovery_tokens = 10
33
+
34
+ # The user is allowed to set his browser as "trusted", no more OTP challenges will be
35
+ # asked for that browser, for a limited time.
36
+ # set to false to disable setting the browser as trusted
37
+ #config.otp_trust_persistence = 1.month
38
+
39
+ # The name of the token issuer, to be added to the provisioning
40
+ # url. Display will vary based on token application. (defaults to the Rails application class)
41
+ #config.otp_issuer = 'my_application'
42
+
43
+ # Custom view path for Devise OTP controllers
44
+ #config.otp_controller_path = 'devise'
18
45
 
19
- # Change time drift settings for valid token values. To change the default, uncomment and change the below:
20
- #config.otp_authentication_time_drift = 3
21
46
  CONTENT
22
47
 
23
48
  inject_into_file "config/initializers/devise.rb", content, :before => /end[ |\n|]+\Z/
@@ -28,4 +53,4 @@ CONTENT
28
53
  end
29
54
  end
30
55
  end
31
- end
56
+ end
@@ -9,10 +9,9 @@ module DeviseOtp
9
9
  :desc => "The scope to copy views to"
10
10
 
11
11
  include ::Devise::Generators::ViewPathTemplates
12
- source_root File.expand_path("../../../../app/views/devise_otp", __FILE__)
12
+ source_root File.expand_path("../../../../app/views", __FILE__)
13
13
  def copy_views
14
- view_directory :credentials, 'app/views/devise_otp/credentials'
15
- view_directory :tokens, 'app/views/devise_otp/tokens'
14
+ view_directory :devise, 'app/views/devise'
16
15
  end
17
16
  end
18
17
  end
File without changes
data/test/dummy/Rakefile CHANGED
File without changes
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
@@ -11,3 +11,4 @@
11
11
  // GO AFTER THE REQUIRES BELOW.
12
12
  //
13
13
  //= require_tree .
14
+ //= require devise-otp
File without changes
@@ -1,4 +1,4 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  protect_from_forgery
3
- before_filter :authenticate_user!
3
+ before_action :authenticate_user!
4
4
  end
@@ -1,4 +1,6 @@
1
1
  class PostsController < ApplicationController
2
+ before_action :authenticate_user!
3
+
2
4
  # GET /posts
3
5
  # GET /posts.json
4
6
  def index
File without changes
File without changes
File without changes
File without changes
@@ -12,7 +12,7 @@ class User < PARENT_MODEL_CLASS
12
12
  end
13
13
 
14
14
  devise :otp_authenticatable, :database_authenticatable, :registerable,
15
- :recoverable, :rememberable, :trackable, :validatable
15
+ :trackable, :validatable
16
16
 
17
17
  # Setup accessible (or protected) attributes for your model
18
18
  #attr_accessible :otp_enabled, :otp_mandatory, :as => :otp_privileged
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -53,6 +53,8 @@ module Dummy
53
53
  # Enable escaping HTML in JSON.
54
54
  config.active_support.escape_html_entities_in_json = true
55
55
 
56
+ config.active_record.legacy_connection_handling = false
57
+
56
58
  # Use SQL instead of Active Record's schema dumper when creating the database.
57
59
  # This is necessary if your schema can't be completely dumped by the schema dumper,
58
60
  # like if you have constraints or database-specific column types
@@ -65,4 +67,3 @@ module Dummy
65
67
  config.assets.version = '1.0'
66
68
  end
67
69
  end
68
-
File without changes
@@ -14,7 +14,7 @@ development:
14
14
  # Do not set this db to the same as development or production.
15
15
  test:
16
16
  adapter: sqlite3
17
- database: ":memory:"
17
+ database: db/test.sqlite3
18
18
  pool: 5
19
19
  timeout: 5000
20
20
 
File without changes
@@ -22,13 +22,6 @@ Dummy::Application.configure do
22
22
  # Only use best-standards-support built into browsers
23
23
  config.action_dispatch.best_standards_support = :builtin
24
24
 
25
- # Raise exception on mass assignment protection for Active Record models
26
- config.active_record.mass_assignment_sanitizer = :strict
27
-
28
- # Log the query plan for queries taking more than this (works
29
- # with SQLite, MySQL, and PostgreSQL)
30
- config.active_record.auto_explain_threshold_in_seconds = 0.5
31
-
32
25
  # Do not compress assets
33
26
  config.assets.compress = false
34
27
 
@@ -66,8 +66,4 @@ Dummy::Application.configure do
66
66
 
67
67
  # Send deprecation notices to registered listeners
68
68
  config.active_support.deprecation = :notify
69
-
70
- # Log the query plan for queries taking more than this (works
71
- # with SQLite, MySQL, and PostgreSQL)
72
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
73
69
  end
File without changes
File without changes
File without changes