devise-multi-factor 3.1.5

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.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +21 -0
  3. data/.github/workflows/gem-push.yml +42 -0
  4. data/.gitignore +23 -0
  5. data/.rubocop.yml +295 -0
  6. data/.travis.yml +28 -0
  7. data/CHANGELOG.md +119 -0
  8. data/Gemfile +32 -0
  9. data/LICENSE +19 -0
  10. data/README.md +322 -0
  11. data/Rakefile +12 -0
  12. data/app/controllers/devise/totp_controller.rb +79 -0
  13. data/app/controllers/devise/two_factor_authentication_controller.rb +84 -0
  14. data/app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb +3 -0
  15. data/app/views/devise/two_factor_authentication/new.html.erb +14 -0
  16. data/app/views/devise/two_factor_authentication/show.html.erb +19 -0
  17. data/config/locales/de.yml +8 -0
  18. data/config/locales/en.yml +8 -0
  19. data/config/locales/es.yml +8 -0
  20. data/config/locales/fr.yml +8 -0
  21. data/config/locales/ru.yml +8 -0
  22. data/devise-multi-factor.gemspec +40 -0
  23. data/lib/devise-multi-factor.rb +1 -0
  24. data/lib/devise_multi_factor.rb +56 -0
  25. data/lib/devise_multi_factor/controllers/helpers.rb +57 -0
  26. data/lib/devise_multi_factor/hooks/two_factor_authenticatable.rb +17 -0
  27. data/lib/devise_multi_factor/models/totp_enrollable.rb +7 -0
  28. data/lib/devise_multi_factor/models/two_factor_authenticatable.rb +142 -0
  29. data/lib/devise_multi_factor/orm/active_record.rb +14 -0
  30. data/lib/devise_multi_factor/rails.rb +7 -0
  31. data/lib/devise_multi_factor/routes.rb +15 -0
  32. data/lib/devise_multi_factor/schema.rb +23 -0
  33. data/lib/devise_multi_factor/version.rb +3 -0
  34. data/lib/generators/active_record/devise_multi_factor_generator.rb +13 -0
  35. data/lib/generators/active_record/templates/migration.rb +11 -0
  36. data/lib/generators/devise_multi_factor/devise_multi_factor_generator.rb +17 -0
  37. data/spec/controllers/two_factor_authentication_controller_spec.rb +41 -0
  38. data/spec/features/two_factor_authenticatable_spec.rb +237 -0
  39. data/spec/generators/active_record/devise_multi_factor_generator_spec.rb +34 -0
  40. data/spec/lib/devise_multi_factor/models/two_factor_authenticatable_spec.rb +282 -0
  41. data/spec/rails_app/.gitignore +3 -0
  42. data/spec/rails_app/README.md +3 -0
  43. data/spec/rails_app/Rakefile +7 -0
  44. data/spec/rails_app/app/assets/config/manifest.js +2 -0
  45. data/spec/rails_app/app/assets/javascripts/application.js +1 -0
  46. data/spec/rails_app/app/assets/stylesheets/application.css +4 -0
  47. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  48. data/spec/rails_app/app/controllers/home_controller.rb +10 -0
  49. data/spec/rails_app/app/helpers/application_helper.rb +8 -0
  50. data/spec/rails_app/app/mailers/.gitkeep +0 -0
  51. data/spec/rails_app/app/models/.gitkeep +0 -0
  52. data/spec/rails_app/app/models/admin.rb +6 -0
  53. data/spec/rails_app/app/models/encrypted_user.rb +7 -0
  54. data/spec/rails_app/app/models/guest_user.rb +7 -0
  55. data/spec/rails_app/app/models/test_user.rb +38 -0
  56. data/spec/rails_app/app/models/user.rb +18 -0
  57. data/spec/rails_app/app/views/home/dashboard.html.erb +11 -0
  58. data/spec/rails_app/app/views/home/index.html.erb +3 -0
  59. data/spec/rails_app/app/views/layouts/application.html.erb +20 -0
  60. data/spec/rails_app/config.ru +4 -0
  61. data/spec/rails_app/config/application.rb +61 -0
  62. data/spec/rails_app/config/boot.rb +10 -0
  63. data/spec/rails_app/config/database.yml +19 -0
  64. data/spec/rails_app/config/environment.rb +5 -0
  65. data/spec/rails_app/config/environments/development.rb +28 -0
  66. data/spec/rails_app/config/environments/production.rb +68 -0
  67. data/spec/rails_app/config/environments/test.rb +41 -0
  68. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  69. data/spec/rails_app/config/initializers/cookies_serializer.rb +3 -0
  70. data/spec/rails_app/config/initializers/devise.rb +258 -0
  71. data/spec/rails_app/config/initializers/inflections.rb +15 -0
  72. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  73. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  74. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  75. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  76. data/spec/rails_app/config/locales/devise.en.yml +59 -0
  77. data/spec/rails_app/config/locales/en.yml +5 -0
  78. data/spec/rails_app/config/routes.rb +65 -0
  79. data/spec/rails_app/db/migrate/20140403184646_devise_create_users.rb +42 -0
  80. data/spec/rails_app/db/migrate/20140407172619_two_factor_authentication_add_to_users.rb +17 -0
  81. data/spec/rails_app/db/migrate/20140407215513_add_nickanme_to_users.rb +7 -0
  82. data/spec/rails_app/db/migrate/20151224171231_add_encrypted_columns_to_user.rb +7 -0
  83. data/spec/rails_app/db/migrate/20151224180310_populate_otp_column.rb +19 -0
  84. data/spec/rails_app/db/migrate/20151228230340_remove_otp_secret_key_from_user.rb +5 -0
  85. data/spec/rails_app/db/migrate/20160209032439_devise_create_admins.rb +42 -0
  86. data/spec/rails_app/db/schema.rb +55 -0
  87. data/spec/rails_app/lib/assets/.gitkeep +0 -0
  88. data/spec/rails_app/lib/sms_provider.rb +17 -0
  89. data/spec/rails_app/public/404.html +26 -0
  90. data/spec/rails_app/public/422.html +26 -0
  91. data/spec/rails_app/public/500.html +25 -0
  92. data/spec/rails_app/public/favicon.ico +0 -0
  93. data/spec/rails_app/script/rails +6 -0
  94. data/spec/spec_helper.rb +26 -0
  95. data/spec/support/authenticated_model_helper.rb +29 -0
  96. data/spec/support/capybara.rb +3 -0
  97. data/spec/support/controller_helper.rb +16 -0
  98. data/spec/support/features_spec_helper.rb +42 -0
  99. data/spec/support/sms_provider.rb +5 -0
  100. data/spec/support/totp_helper.rb +11 -0
  101. metadata +315 -0
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise_ip_filter.gemspec
4
+ gemspec
5
+
6
+ rails_version = ENV["RAILS_VERSION"] || "default"
7
+
8
+ rails = case rails_version
9
+ when "master"
10
+ {github: "rails/rails"}
11
+ when "default"
12
+ "~> 5.2"
13
+ else
14
+ "~> #{rails_version}"
15
+ end
16
+
17
+ gem "rails", rails
18
+
19
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.2.0')
20
+ gem "test-unit", "~> 3.0"
21
+ end
22
+
23
+ group :test, :development do
24
+ gem 'byebug'
25
+ gem 'lockbox'
26
+ gem 'sqlite3'
27
+ end
28
+
29
+ group :test do
30
+ gem 'rack_session_access'
31
+ gem 'ammeter'
32
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Dmitrii Golub
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,322 @@
1
+ # Devise Multi Factor
2
+
3
+ This is a fork of the gem [two_factor_authentication](https://github.com/Houdini/two_factor_authentication) by Houdini. The name has been changed to `devise-multi-factor` to avoid conflicts of both gems.
4
+ This version uses [Lockbox](https://github.com/ankane/lockbox) to manage the encryption of the key.
5
+
6
+ ## Features
7
+
8
+ * Support for 2 types of OTP codes
9
+ 1. Codes delivered directly to the user
10
+ 2. TOTP (Google Authenticator) codes based on a shared secret (HMAC)
11
+ * Configurable OTP code digit length
12
+ * Configurable max login attempts
13
+ * Customizable logic to determine if a user needs two factor authentication
14
+ * Configurable period where users won't be asked for 2FA again
15
+ * TOTP secret key automatically encrypted in DB
16
+ * Enroll form for TOTP using `:totp_enrollable` on your model
17
+
18
+ ## Configuration
19
+
20
+ ### Initial Setup
21
+
22
+ In a Rails environment, require the gem in your Gemfile:
23
+
24
+ gem 'devise-multi-factor'
25
+
26
+ Once that's done, run:
27
+
28
+ bundle install
29
+
30
+ Note that Ruby 2.1 or greater is required.
31
+
32
+ ### Installation
33
+
34
+ #### Automatic initial setup
35
+
36
+ To set up the model and database migration file automatically, run the
37
+ following command:
38
+
39
+ bundle exec rails g devise_multi_factor MODEL
40
+
41
+ Where MODEL is your model name (e.g. User or Admin). This generator will add
42
+ `:two_factor_authenticatable` to your model's Devise options and create a
43
+ migration in `db/migrate/`, which will add the following columns to your table:
44
+
45
+ - `:second_factor_attempts_count`
46
+ - `:encrypted_otp_secret_key`
47
+ - `:direct_otp`
48
+ - `:direct_otp_sent_at`
49
+ - `:totp_timestamp`
50
+
51
+ #### Manual initial setup
52
+
53
+ If you prefer to set up the model and migration manually, add the
54
+ `:two_factor_authenticatable` option to your existing devise options, such as:
55
+
56
+ ```ruby
57
+ devise :database_authenticatable, :registerable, :recoverable, :rememberable,
58
+ :trackable, :validatable, :two_factor_authenticatable
59
+ ```
60
+
61
+ Then create your migration file using the Rails generator, such as:
62
+
63
+ ```
64
+ rails g migration AddTwoFactorFieldsToUsers second_factor_attempts_count:integer encrypted_otp_secret_key:string direct_otp:string direct_otp_sent_at:datetime totp_timestamp:integer
65
+ ```
66
+
67
+ #### Complete the setup
68
+
69
+ Run the migration with:
70
+
71
+ bundle exec rake db:migrate
72
+
73
+ Add the following line to your model to fully enable two-factor auth:
74
+
75
+ has_one_time_password
76
+
77
+ Set config values in `config/initializers/devise.rb`:
78
+
79
+ ```ruby
80
+ config.max_login_attempts = 3 # Maximum second factor attempts count.
81
+ config.allowed_otp_drift_seconds = 30 # Allowed TOTP time drift between client and server.
82
+ config.otp_length = 6 # TOTP code length
83
+ config.direct_otp_valid_for = 5.minutes # Time before direct OTP becomes invalid
84
+ config.direct_otp_length = 6 # Direct OTP code length
85
+ config.remember_otp_session_for_seconds = 30.days # Time before browser has to perform 2fA again. Default is 0.
86
+ config.otp_secret_encryption_key = ENV['OTP_SECRET_ENCRYPTION_KEY']
87
+ config.second_factor_resource_id = 'id' # Field or method name used to set value for 2fA remember cookie
88
+ config.delete_cookie_on_logout = false # Delete cookie when user signs out, to force 2fA again on login
89
+ ```
90
+ The `otp_secret_encryption_key` must be a random key that is not stored in the
91
+ DB, and is not checked in to your repo. It is recommended to store it in an
92
+ environment variable, and you can generate it with `bundle exec rake secret`.
93
+
94
+ Override the method in your model in order to send direct OTP codes. This is
95
+ automatically called when a user logs in unless they have TOTP enabled (see
96
+ below):
97
+
98
+ ```ruby
99
+ def send_two_factor_authentication_code(code)
100
+ # Send code via SMS, etc.
101
+ end
102
+ ```
103
+
104
+ ### Customisation and Usage
105
+
106
+ By default, second factor authentication is required for each user. You can
107
+ change that by overriding the following method in your model:
108
+
109
+ ```ruby
110
+ def need_two_factor_authentication?(request)
111
+ request.ip != '127.0.0.1'
112
+ end
113
+ ```
114
+
115
+ In the example above, two factor authentication will not be required for local
116
+ users.
117
+
118
+ This gem is compatible with [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en).
119
+ To enable this a shared secret must be generated by invoking the following
120
+ method on your model:
121
+
122
+ ```ruby
123
+ user.generate_totp_secret
124
+ ```
125
+
126
+ This must then be shared via a provisioning uri:
127
+
128
+ ```ruby
129
+ user.provisioning_uri # This assumes a user model with an email attribute
130
+ ```
131
+
132
+ This provisioning uri can then be turned in to a QR code if desired so that
133
+ users may add the app to Google Authenticator easily. Once this is done, they
134
+ may retrieve a one-time password directly from the Google Authenticator app.
135
+
136
+ #### Overriding the view
137
+
138
+ The default view that shows the form can be overridden by adding a
139
+ file named `show.html.erb` (or `show.html.haml` if you prefer HAML)
140
+ inside `app/views/devise/two_factor_authentication/` and customizing it.
141
+ Below is an example using ERB:
142
+
143
+
144
+ ```html
145
+ <h2>Hi, you received a code by email, please enter it below, thanks!</h2>
146
+
147
+ <%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
148
+ <%= text_field_tag :code %>
149
+ <%= submit_tag "Log in!" %>
150
+ <% end %>
151
+
152
+ <%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
153
+ ```
154
+
155
+ #### Upgrading from version 1.X to 2.X
156
+
157
+ The following database fields are new in version 2.
158
+
159
+ - `direct_otp`
160
+ - `direct_otp_sent_at`
161
+ - `totp_timestamp`
162
+
163
+ To add them, generate a migration such as:
164
+
165
+ $ rails g migration AddTwoFactorFieldsToUsers direct_otp:string direct_otp_sent_at:datetime totp_timestamp:timestamp
166
+
167
+ The `otp_secret_key` is only required for users who use TOTP (Google Authenticator) codes,
168
+ so unless it has been shared with the user it should be set to `nil`. The
169
+ following pseudo-code is an example of how this might be done:
170
+
171
+ ```ruby
172
+ User.find_each do |user| do
173
+ if !uses_authenticator_app(user)
174
+ user.otp_secret_key = nil
175
+ user.save!
176
+ end
177
+ end
178
+ ```
179
+
180
+ #### Adding the TOTP encryption option to an existing app
181
+
182
+ If you've already been using this gem, and want to start encrypting the OTP
183
+ secret key in the database (recommended), you'll need to perform the following
184
+ steps:
185
+
186
+ 1. Generate a migration to add the necessary columns to your model's table:
187
+
188
+ ```
189
+ rails g migration AddEncryptionFieldsToUsers encrypted_otp_secret_key:string
190
+ ```
191
+
192
+ Open your migration file (it will be in the `db/migrate` directory and will be
193
+ named something like `20151230163930_add_encryption_fields_to_users.rb`), and
194
+ add `unique: true` to the `add_index` line so that it looks like this:
195
+
196
+ ```ruby
197
+ add_index :users, :encrypted_otp_secret_key, unique: true
198
+ ```
199
+ Save the file.
200
+
201
+ 2. Run the migration: `bundle exec rake db:migrate`
202
+
203
+ 2. Update the gem: `bundle update two_factor_authentication`
204
+
205
+ 3. Add `encrypted: true` to `has_one_time_password` in your model.
206
+ For example: `has_one_time_password(encrypted: true)`
207
+
208
+ 4. Generate a migration to populate the new encryption fields:
209
+ ```
210
+ rails g migration PopulateEncryptedOtpFields
211
+ ```
212
+
213
+ Open the generated file, and replace its contents with the following:
214
+ ```ruby
215
+ class PopulateEncryptedOtpFields < ActiveRecord::Migration
216
+ def up
217
+ User.reset_column_information
218
+
219
+ User.find_each do |user|
220
+ user.otp_secret_key = user.read_attribute('otp_secret_key')
221
+ user.save!
222
+ end
223
+ end
224
+
225
+ def down
226
+ User.reset_column_information
227
+
228
+ User.find_each do |user|
229
+ user.otp_secret_key = ROTP::Base32.random_base32
230
+ user.save!
231
+ end
232
+ end
233
+ end
234
+ ```
235
+
236
+ 5. Generate a migration to remove the `:otp_secret_key` column:
237
+ ```
238
+ rails g migration RemoveOtpSecretKeyFromUsers otp_secret_key:string
239
+ ```
240
+
241
+ 6. Run the migrations: `bundle exec rake db:migrate`
242
+
243
+ If, for some reason, you want to switch back to the old non-encrypted version,
244
+ use these steps:
245
+
246
+ 1. Remove `(encrypted: true)` from `has_one_time_password`
247
+
248
+ 2. Roll back the last 3 migrations (assuming you haven't added any new ones
249
+ after them):
250
+ ```
251
+ bundle exec rake db:rollback STEP=3
252
+ ```
253
+
254
+ #### Critical Security Note! Add before_action to your user registration controllers
255
+
256
+ You should have a file registrations_controller.rb in your controllers folder
257
+ to overwrite/customize user registrations. It should include the lines below, for 2FA protection of user model updates, meaning that users can only access the users/edit page after confirming 2FA fully, not simply by logging in. Otherwise the entire 2FA system can be bypassed!
258
+
259
+ ```ruby
260
+ class RegistrationsController < Devise::RegistrationsController
261
+ before_action :two_factor_authenticate!, except: [:new, :create, :cancel]
262
+ end
263
+ ```
264
+
265
+
266
+ ### Example App
267
+ (This gem is not 100% compatible with this example app)
268
+ [TwoFactorAuthenticationExample](https://github.com/Houdini/TwoFactorAuthenticationExample)
269
+
270
+
271
+ ### Example user actions
272
+
273
+ to use an ENV VAR for the 2FA encryption key:
274
+
275
+ config.otp_secret_encryption_key = ENV['OTP_SECRET_ENCRYPTION_KEY']
276
+
277
+ to set up TOTP for Google Authenticator for user:
278
+
279
+ ```
280
+ current_user.otp_secret_key = current_user.generate_totp_secret
281
+ current_user.save!
282
+ ```
283
+
284
+ ( encrypted db fields are set upon user model save action,
285
+ rails c access relies on setting env var: OTP_SECRET_ENCRYPTION_KEY )
286
+
287
+ to check if user has input the correct code (from the QR display page)
288
+ before saving the user model:
289
+
290
+ ```
291
+ current_user.authenticate_totp('123456')
292
+ ```
293
+
294
+ additional note:
295
+
296
+ ```
297
+ current_user.otp_secret_key
298
+ ```
299
+
300
+ This returns the OTP secret key in plaintext for the user (if you have set the env var) in the console
301
+ the string used for generating the QR given to the user for their Google Auth is something like:
302
+
303
+ otpauth://totp/LABEL?secret=p6wwetjnkjnrcmpd (example secret used here)
304
+
305
+ where LABEL should be something like "example.com (Username)", which shows up in their GA app to remind them the code is for example.com
306
+
307
+ this returns true or false with an allowed_otp_drift_seconds 'grace period'
308
+
309
+ to set TOTP to DISABLED for a user account:
310
+
311
+ ```
312
+ current_user.second_factor_attempts_count=nil
313
+ current_user.encrypted_otp_secret_key=nil
314
+ current_user.direct_otp=nil
315
+ current_user.direct_otp_sent_at=nil
316
+ current_user.totp_timestamp=nil
317
+ current_user.direct_otp=nil
318
+ current_user.otp_secret_key=nil
319
+ current_user.save! (if in ruby code instead of console)
320
+ current_user.direct_otp? => false
321
+ current_user.totp_enabled? => false
322
+ ```
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ APP_RAKEFILE = File.expand_path("../spec/rails_app/Rakefile", __FILE__)
4
+ load 'rails/tasks/engine.rake'
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ desc "Run all specs in spec directory (excluding plugin specs)"
9
+ task :default => :spec
10
+
11
+ # To test against a specific version of Rails
12
+ # export RAILS_VERSION=3.2.0; bundle update; rake
@@ -0,0 +1,79 @@
1
+ require 'devise/version'
2
+
3
+ class Devise::TotpController < DeviseController
4
+ prepend_before_action :authenticate_scope!
5
+ before_action :two_factor_authenticate!
6
+
7
+ def new
8
+ @otp_secret = resource.generate_totp_secret
9
+ @otp_secret_signature = sign_otp_secret(@otp_secret)
10
+ render_enroll_form
11
+ end
12
+
13
+ def create
14
+ @otp_secret_signature = params[:otp_secret_signature]
15
+ @otp_secret = verify_otp_secret(@otp_secret_signature)
16
+ if resource.enroll_totp!(@otp_secret, params[:otp_attempt])
17
+ after_two_factor_enroll_success_for(resource)
18
+ else
19
+ flash.now[:error] = 'The authenticator code provided was invalid!'
20
+ render_enroll_form
21
+ end
22
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
23
+ redirect_to send("new_#{resource_name}_two_factor_authentication_path"), flash: { error: 'There has been a problem in the configuration process, please try again.' }
24
+ end
25
+
26
+ def show
27
+ end
28
+
29
+ def destroy
30
+ end
31
+
32
+ private
33
+
34
+ def generate_qr_code(otp_secret)
35
+ return unless defined?(::RQRCode)
36
+
37
+ qr_code = RQRCode::QRCode
38
+ .new(resource.provisioning_uri(nil, otp_secret_key: @otp_secret, issuer: Devise.otp_issuer))
39
+ .as_png(resize_exactly_to: 246)
40
+ .to_data_url
41
+ end
42
+
43
+ def render_enroll_form
44
+ @qr_code = generate_qr_code(@otp_secret)
45
+ render :new
46
+ end
47
+
48
+ def verifier
49
+ ActiveSupport::MessageVerifier.new(Devise.secret_key, digest: 'SHA256')
50
+ end
51
+
52
+ def verify_otp_secret(otp_secret_signature)
53
+ data = verifier.verify(otp_secret_signature)
54
+ valid = data[Devise.second_factor_resource_id] == resource[Devise.second_factor_resource_id]
55
+ raise ActiveSupport::MessageVerifier::InvalidSignature unless valid
56
+
57
+ data['otp_secret']
58
+ end
59
+
60
+ def sign_otp_secret(otp_secret)
61
+ data = {
62
+ Devise.second_factor_resource_id => resource[Devise.second_factor_resource_id],
63
+ 'otp_secret' => otp_secret,
64
+ }
65
+ verifier.generate(data)
66
+ end
67
+
68
+ def after_two_factor_enroll_success_for(resource)
69
+ redirect_to after_two_factor_enroll_success_path_for(resource), flash: { success: 'Multi-factor authentication successfully setup!' }
70
+ end
71
+
72
+ def after_two_factor_enroll_success_path_for(resource)
73
+ :root
74
+ end
75
+
76
+ def authenticate_scope!
77
+ self.resource = send("current_#{resource_name}")
78
+ end
79
+ end