devise-otp 1.1.0 → 2.1.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.erb_lint.yml +30 -0
  3. data/.github/workflows/ci.yml +4 -2
  4. data/.rubocop.yml +18 -0
  5. data/Appraisals +4 -17
  6. data/CHANGELOG.md +59 -1
  7. data/Gemfile +7 -1
  8. data/README.md +37 -14
  9. data/app/controllers/devise_otp/devise/otp_credentials_controller.rb +30 -7
  10. data/app/controllers/devise_otp/devise/otp_persistence_controller.rb +60 -0
  11. data/app/controllers/devise_otp/devise/otp_tokens_controller.rb +2 -37
  12. data/app/views/devise/otp_credentials/refresh.html.erb +6 -6
  13. data/app/views/devise/otp_credentials/show.html.erb +10 -13
  14. data/app/views/devise/otp_tokens/_token_secret.html.erb +9 -9
  15. data/app/views/devise/otp_tokens/_trusted_devices.html.erb +7 -7
  16. data/app/views/devise/otp_tokens/edit.html.erb +9 -9
  17. data/app/views/devise/otp_tokens/recovery.html.erb +8 -6
  18. data/app/views/devise/otp_tokens/show.html.erb +6 -6
  19. data/bin/appraisal +16 -0
  20. data/bin/erb_lint +16 -0
  21. data/bin/rake +16 -0
  22. data/bin/rubocop +16 -0
  23. data/config/locales/{en.yml → devise-otp.en.yml} +10 -8
  24. data/devise-otp.gemspec +2 -2
  25. data/gemfiles/rails_8.0.gemfile +5 -1
  26. data/gemfiles/{rails_7.1.gemfile → rails_8.1.gemfile} +7 -7
  27. data/lib/devise/strategies/database_authenticatable.rb +4 -17
  28. data/lib/devise-otp/version.rb +1 -1
  29. data/lib/devise-otp.rb +1 -2
  30. data/lib/devise_otp_authenticatable/controllers/helpers.rb +1 -2
  31. data/lib/devise_otp_authenticatable/controllers/public_helpers.rb +1 -2
  32. data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +7 -2
  33. data/lib/devise_otp_authenticatable/hooks/refreshable.rb +0 -1
  34. data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +18 -12
  35. data/lib/devise_otp_authenticatable/routes.rb +7 -7
  36. data/test/dummy/app/assets/stylesheets/application.css +15 -1
  37. data/test/dummy/app/controllers/admin_posts_controller.rb +0 -1
  38. data/test/dummy/app/controllers/base_controller.rb +0 -2
  39. data/test/dummy/app/controllers/non_otp_posts_controller.rb +0 -1
  40. data/test/dummy/app/models/admin.rb +0 -1
  41. data/test/dummy/app/models/lockable_user.rb +8 -0
  42. data/test/dummy/app/models/non_otp_user.rb +0 -1
  43. data/test/dummy/app/models/rememberable_user.rb +8 -0
  44. data/test/dummy/app/views/layouts/application.html.erb +4 -2
  45. data/test/dummy/app/views/posts/show.html.erb +0 -1
  46. data/test/dummy/app/views/shared/_navbar.html.erb +23 -0
  47. data/test/dummy/config/application.rb +3 -0
  48. data/test/dummy/config/initializers/devise.rb +2 -2
  49. data/test/dummy/config/routes.rb +3 -0
  50. data/test/dummy/db/migrate/20250731000001_create_lockable_users.rb +9 -0
  51. data/test/dummy/db/migrate/20250731000002_add_devise_to_lockable_users.rb +52 -0
  52. data/test/dummy/db/migrate/20250731000003_devise_otp_add_to_lockable_users.rb +28 -0
  53. data/test/dummy/db/migrate/20250817221304_create_rememberable_users.rb +50 -0
  54. data/test/dummy/db/migrate/20250818030305_add_devise_otp_to_rememberable_users.rb +28 -0
  55. data/test/dummy/db/schema.rb +110 -44
  56. data/test/integration/disable_token_test.rb +0 -2
  57. data/test/integration/enable_otp_form_test.rb +12 -1
  58. data/test/integration/lockable_test.rb +143 -0
  59. data/test/integration/non_otp_user_models_test.rb +0 -2
  60. data/test/integration/otp_drift_test.rb +35 -0
  61. data/test/integration/persistence_test.rb +96 -5
  62. data/test/integration/refresh_test.rb +23 -14
  63. data/test/integration/rememberable_test.rb +143 -0
  64. data/test/integration/reset_token_test.rb +0 -2
  65. data/test/integration/sign_in_test.rb +15 -8
  66. data/test/integration/trackable_test.rb +0 -2
  67. data/test/integration_tests_helper.rb +22 -1
  68. data/test/models/otp_authenticatable_test.rb +48 -11
  69. data/test/test_helper.rb +1 -0
  70. metadata +26 -9
  71. data/gemfiles/rails_7.2.gemfile +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f329ff1fa9732961646ad4169f89921f3429a6aa86744486eafa21a891fd1628
4
- data.tar.gz: 8a4797664986ea6c11e21a50a43d5aeda471000ce5610e91451ec37f3d231121
3
+ metadata.gz: d19fbc8b7c55dd43387e81ac13a7f1807f1a5c700b987c97d99f37985984831c
4
+ data.tar.gz: 8a396782dc0fd10ab4fe45a246f5a7460af6144de7b92834dc6ff755fb5afe43
5
5
  SHA512:
6
- metadata.gz: e42b34ea4259e698e75f6408b6d0a0021b39f934d960e039a305ee2f9dc7d443a8dbdc9f99e1df452b75602430e601bf7ece1214b11766ba558fb3806d07355c
7
- data.tar.gz: 8c1bde0740a5f96dfc440e976c568a8ef0b7f0ac91e7af620d14aea1e8208811e6339384dc9d7b6363c6f4ed9de363c1637422a82b5f5f5c6696fa17c9879764
6
+ metadata.gz: 5e92163f98f4ffbf5ee3f649991df5fc40df6f2531fdf7d3d1081d7174e36598e4751de6b258ed10287da5853ff02ea87170c11c9b62122cc6fd5ffec35cd59d
7
+ data.tar.gz: f2640e640e6c72a299c78bf87e7ae6dea9137f2d0fdf9a0a9e0345edac6c4858418f6e4285357ae6266ac3bbeeb1c1c0c65fd23b3554d7b19369d2232f58b58b
data/.erb_lint.yml ADDED
@@ -0,0 +1,30 @@
1
+ ---
2
+ EnableDefaultLinters: true
3
+ glob: "**/*.{html,text,js}{+*,}.erb"
4
+ linters:
5
+ # ErbSafety:
6
+ # enabled: true
7
+ # better_html_config: .better-html.yml
8
+ SelfClosingTag:
9
+ enabled: false
10
+ Rubocop:
11
+ enabled: true
12
+ rubocop_config:
13
+ inherit_from:
14
+ - .rubocop.yml
15
+ Layout/InitialIndentation:
16
+ Enabled: false
17
+ Layout/LineLength:
18
+ Enabled: false
19
+ Layout/TrailingEmptyLines:
20
+ Enabled: false
21
+ Layout/TrailingWhitespace:
22
+ Enabled: false
23
+ Naming/FileName:
24
+ Enabled: false
25
+ Style/FrozenStringLiteralComment:
26
+ Enabled: false
27
+ Lint/UselessAssignment:
28
+ Enabled: false
29
+ Rails/OutputSafety:
30
+ Enabled: false
@@ -12,17 +12,19 @@ jobs:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  ruby:
15
+ - '4.0'
15
16
  - '3.4'
16
17
  - '3.3'
17
18
  - '3.2'
18
19
  - 'head'
19
20
  rails:
21
+ - rails_8.1
20
22
  - rails_8.0
21
- - rails_7.2
22
- - rails_7.1
23
23
  exclude:
24
24
  - ruby: '3.1'
25
25
  rails: 'rails_8.0'
26
+ - ruby: '3.1'
27
+ rails: 'rails_8.1'
26
28
 
27
29
  env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
28
30
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.rails }}.gemfile
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ # Omakase Ruby styling for Rails
2
+ inherit_gem:
3
+ rubocop-rails-omakase: rubocop.yml
4
+
5
+ # Excluded directories
6
+ AllCops:
7
+ Exclude:
8
+ - "lib/generators/active_record/templates/migration.rb"
9
+
10
+ # Disabled checks
11
+ Style/StringLiterals:
12
+ Enabled: false
13
+
14
+ Layout/SpaceInsideArrayLiteralBrackets:
15
+ Enabled: false
16
+
17
+ Layout/SpaceInsideHashLiteralBraces:
18
+ Enabled: false
data/Appraisals CHANGED
@@ -1,22 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise 'rails_7.1' do
4
- gem 'rails', '~> 7.1.0'
5
- gem 'sqlite3', '~> 1.5.0'
6
-
7
- # Fix:
8
- # warning: logger was loaded from the standard library, but will no longer be part of the default gems since Ruby 3.5.0.
9
- # Add logger to your Gemfile or gemspec.
10
- install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") }' do
11
- gem 'logger'
12
- end
13
- end
14
-
15
- appraise 'rails_7.2' do
16
- gem 'rails', '~> 7.2.0'
17
- gem 'sqlite3', '~> 1.5.0'
18
- end
19
-
20
3
  appraise 'rails_8.0' do
21
4
  gem 'rails', '~> 8.0.0'
22
5
  end
6
+
7
+ appraise 'rails_8.1' do
8
+ gem 'rails', '~> 8.1.0'
9
+ end
data/CHANGELOG.md CHANGED
@@ -1,6 +1,64 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## v2.1.0
4
+
5
+ Bug fixes
6
+ - Fix issue with refreshing credentials when enabling browser persistence;
7
+ - Add back link from "View recovery codes" page;
8
+
9
+ Improvements
10
+ - Simplify "View recovery codes" verbiage;
11
+ - Move locale file to devise-otp.en.yml;
12
+
13
+ Maintenance
14
+ - Disable tests for Rails 7.1 (EOL on 10/2025) and 7.2 (EOL on 8/9/2026)
15
+
16
+ Documentation
17
+ - Switch to native CSS import in README for Propshaft compatibility;
18
+ - Add Getting Started section to README with sample link to OTP Token page;
19
+
20
+ ## v2.0.0
21
+
22
+ Bug fixes:
23
+ - Fixed an issue where Turbo/Hotwire enabled applications return a JS error for failed OTP authentication;
24
+ - Fixed an issue with the "Remember me" functionality for users with OTP enabled;
25
+
26
+ Improvements:
27
+ - Add support for Lockable strategy to OTP credentials form;
28
+ - Use locales for "Enabled/Disabled" status;
29
+ - Fix spelling, spacing, and grammatical issues in the default locales file;
30
+
31
+ Code Quality:
32
+ - Use built-in functionality from rotp gem for handling the TOTP drift window;
33
+ - Use standard URL Helpers in database\_authenticatable;
34
+ - Refactor code for otp\_issuer method;
35
+ - Cleanup Ruby syntax in ERB views;
36
+ - Add Timecop for testing time based functionality;
37
+ - Add Rubocop and ERB Lint;
38
+
39
+ Maintenance
40
+ - Bump rqrcode to 3.0.0
41
+
42
+ Breaking changes
43
+ - Move browser persistence actions to dedicated controller
44
+ - Standardize browser persistence routes, and use buttons for controls
45
+
46
+ ### Upgrading
47
+
48
+ If you have customized the otp_tokens controller/views, or the locales file for browser persistence:
49
+ 1. Regenerate the "otp_tokens" controller and views
50
+
51
+ ```
52
+ rails g devise_otp:controllers
53
+ rails g devise_otp:views
54
+ ```
55
+
56
+ 2. Reapply any desired changes
57
+ - Note that the browser persistence controls now use "button_to", rather than "link_to"
58
+
59
+ 3. Update your locales file
60
+ - Move any \*\_persistence keys from devise.otp.otp_tokens/ to devise.otp.otp_persistence/
61
+
4
62
 
5
63
  ## 1.1.0
6
64
 
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
  gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
7
7
 
8
8
  gem "capybara"
9
+ gem "debug"
9
10
  gem "minitest-reporters", ">= 0.5.0"
10
11
  gem "puma"
11
12
  gem "rake"
@@ -13,4 +14,9 @@ gem "rdoc"
13
14
  gem "shoulda"
14
15
  gem "sprockets-rails"
15
16
  gem "sqlite3", "~> 2.1"
16
- gem "standardrb"
17
+ gem "timecop", "~> 0.9.10"
18
+
19
+ # Formatting gems
20
+ gem "erb_lint", require: false
21
+ gem "standardrb", require: false
22
+ gem "rubocop-rails-omakase", require: false
data/README.md CHANGED
@@ -50,23 +50,31 @@ Run the following generator to add the necessary configuration options to Devise
50
50
 
51
51
  rails g devise_otp:install
52
52
 
53
- After you've created your Devise user models (which is usually done with a `rails g devise MODEL`), set up your Devise OTP additions:
53
+ ## Getting Started
54
+
55
+ Create your Devise user model if you have not done so
56
+
57
+ rails g devise MODEL
58
+
59
+ Add Devise OTP to the model
54
60
 
55
61
  rails g devise_otp MODEL
56
62
 
57
- Don't forget to migrate:
63
+ Add a link to the OTP Token settings to your navigation menu (or equivalent) for the user model
58
64
 
59
- rake db:migrate
65
+ <%= link_to "OTP Token", user_otp_token_path %>
60
66
 
61
- ### Default CSS
67
+ NOTE: replace "user" with the lowercase name of the model (same as the main devise gem)
62
68
 
63
- To use the default CSS for devise-otp, just require the devise-otp.css file as usual in your application.css file (or equivalent):
69
+ ## Default CSS
64
70
 
65
- *= require devise-otp
71
+ To use the default CSS for devise-otp, just import the devise-otp.css file into your application.css file (or equivalent):
66
72
 
67
- It might be even easier to just copy the styles to your project.
73
+ @import url("devise-otp.css");
68
74
 
69
- ### Custom views
75
+ NOTE: You can also copy/paste the styles from [devise-otp.css](https://github.com/wmlele/devise-otp/blob/master/app/assets/stylesheets/devise-otp.css) into your stylesheet.
76
+
77
+ ## Custom views
70
78
 
71
79
  If you want to customise your views, you can use the following generator to eject the default view files:
72
80
 
@@ -74,11 +82,11 @@ If you want to customise your views, you can use the following generator to ejec
74
82
 
75
83
  By default, the files live within the Devise namespace (`app/views/devise`, but if you want to move them or want to match the Devise configuration, set `config.otp_controller_path` in your initializers.
76
84
 
77
- ### I18n
85
+ ## I18n
78
86
 
79
87
  The install generator also installs an english copy of a Devise OTP i18n file. This can be modified (or used to create other language versions) and is located at: _config/locales/devise.otp.en.yml_
80
88
 
81
- ### QR codes
89
+ ## QR codes
82
90
 
83
91
  Devise OTP generates QR Codes directly as SVG's via the [rqrcode](https://github.com/whomwah/rqrcode), so there are no JavaScript (or Sprockets) dependencies.
84
92
 
@@ -88,7 +96,7 @@ The install generator adds some options to the end of your Devise config file (`
88
96
 
89
97
  * `config.otp_mandatory`: OTP is mandatory, users are going to be asked to enroll the next time they sign in, before they can successfully complete the session establishment.
90
98
  * `config.otp_authentication_timeout`: How long the user has to authenticate with their token. (defaults to `3.minutes`)
91
- * `config.otp_drift_window`: A window which provides allowance for drift between a user's token device clock (and therefore their OTP tokens) and the authentication server's clock. Expressed in minutes centered at the current time. (default: `3`)
99
+ * `config.otp_drift_window`: A window which provides allowance for drift between a user's token device clock (and therefore their OTP tokens) and the authentication server's clock. Expressed in "steps" of 30 second increments. (default: `3`)
92
100
  * `config.otp_credentials_refresh`: Users that have logged in longer than this time ago, are going to be asked their password (and an OTP challenge, if enabled) before they can see or change their otp informations. (defaults to `15.minutes`)
93
101
  * `config.otp_recovery_tokens`: Whether the users are given a list of one-time recovery tokens, for emergency access (default: `10`, set to `false` to disable)
94
102
  * `config.otp_trust_persistence`: The user is allowed to set his browser as "trusted", no more OTP challenges will be asked for that browser, for a limited time. (default: `1.month`, set to false to disable setting the browser as trusted)
@@ -110,10 +118,25 @@ To run the devise-otp dummy application in the development environment:
110
118
  - Run the rails console or server (e.g. "rails c")
111
119
 
112
120
  To run the tests for devise-otp against your current Ruby/Rails configuration:
113
- - Navigate to the dummy app directory ("cd test/dummy")
114
- - Create and migrate the database for the test environment ("RAILS\_ENV=test rails db:drop db:create db:migrate")
121
+ - Navigate to the dummy app directory
122
+
123
+ cd test/dummy
124
+
125
+ - Create and migrate the database for the test environment
126
+
127
+ RAILS\_ENV=test rails db:drop
128
+ RAILS\_ENV=test rails db:create
129
+ RAILS\_ENV=test rails db:migrate
130
+
115
131
  - Return to the root directory of devise-otp
116
- - Run "rake test"
132
+
133
+ - Execute the tests
134
+
135
+ rake test
136
+
137
+ To check Ruby syntax and formatting:
138
+ - Rubocop: "./bin/rubocop"
139
+ - ERB Lint (for views): "./bin/erb\_lint -a --lint-all"
117
140
 
118
141
  ## Authors
119
142
 
@@ -1,11 +1,13 @@
1
1
  module DeviseOtp
2
2
  module Devise
3
3
  class OtpCredentialsController < DeviseController
4
+ include ::Devise::Controllers::Rememberable
4
5
  helper_method :new_session_path
5
6
 
6
7
  prepend_before_action :authenticate_scope!, only: [:get_refresh, :set_refresh]
7
8
  prepend_before_action :require_no_authentication, only: [:show, :update]
8
9
  before_action :set_challenge, only: [:show, :update]
10
+ before_action :set_remember_me, only: [:show, :update]
9
11
  before_action :set_recovery, only: [:show, :update]
10
12
  before_action :set_resource, only: [:show, :update]
11
13
  before_action :set_token, only: [:update]
@@ -26,16 +28,31 @@ module DeviseOtp
26
28
  # signs the resource in, if the OTP token is valid and the user has a valid challenge
27
29
  #
28
30
  def update
29
- if resource.otp_challenge_valid? && resource.validate_otp_token(@token, @recovery)
31
+ # The valid_for_authentication? method must be executed with the validation result as
32
+ # a block (as the DatabaseAuthenticatable strategy does via the validate method of the
33
+ # Authenticatable strategy). If true, and the account is not locked, then
34
+ # authentication will proceed as normal. If false, then the valid_for_authentication?
35
+ # method will increment the failed attempts and/or lock the account as specified.
36
+
37
+ if resource.valid_for_authentication? { resource.validate_otp_token(@token, @recovery) }
30
38
  sign_in(resource_name, resource)
31
39
 
32
- otp_set_trusted_device_for(resource) if params[:enable_persistence] == "true"
40
+ remember_me(resource) if resource.devise_modules.include?(:rememberable) and @remember_me
33
41
  otp_refresh_credentials_for(resource)
34
42
  respond_with resource, location: after_sign_in_path_for(resource)
43
+ elsif resource.devise_modules.include?(:lockable) and resource.access_locked?
44
+ otp_set_flash_message :alert, resource.unauthenticated_message, scope: "devise.failure"
45
+ redirect_to new_session_path(resource_name)
35
46
  else
36
- kind = (@token.blank? ? :token_blank : :token_invalid)
37
- otp_set_flash_message :alert, kind, :now => true
38
- render :show
47
+ if resource.devise_modules.include?(:lockable) and resource.unauthenticated_message == :last_attempt
48
+ otp_set_flash_message :alert, :last_attempt, scope: "devise.failure", now: true
49
+ elsif @token.blank?
50
+ otp_set_flash_message :alert, :token_blank, now: true
51
+ else
52
+ otp_set_flash_message :alert, :token_invalid, now: true
53
+ end
54
+
55
+ render :show, status: :unprocessable_entity
39
56
  end
40
57
  end
41
58
 
@@ -70,6 +87,10 @@ module DeviseOtp
70
87
  end
71
88
  end
72
89
 
90
+ def set_remember_me
91
+ @remember_me = (params[:remember_me] == "true")
92
+ end
93
+
73
94
  def set_recovery
74
95
  @recovery = (recovery_enabled? && params[:recovery] == "true")
75
96
  end
@@ -90,6 +111,8 @@ module DeviseOtp
90
111
  def skip_challenge_if_trusted_browser
91
112
  if is_otp_trusted_browser_for?(resource)
92
113
  sign_in(resource_name, resource)
114
+
115
+ remember_me(resource) if resource.devise_modules.include?(:rememberable) and @remember_me
93
116
  otp_refresh_credentials_for(resource)
94
117
  redirect_to after_sign_in_path_for(resource)
95
118
  end
@@ -101,8 +124,8 @@ module DeviseOtp
101
124
  end
102
125
 
103
126
  def failed_refresh
104
- otp_set_flash_message :alert, :invalid_refresh, :now => true
105
- render :refresh
127
+ otp_set_flash_message :alert, :invalid_refresh, now: true
128
+ render :refresh, status: :unprocessable_entity
106
129
  end
107
130
 
108
131
  def self.controller_path
@@ -0,0 +1,60 @@
1
+ module DeviseOtp
2
+ module Devise
3
+ class OtpPersistenceController < DeviseController
4
+ include ::Devise::Controllers::Helpers
5
+
6
+ prepend_before_action :ensure_credentials_refresh
7
+ prepend_before_action :authenticate_scope!
8
+
9
+ #
10
+ # fallback in case of redirect after authentication
11
+ #
12
+ def show
13
+ redirect_to otp_token_path_for(resource)
14
+ end
15
+
16
+ #
17
+ # makes the current browser persistent
18
+ #
19
+ def create
20
+ if otp_set_trusted_device_for(resource)
21
+ otp_set_flash_message :success, :successfully_set_persistence
22
+ end
23
+
24
+ redirect_to otp_token_path_for(resource)
25
+ end
26
+
27
+ #
28
+ # clears persistence for the current browser
29
+ #
30
+ def destroy
31
+ if otp_clear_trusted_device_for(resource)
32
+ otp_set_flash_message :success, :successfully_cleared_persistence
33
+ end
34
+
35
+ redirect_to otp_token_path_for(resource)
36
+ end
37
+
38
+ #
39
+ # rehash the persistence secret, thus, making all the persistence cookies invalid
40
+ #
41
+ def reset
42
+ if otp_reset_persistence_for(resource)
43
+ otp_set_flash_message :notice, :successfully_reset_persistence
44
+ end
45
+
46
+ redirect_to otp_token_path_for(resource)
47
+ end
48
+
49
+ private
50
+
51
+ def ensure_credentials_refresh
52
+ ensure_resource!
53
+
54
+ if needs_credentials_refresh?(resource)
55
+ redirect_to refresh_otp_credential_path_for(resource)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -6,8 +6,6 @@ module DeviseOtp
6
6
  prepend_before_action :ensure_credentials_refresh
7
7
  prepend_before_action :authenticate_scope!
8
8
 
9
- protect_from_forgery except: [:clear_persistence, :delete_persistence]
10
-
11
9
  #
12
10
  # Displays the status of OTP authentication
13
11
  #
@@ -35,8 +33,8 @@ module DeviseOtp
35
33
  otp_set_flash_message :success, :successfully_updated
36
34
  redirect_to otp_token_path_for(resource)
37
35
  else
38
- otp_set_flash_message :danger, :could_not_confirm, :now => true
39
- render :edit
36
+ otp_set_flash_message :danger, :could_not_confirm, now: true
37
+ render :edit, status: :unprocessable_entity
40
38
  end
41
39
  end
42
40
 
@@ -51,39 +49,6 @@ module DeviseOtp
51
49
  redirect_to otp_token_path_for(resource)
52
50
  end
53
51
 
54
- #
55
- # makes the current browser persistent
56
- #
57
- def get_persistence
58
- if otp_set_trusted_device_for(resource)
59
- otp_set_flash_message :success, :successfully_set_persistence
60
- end
61
-
62
- redirect_to otp_token_path_for(resource)
63
- end
64
-
65
- #
66
- # clears persistence for the current browser
67
- #
68
- def clear_persistence
69
- if otp_clear_trusted_device_for(resource)
70
- otp_set_flash_message :success, :successfully_cleared_persistence
71
- end
72
-
73
- redirect_to otp_token_path_for(resource)
74
- end
75
-
76
- #
77
- # rehash the persistence secret, thus, making all the persistence cookies invalid
78
- #
79
- def delete_persistence
80
- if otp_reset_persistence_for(resource)
81
- otp_set_flash_message :notice, :successfully_reset_persistence
82
- end
83
-
84
- redirect_to otp_token_path_for(resource)
85
- end
86
-
87
52
  def recovery
88
53
  respond_to do |format|
89
54
  format.html
@@ -1,19 +1,19 @@
1
- <h2><%= I18n.t('title', :scope => 'devise.otp.credentials_refresh') %></h2>
2
- <p><%= I18n.t('explain', :scope => 'devise.otp.credentials_refresh') %></p>
1
+ <h2><%= t('title', scope: 'devise.otp.credentials_refresh') %></h2>
2
+ <p><%= t('explain', scope: 'devise.otp.credentials_refresh') %></p>
3
3
 
4
- <%= form_for(resource, :as => resource_name, :url => [:refresh, resource_name, :otp_credential], :html => { :method => :put, "data-turbo" => false }) do |f| %>
4
+ <%= form_for(resource, as: resource_name, url: [:refresh, resource_name, :otp_credential], html: { method: :put }) do |f| %>
5
5
 
6
6
  <%= render "devise/shared/error_messages", resource: resource %>
7
7
 
8
8
  <div>
9
9
  <%= f.label :email %><br />
10
- <%= f.text_field :email, :disabled => :true%>
10
+ <%= f.text_field :email, disabled: true %>
11
11
  </div>
12
12
 
13
13
  <div>
14
14
  <%= f.label :password %><br />
15
- <%= f.password_field :refresh_password, :autocomplete => :off, :autofocus => true %>
15
+ <%= f.password_field :refresh_password, autocomplete: :off, autofocus: true %>
16
16
  </div>
17
17
 
18
- <div><%= f.submit I18n.t(:go_on, :scope => 'devise.otp.credentials_refresh') %></div>
18
+ <div><%= f.submit t(:go_on, scope: 'devise.otp.credentials_refresh') %></div>
19
19
  <% end %>
@@ -1,31 +1,28 @@
1
- <h2><%= I18n.t('title', :scope => 'devise.otp.submit_token') %></h2>
2
- <p><%= I18n.t('explain', :scope => 'devise.otp.submit_token') %></p>
1
+ <h2><%= t('title', scope: 'devise.otp.submit_token') %></h2>
2
+ <p><%= t('explain', scope: 'devise.otp.submit_token') %></p>
3
3
 
4
- <%= form_for(resource, :as => resource_name, :url => [resource_name, :otp_credential], :html => { :method => :put, "data-turbo" => false }) do |f| %>
4
+ <%= form_for(resource, as: resource_name, url: [resource_name, :otp_credential], html: { method: :put }) do |f| %>
5
5
 
6
6
  <%= hidden_field_tag :challenge, @challenge %>
7
7
  <%= hidden_field_tag :recovery, @recovery %>
8
+ <%= hidden_field_tag :remember_me, @remember_me %>
8
9
 
9
10
  <% if @recovery %>
10
11
  <p>
11
- <%= label_tag :token, I18n.t('recovery_prompt', :scope => 'devise.otp.submit_token') %><br />
12
- <%= text_field_tag :otp_recovery_counter, resource.otp_recovery_counter, :autocomplete => :off, :disabled => true, :size => 4 %>
12
+ <%= label_tag :token, t('recovery_prompt', scope: 'devise.otp.submit_token') %><br />
13
+ <%= text_field_tag :otp_recovery_counter, resource.otp_recovery_counter, autocomplete: :off, disabled: true, size: 4 %>
13
14
  </p>
14
15
  <% else %>
15
16
  <p>
16
- <%= label_tag :token, I18n.t('prompt', :scope => 'devise.otp.submit_token') %><br />
17
+ <%= label_tag :token, t('prompt', scope: 'devise.otp.submit_token') %><br />
17
18
  </p>
18
19
  <% end %>
19
20
 
20
- <%= text_field_tag :token, nil, :autocomplete => :off, :autofocus => true, :size => 6 %><br>
21
+ <%= text_field_tag :token, nil, autocomplete: :off, autofocus: true, size: 6 %><br>
21
22
 
22
- <%= label_tag :enable_persistence do %>
23
- <%= check_box_tag :enable_persistence, true, false %> <%= I18n.t('remember', :scope => 'devise.otp.general') %>
24
- <% end %>
25
-
26
- <p><%= f.submit I18n.t('submit', :scope => 'devise.otp.submit_token') %></p>
23
+ <p><%= f.submit t('submit', scope: 'devise.otp.submit_token') %></p>
27
24
 
28
25
  <% if !@recovery && recovery_enabled? %>
29
- <p><%= link_to I18n.t('recovery_link', :scope => 'devise.otp.submit_token'), otp_credential_path_for(resource_name, :challenge => @challenge, :recovery => true) %></p>
26
+ <p><%= link_to t('recovery_link', scope: 'devise.otp.submit_token'), otp_credential_path_for(resource_name, challenge: @challenge, recovery: true) %></p>
30
27
  <% end %>
31
28
  <% end %>
@@ -1,22 +1,22 @@
1
- <h3><%= I18n.t('title', :scope => 'devise.otp.token_secret') %></h3>
1
+ <h3><%= t('title', scope: 'devise.otp.token_secret') %></h3>
2
2
 
3
3
  <%= otp_authenticator_token_image(resource) %>
4
4
 
5
5
  <p>
6
- <strong><%= I18n.t('manual_provisioning', :scope => 'devise.otp.token_secret') %>:</strong>
6
+ <strong><%= t('manual_provisioning', scope: 'devise.otp.token_secret') %>:</strong>
7
7
  <code><%= resource.otp_auth_secret %></code>
8
8
  </p>
9
9
 
10
- <p><%= button_to I18n.t('reset_link', :scope => 'devise.otp.otp_tokens'), reset_otp_token_path_for(resource), :method => :post , :data => { "turbo-method": "POST" } %></p>
10
+ <p><%= button_to t('reset_link', scope: 'devise.otp.otp_tokens'), reset_otp_token_path_for(resource), method: :post, data: { turbo_method: :post } %></p>
11
11
 
12
12
  <p>
13
- <%= I18n.t('reset_explain', :scope => 'devise.otp.otp_tokens') %>
14
- <strong><%= I18n.t('reset_explain_warn', :scope => 'devise.otp.otp_tokens') %></strong>
13
+ <%= t('reset_explain', scope: 'devise.otp.otp_tokens') %>
14
+ <strong><%= t('reset_explain_warn', scope: 'devise.otp.otp_tokens') %></strong>
15
15
  </p>
16
16
 
17
17
  <%- if recovery_enabled? %>
18
- <h3><%= I18n.t('title', :scope => 'devise.otp.otp_tokens.recovery') %></h3>
19
- <p><%= I18n.t('explain', :scope => 'devise.otp.otp_tokens.recovery') %></p>
20
- <p><%= link_to I18n.t('codes_list', :scope => 'devise.otp.otp_tokens.recovery'), recovery_otp_token_for(resource_name) %></p>
21
- <p><%= link_to I18n.t('download_codes', :scope => 'devise.otp.otp_tokens.recovery'), recovery_otp_token_for(resource_name, format: :text) %></p>
18
+ <h3><%= t('title', scope: 'devise.otp.otp_tokens.recovery') %></h3>
19
+ <p><%= t('explain', scope: 'devise.otp.otp_tokens.recovery') %></p>
20
+ <p><%= link_to t('codes_list', scope: 'devise.otp.otp_tokens.recovery'), recovery_otp_token_for(resource_name) %></p>
21
+ <p><%= link_to t('download_codes', scope: 'devise.otp.otp_tokens.recovery'), recovery_otp_token_for(resource_name, format: :text) %></p>
22
22
  <% end %>
@@ -1,12 +1,12 @@
1
- <h3><%= I18n.t('title', :scope => 'devise.otp.trusted_browsers') %></h3>
2
- <p><%= I18n.t('explain', :scope => 'devise.otp.trusted_browsers') %></p>
1
+ <h3><%= t('title', scope: 'devise.otp.trusted_browsers') %></h3>
2
+ <p><%= t('explain', scope: 'devise.otp.trusted_browsers') %></p>
3
3
 
4
4
  <%- if is_otp_trusted_browser_for? resource %>
5
- <p><em><%= I18n.t('browser_trusted', :scope => 'devise.otp.trusted_browsers') %></em></p>
6
- <p><%= link_to I18n.t('trust_remove', :scope => 'devise.otp.trusted_browsers'), persistence_otp_token_path_for(resource_name), :method => :post, :data => { "turbo-method": "POST" } %></p>
5
+ <p><em><%= t('browser_trusted', scope: 'devise.otp.trusted_browsers') %></em></p>
6
+ <p><%= button_to t('trust_remove', scope: 'devise.otp.trusted_browsers'), otp_persistence_path_for(resource_name), method: :delete %></p>
7
7
  <% else %>
8
- <p><%= I18n.t('browser_not_trusted', :scope => 'devise.otp.trusted_browsers') %></p>
9
- <p><%= link_to I18n.t('trust_add', :scope => 'devise.otp.trusted_browsers'), persistence_otp_token_path_for(resource_name) %></p>
8
+ <p><%= t('browser_not_trusted', scope: 'devise.otp.trusted_browsers') %></p>
9
+ <p><%= button_to t('trust_add', scope: 'devise.otp.trusted_browsers'), otp_persistence_path_for(resource_name) %></p>
10
10
  <% end %>
11
11
 
12
- <p><%= button_to I18n.t('trust_clear', :scope => 'devise.otp.trusted_browsers'), persistence_otp_token_path_for(resource_name), :method => :delete, :data => { "turbo-method": "DELETE" } %></p>
12
+ <p><%= button_to t('trust_clear', scope: 'devise.otp.trusted_browsers'), reset_otp_persistence_path_for(resource_name), method: :delete %></p>
@@ -1,26 +1,26 @@
1
- <h2><%= I18n.t('title', :scope => 'devise.otp.edit_otp_token') %></h2>
2
- <p><%= I18n.t('explain', :scope => 'devise.otp.edit_otp_token') %></p>
1
+ <h2><%= t('title', scope: 'devise.otp.edit_otp_token') %></h2>
2
+ <p><%= t('explain', scope: 'devise.otp.edit_otp_token') %></p>
3
3
 
4
- <h2><%= I18n.t('lead_in', :scope => 'devise.otp.edit_otp_token') %></h2>
4
+ <h2><%= t('lead_in', scope: 'devise.otp.edit_otp_token') %></h2>
5
5
 
6
- <p><%= I18n.t('step_1', :scope => 'devise.otp.edit_otp_token') %></p>
6
+ <p><%= t('step_1', scope: 'devise.otp.edit_otp_token') %></p>
7
7
 
8
8
  <%= otp_authenticator_token_image(resource) %>
9
9
 
10
10
  <p>
11
- <strong><%= I18n.t('manual_provisioning', :scope => 'devise.otp.token_secret') %>:</strong>
11
+ <strong><%= t('manual_provisioning', scope: 'devise.otp.token_secret') %>:</strong>
12
12
  <code><%= resource.otp_auth_secret %></code>
13
13
  </p>
14
14
 
15
- <p><%= I18n.t('step_2', :scope => 'devise.otp.edit_otp_token') %></p>
15
+ <p><%= t('step_2', scope: 'devise.otp.edit_otp_token') %></p>
16
16
 
17
- <%= form_with(:url => [resource_name, :otp_token], :method => :put) do |f| %>
17
+ <%= form_with(url: [resource_name, :otp_token], method: :put) do |f| %>
18
18
 
19
19
  <p>
20
- <%= f.label :confirmation_code, I18n.t('confirmation_code', :scope => 'devise.otp.edit_otp_token') %>
20
+ <%= f.label :confirmation_code, t('confirmation_code', scope: 'devise.otp.edit_otp_token') %>
21
21
  <%= f.text_field :confirmation_code %>
22
22
  </p>
23
23
 
24
- <p><%= f.submit I18n.t('submit', :scope => 'devise.otp.edit_otp_token') %></p>
24
+ <p><%= f.submit t('submit', scope: 'devise.otp.edit_otp_token') %></p>
25
25
 
26
26
  <% end %>