devise-otp 0.6.0 → 0.8.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -10
  3. data/.gitignore +3 -1
  4. data/CHANGELOG.md +54 -8
  5. data/Gemfile +10 -0
  6. data/README.md +8 -2
  7. data/app/controllers/devise_otp/devise/otp_credentials_controller.rb +46 -27
  8. data/app/controllers/devise_otp/devise/otp_tokens_controller.rb +28 -10
  9. data/app/views/devise/otp_credentials/show.html.erb +6 -6
  10. data/app/views/devise/otp_tokens/_token_secret.html.erb +3 -4
  11. data/app/views/devise/otp_tokens/edit.html.erb +26 -0
  12. data/app/views/devise/otp_tokens/show.html.erb +7 -14
  13. data/config/locales/en.yml +23 -14
  14. data/devise-otp.gemspec +3 -13
  15. data/docs/QR_CODES.md +1 -40
  16. data/lib/devise/strategies/database_authenticatable.rb +64 -0
  17. data/lib/devise-otp/version.rb +1 -1
  18. data/lib/devise-otp.rb +31 -11
  19. data/lib/devise_otp_authenticatable/controllers/helpers.rb +9 -10
  20. data/lib/devise_otp_authenticatable/controllers/public_helpers.rb +39 -0
  21. data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +10 -0
  22. data/lib/devise_otp_authenticatable/engine.rb +2 -5
  23. data/lib/devise_otp_authenticatable/hooks/refreshable.rb +5 -0
  24. data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +22 -20
  25. data/lib/devise_otp_authenticatable/routes.rb +3 -1
  26. data/lib/generators/active_record/templates/migration.rb +1 -1
  27. data/test/dummy/app/controllers/admin_posts_controller.rb +85 -0
  28. data/test/dummy/app/controllers/application_controller.rb +0 -1
  29. data/test/dummy/app/controllers/base_controller.rb +6 -0
  30. data/test/dummy/app/models/admin.rb +25 -0
  31. data/test/dummy/app/views/admin_posts/_form.html.erb +25 -0
  32. data/test/dummy/app/views/admin_posts/edit.html.erb +6 -0
  33. data/test/dummy/app/views/admin_posts/index.html.erb +25 -0
  34. data/test/dummy/app/views/admin_posts/new.html.erb +5 -0
  35. data/test/dummy/app/views/admin_posts/show.html.erb +15 -0
  36. data/test/dummy/app/views/base/home.html.erb +1 -0
  37. data/test/dummy/config/application.rb +0 -2
  38. data/test/dummy/config/routes.rb +4 -1
  39. data/test/dummy/db/migrate/20240604000001_create_admins.rb +9 -0
  40. data/test/dummy/db/migrate/20240604000002_add_devise_to_admins.rb +52 -0
  41. data/test/dummy/db/migrate/20240604000003_devise_otp_add_to_admins.rb +28 -0
  42. data/test/integration/disable_token_test.rb +53 -0
  43. data/test/integration/enable_otp_form_test.rb +57 -0
  44. data/test/integration/persistence_test.rb +3 -6
  45. data/test/integration/refresh_test.rb +32 -0
  46. data/test/integration/reset_token_test.rb +45 -0
  47. data/test/integration/sign_in_test.rb +10 -14
  48. data/test/integration/trackable_test.rb +50 -0
  49. data/test/integration_tests_helper.rb +24 -6
  50. data/test/models/otp_authenticatable_test.rb +62 -27
  51. data/test/orm/active_record.rb +6 -1
  52. data/test/test_helper.rb +1 -71
  53. metadata +26 -135
  54. data/lib/devise_otp_authenticatable/hooks/sessions.rb +0 -58
  55. data/lib/devise_otp_authenticatable/hooks.rb +0 -11
  56. data/test/integration/token_test.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40ba57c939a2a84a81a014b151a2ea0fe37a4253942df15a703a0fd83aa9e80c
4
- data.tar.gz: bba9a1e8d78ea6760c9c32ed643c05ed9138635095c0f9a16451f6a9922bfeba
3
+ metadata.gz: 95e8a170c1942c78dae2cb24c06a818c6e91854268acf78df61b83e22fd18726
4
+ data.tar.gz: 409a75794455459fa1c88892216d556b138cf1a40f4bfe0a06f3ce65a47a528e
5
5
  SHA512:
6
- metadata.gz: 79f30180843989aed3f784e8d775d8d86a206a328f3032b7f202815e1f590fcc5d3bf3d65cccc20731af5d1de227dd2d857111432fc21ad305524a402219f91a
7
- data.tar.gz: 905e257c92ef4cad86876a2e37ec46cea614dafc715ebff364e58c580f7ab1528181771ff96534e5e61978168840f4a9820729b9d819f4c7c002ec970de81da5
6
+ metadata.gz: 45d13d374f2a2504fcee11d81f8771712ae706d138725e3bb777470b099b106cb09ffdb4e2132b6ac5c837b00bb341ff9b5fc56ce5981ff144b358d1e8ed4338
7
+ data.tar.gz: b946e7a0551ba464285e324559f287a9765657258159e508fe08f4b22068548a4be6602d3173a6fac46638130e5795c69bdbdd4b28031d48cfaa592b87eef9da
@@ -7,30 +7,27 @@ on:
7
7
 
8
8
  jobs:
9
9
  rspec:
10
- runs-on: ubuntu-20.04
10
+ runs-on: ubuntu-latest
11
11
  strategy:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  ruby:
15
+ - '3.3'
16
+ - '3.2'
15
17
  - '3.1'
16
- - '3.0'
17
- - '2.7'
18
+ - 'head'
18
19
 
19
20
  steps:
20
21
  - name: Checkout
21
- uses: actions/checkout@v2
22
+ uses: actions/checkout@v4
22
23
 
23
24
  - name: Setup Ruby
24
25
  uses: ruby/setup-ruby@v1
25
26
  with:
26
27
  ruby-version: ${{ matrix.ruby }}
27
-
28
- - name: Bundle
29
- run: |
30
- gem install bundler
31
- bundle install --jobs 4 --retry 3
28
+ bundler-cache: true
32
29
 
33
30
  - name: Run tests
34
31
  env:
35
32
  DEVISE_ORM: active_record
36
- run: rake test
33
+ run: bundle exec rake test
data/.gitignore CHANGED
@@ -35,8 +35,10 @@ lib/bundler/man
35
35
  test/dummy/log/**
36
36
  test/dummy/tmp/**
37
37
  test/dummy/db/*.sqlite3
38
+ test/dummy/db/*.sqlite3-shm
39
+ test/dummy/db/*.sqlite3-wal
38
40
 
39
41
  Gemfile.lock
40
42
 
41
43
  # Generated test files
42
- tmp/*
44
+ tmp/*
data/CHANGELOG.md CHANGED
@@ -1,17 +1,63 @@
1
1
  # Changelog
2
2
 
3
- ## 0.4.0
3
+ ## Unreleased
4
+
5
+ - Upgrade gemspec to support Rails v7.2
6
+
7
+ ## 0.7.0
4
8
 
5
9
  Breaking changes:
6
10
 
7
- - rename `Devise::Otp` to `Devise::OTP`
8
- - change `credentials` directory to `otp_credentials`
9
- - change `tokens` directory to `otp_tokens`
11
+ - Require confirmation token before enabling Two Factor Authentication (2FA) to ensure that user has added OTP token properly to their device
12
+ - Update DeviseAuthenticatable to redirect user (rather than login user) when OTP is enabled
13
+ - Remove OtpAuthenticatable callbacks for setting OTP credentials on create action (no longer needed)
14
+ - Replace OtpAuthenticatable "reset_otp_credentials" methods with "clear_otp_fields!" method
15
+ - Update otp_tokens#edit to populate OTP secrets (rather than assuming they are populated via callbacks in OTPDeviseAuthenticatable module)
16
+ - Repurpose otp_tokens#destroy to disable 2FA and clear OTP secrets (rather than resetting them)
17
+ - Add reset token action and hide/repurpose disable token action
18
+ - Update disable action to preserve the existing token secret
19
+ - Hide button for mandatory OTP
20
+ - Add Refreshable hook, and tie into after\_set\_user calback
21
+ - Utilize native warden session for scoping of credentials\_refreshed\_at and refresh\_return\_url properties
22
+ - Require adding "ensure\_mandatory\_{scope}\_otp! to controllers for mandatory OTP
23
+ - Update locales to support the new workflow
24
+
25
+ ### Upgrading
26
+
27
+ Regenerate your views with `rails g devise_otp:views` and update locales.
28
+
29
+ Changes to locales:
30
+
31
+ - Remove:
32
+ - otp_tokens.enable_request
33
+ - otp_tokens.status
34
+ - otp_tokens.submit
35
+ - Add to otp_tokens scope:
36
+ - enable_link
37
+ - Move/rename devise.otp.token_secret.reset_\* values to devise.otp.otp_tokens.disable_\* (for consistency with "enable_link")
38
+ - disable_link
39
+ - disable_explain
40
+ - disable_explain_warn
41
+ - Add to new edit_otp_token scope:
42
+ - title
43
+ - lead_in
44
+ - step1
45
+ - step2
46
+ - confirmation_code
47
+ - submit
48
+ - Move "explain" to new edit_otp_token scope
49
+ - Add devise.otp.otp_tokens.could_not_confirm
50
+ - Rename "successfully_reset_creds" to "successfully_disabled_otp"
51
+
52
+ You can grab the full locale file [here](https://github.com/wmlele/devise-otp/blob/master/config/locales/en.yml).
53
+
54
+ ## 0.6.0
10
55
 
11
- Other improvements:
56
+ Improvements:
12
57
 
13
- - Fix file permissions
58
+ - support rails 6.1 by @cotcomsol in #67
14
59
 
15
- ## 0.3.0
60
+ Fixes:
16
61
 
17
- A long awaited update bringing Devise::OTP from the dead!
62
+ - mandatory otp fix by @cotcomsol in #68
63
+ - remove success message by @strzibny in #69
data/Gemfile CHANGED
@@ -2,3 +2,13 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in devise-otp.gemspec
4
4
  gemspec
5
+
6
+ gem "capybara"
7
+ gem "minitest-reporters", ">= 0.5.0"
8
+ gem "puma"
9
+ gem "rake"
10
+ gem "rdoc"
11
+ gem "shoulda"
12
+ gem "sprockets-rails"
13
+ gem "sqlite3", "~> 1.4"
14
+ gem "standardrb"
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Devise::OTP
2
2
 
3
- Devise OTP is a two-factors authentication extension for Devise. The second factor is done using an [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238) Time-Based One-Time Password (TOTP) implemented by the [rotp library](https://github.com/mdp/rotp).
3
+ Devise OTP is a Two-Factor Authentication extension for Devise. The second factor is done using an [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238) Time-Based One-Time Password (TOTP) implemented by the [rotp library](https://github.com/mdp/rotp).
4
4
 
5
5
  It has the following features:
6
6
 
@@ -19,7 +19,7 @@ Device OTP was recently updated to work with Rails 7 and Turbo.
19
19
 
20
20
  Devise::OTP development is sponsored by [Business Class](https://businessclasskit.com/) Rails SaaS starter kit. If you don't want to setup OTP yourself for your new project, consider starting one on Business Class.
21
21
 
22
- ## Two-factors authentication using OTP
22
+ ## Two-Factor Authentication using OTP
23
23
 
24
24
  * A shared secret is generated on the server, and stored both on the token device (e.g. the phone) and the server itself.
25
25
  * The secret is used to generate short numerical tokens that are either time or sequence based.
@@ -94,6 +94,12 @@ The install generator adds some options to the end of your Devise config file (`
94
94
  * `config.otp_issuer`: The name of the token issuer, to be added to the provisioning url. Display will vary based on token application. (defaults to the Rails application class)
95
95
  * `config.otp_controller_path`: The view path for Devise OTP controllers. The default being 'devise' to match Devise default installation.
96
96
 
97
+ ## Mandatory OTP
98
+ Enforcing mandatory OTP requires adding the ensure\_mandatory\_{scope}\_otp! method to the desired controller(s) to ensure that the user is redirected to the Enable Two-Factor Authentication form before proceeding to other parts of the application. This functions the same way as the authenticate\_{scope}! methods, and can be included inline with them in the controllers, e.g.:
99
+
100
+ before_action :authenticate_user!
101
+ before_action :ensure_mandatory_user_otp!
102
+
97
103
  ## Authors
98
104
 
99
105
  The project was originally started by Lele Forzani by forking [devise_google_authenticator](https://github.com/AsteriskLabs/devise_google_authenticator) and still contains some devise_google_authenticator code. It's now maintained by [Josef Strzibny](https://github.com/strzibny/).
@@ -5,45 +5,31 @@ module DeviseOtp
5
5
 
6
6
  prepend_before_action :authenticate_scope!, only: [:get_refresh, :set_refresh]
7
7
  prepend_before_action :require_no_authentication, only: [:show, :update]
8
+ before_action :set_challenge, only: [:show, :update]
9
+ before_action :set_recovery, only: [:show, :update]
10
+ before_action :set_resource, only: [:show, :update]
11
+ before_action :set_token, only: [:update]
12
+ before_action :skip_challenge_if_trusted_browser, only: [:show, :update]
8
13
 
9
14
  #
10
15
  # show a request for the OTP token
11
16
  #
12
17
  def show
13
- @challenge = params[:challenge]
14
- @recovery = (params[:recovery] == "true") && recovery_enabled?
15
-
16
- if @challenge.nil?
17
- redirect_to :root
18
- else
19
- self.resource = resource_class.find_valid_otp_challenge(@challenge)
20
- if resource.nil?
21
- redirect_to :root
22
- elsif @recovery
23
- @recovery_count = resource.otp_recovery_counter
24
- render :show
25
- else
26
- render :show
27
- end
18
+ if @recovery
19
+ @recovery_count = resource.otp_recovery_counter
28
20
  end
21
+
22
+ render :show
29
23
  end
30
24
 
31
25
  #
32
26
  # signs the resource in, if the OTP token is valid and the user has a valid challenge
33
27
  #
34
28
  def update
35
- resource = resource_class.find_valid_otp_challenge(params[resource_name][:challenge])
36
- recovery = (params[resource_name][:recovery] == "true") && recovery_enabled?
37
- token = params[resource_name][:token]
38
-
39
- if token.blank?
29
+ if @token.blank?
40
30
  otp_set_flash_message(:alert, :token_blank)
41
- redirect_to otp_credential_path_for(resource_name, challenge: params[resource_name][:challenge],
42
- recovery: recovery)
43
- elsif resource.nil?
44
- otp_set_flash_message(:alert, :otp_session_invalid)
45
- redirect_to new_session_path(resource_name)
46
- elsif resource.otp_challenge_valid? && resource.validate_otp_token(params[resource_name][:token], recovery)
31
+ redirect_to otp_credential_path_for(resource_name, challenge: @challenge, recovery: @recovery)
32
+ elsif resource.otp_challenge_valid? && resource.validate_otp_token(@token, @recovery)
47
33
  sign_in(resource_name, resource)
48
34
 
49
35
  otp_set_trusted_device_for(resource) if params[:enable_persistence] == "true"
@@ -51,7 +37,7 @@ module DeviseOtp
51
37
  respond_with resource, location: after_sign_in_path_for(resource)
52
38
  else
53
39
  otp_set_flash_message :alert, :token_invalid
54
- redirect_to new_session_path(resource_name)
40
+ render :show
55
41
  end
56
42
  end
57
43
 
@@ -78,6 +64,39 @@ module DeviseOtp
78
64
 
79
65
  private
80
66
 
67
+ def set_challenge
68
+ @challenge = params[:challenge]
69
+
70
+ unless @challenge.present?
71
+ redirect_to :root
72
+ end
73
+ end
74
+
75
+ def set_recovery
76
+ @recovery = (recovery_enabled? && params[:recovery] == "true")
77
+ end
78
+
79
+ def set_resource
80
+ self.resource = resource_class.find_valid_otp_challenge(@challenge)
81
+
82
+ unless resource.present?
83
+ otp_set_flash_message(:alert, :otp_session_invalid)
84
+ redirect_to new_session_path(resource_name)
85
+ end
86
+ end
87
+
88
+ def set_token
89
+ @token = params[:token]
90
+ end
91
+
92
+ def skip_challenge_if_trusted_browser
93
+ if is_otp_trusted_browser_for?(resource)
94
+ sign_in(resource_name, resource)
95
+ otp_refresh_credentials_for(resource)
96
+ redirect_to after_sign_in_path_for(resource)
97
+ end
98
+ end
99
+
81
100
  def done_valid_refresh
82
101
  otp_refresh_credentials_for(resource)
83
102
  respond_with resource, location: otp_fetch_refresh_return_url
@@ -19,27 +19,36 @@ module DeviseOtp
19
19
  end
20
20
  end
21
21
 
22
+ #
23
+ # Displays the QR Code and Validation Token form for enabling the OTP
24
+ #
25
+ def edit
26
+ resource.populate_otp_secrets!
27
+ end
28
+
22
29
  #
23
30
  # Updates the status of OTP authentication
24
31
  #
25
32
  def update
26
- enabled = params[resource_name][:otp_enabled] == "1"
27
- if enabled ? resource.enable_otp! : resource.disable_otp!
33
+ if resource.valid_otp_token?(params[:confirmation_code])
34
+ resource.enable_otp!
28
35
  otp_set_flash_message :success, :successfully_updated
36
+ redirect_to otp_token_path_for(resource)
37
+ else
38
+ otp_set_flash_message :danger, :could_not_confirm
39
+ render :edit
29
40
  end
30
-
31
- render :show
32
41
  end
33
42
 
34
43
  #
35
44
  # Resets OTP authentication, generates new credentials, sets it to off
36
45
  #
37
46
  def destroy
38
- if resource.reset_otp_credentials!
39
- otp_set_flash_message :success, :successfully_reset_creds
47
+ if resource.disable_otp!
48
+ otp_set_flash_message :success, :successfully_disabled_otp
40
49
  end
41
50
 
42
- redirect_to action: :show
51
+ redirect_to otp_token_path_for(resource)
43
52
  end
44
53
 
45
54
  #
@@ -50,7 +59,7 @@ module DeviseOtp
50
59
  otp_set_flash_message :success, :successfully_set_persistence
51
60
  end
52
61
 
53
- redirect_to action: :show
62
+ redirect_to otp_token_path_for(resource)
54
63
  end
55
64
 
56
65
  #
@@ -61,7 +70,7 @@ module DeviseOtp
61
70
  otp_set_flash_message :success, :successfully_cleared_persistence
62
71
  end
63
72
 
64
- redirect_to action: :show
73
+ redirect_to otp_token_path_for(resource)
65
74
  end
66
75
 
67
76
  #
@@ -72,7 +81,7 @@ module DeviseOtp
72
81
  otp_set_flash_message :notice, :successfully_reset_persistence
73
82
  end
74
83
 
75
- redirect_to action: :show
84
+ redirect_to otp_token_path_for(resource)
76
85
  end
77
86
 
78
87
  def recovery
@@ -85,6 +94,15 @@ module DeviseOtp
85
94
  end
86
95
  end
87
96
 
97
+ def reset
98
+ if resource.disable_otp!
99
+ resource.clear_otp_fields!
100
+ otp_set_flash_message :success, :successfully_reset_otp
101
+ end
102
+
103
+ redirect_to edit_otp_token_path_for(resource)
104
+ end
105
+
88
106
  private
89
107
 
90
108
  def ensure_credentials_refresh
@@ -3,21 +3,21 @@
3
3
 
4
4
  <%= form_for(resource, :as => resource_name, :url => [resource_name, :otp_credential], :html => { :method => :put, "data-turbo" => false }) do |f| %>
5
5
 
6
- <%= f.hidden_field :challenge, {:value => @challenge} %>
7
- <%= f.hidden_field :recovery, {:value => @recovery} %>
6
+ <%= hidden_field_tag :challenge, @challenge %>
7
+ <%= hidden_field_tag :recovery, @recovery %>
8
8
 
9
9
  <% if @recovery %>
10
10
  <p>
11
- <%= f.label :token, I18n.t('recovery_prompt', :scope => 'devise.otp.submit_token') %><br />
12
- <%= f.text_field :otp_recovery_counter, :autocomplete => :off, :disabled => true, :size => 4 %>
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 %>
13
13
  </p>
14
14
  <% else %>
15
15
  <p>
16
- <%= f.label :token, I18n.t('prompt', :scope => 'devise.otp.submit_token') %><br />
16
+ <%= label_tag :token, I18n.t('prompt', :scope => 'devise.otp.submit_token') %><br />
17
17
  </p>
18
18
  <% end %>
19
19
 
20
- <%= f.text_field :token, :autocomplete => :off, :autofocus => true, :size => 6, :value => '' %><br>
20
+ <%= text_field_tag :token, nil, :autocomplete => :off, :autofocus => true, :size => 6 %><br>
21
21
 
22
22
  <%= label_tag :enable_persistence do %>
23
23
  <%= check_box_tag :enable_persistence, true, false %> <%= I18n.t('remember', :scope => 'devise.otp.general') %>
@@ -1,5 +1,4 @@
1
1
  <h3><%= I18n.t('title', :scope => 'devise.otp.token_secret') %></h3>
2
- <p><%= I18n.t('explain', :scope => 'devise.otp.token_secret') %></p>
3
2
 
4
3
  <%= otp_authenticator_token_image(resource) %>
5
4
 
@@ -8,11 +7,11 @@
8
7
  <code><%= resource.otp_auth_secret %></code>
9
8
  </p>
10
9
 
11
- <p><%= button_to I18n.t('reset_otp', :scope => 'devise.otp.token_secret'), @resource, :method => :delete, :data => { "turbo-method": "DELETE" } %></p>
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>
12
11
 
13
12
  <p>
14
- <%= I18n.t('reset_explain', :scope => 'devise.otp.token_secret') %>
15
- <strong><%= I18n.t('reset_explain_warn', :scope => 'devise.otp.token_secret') %></strong>
13
+ <%= I18n.t('reset_explain', :scope => 'devise.otp.otp_tokens') %>
14
+ <strong><%= I18n.t('reset_explain_warn', :scope => 'devise.otp.otp_tokens') %></strong>
16
15
  </p>
17
16
 
18
17
  <%- if recovery_enabled? %>
@@ -0,0 +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>
3
+
4
+ <h2><%= I18n.t('lead_in', :scope => 'devise.otp.edit_otp_token') %></h2>
5
+
6
+ <p><%= I18n.t('step_1', :scope => 'devise.otp.edit_otp_token') %></p>
7
+
8
+ <%= otp_authenticator_token_image(resource) %>
9
+
10
+ <p>
11
+ <strong><%= I18n.t('manual_provisioning', :scope => 'devise.otp.token_secret') %>:</strong>
12
+ <code><%= resource.otp_auth_secret %></code>
13
+ </p>
14
+
15
+ <p><%= I18n.t('step_2', :scope => 'devise.otp.edit_otp_token') %></p>
16
+
17
+ <%= form_with(:url => [resource_name, :otp_token], :method => :put) do |f| %>
18
+
19
+ <p>
20
+ <%= f.label :confirmation_code, I18n.t('confirmation_code', :scope => 'devise.otp.edit_otp_token') %>
21
+ <%= f.text_field :confirmation_code %>
22
+ </p>
23
+
24
+ <p><%= f.submit I18n.t('submit', :scope => 'devise.otp.edit_otp_token') %></p>
25
+
26
+ <% end %>
@@ -1,21 +1,14 @@
1
1
  <h2><%= I18n.t('title', :scope => 'devise.otp.otp_tokens') %></h2>
2
- <p><%= I18n.t('explain', :scope => 'devise.otp.otp_tokens') %></p>
3
2
 
4
- <%= form_for(resource, :as => resource_name, :url => [resource_name, :otp_token], :html => { :method => :put, "data-turbo" => false }) do |f| %>
5
-
6
- <%= render "devise/shared/error_messages", resource: resource %>
7
-
8
- <h3><%= I18n.t('enable_request', :scope => 'devise.otp.otp_tokens') %></h3>
9
-
10
- <p>
11
- <%= f.label :otp_enabled, I18n.t('status', :scope => 'devise.otp.otp_tokens') %><br />
12
- <%= f.check_box :otp_enabled %>
13
- </p>
14
-
15
- <p><%= f.submit I18n.t('submit', :scope => 'devise.otp.otp_tokens') %></p>
16
- <% end %>
3
+ <p><strong>Status:</strong> <%= resource.otp_enabled? ? "Enabled" : "Disabled" %></p>
17
4
 
18
5
  <%- if resource.otp_enabled? %>
19
6
  <%= render :partial => 'token_secret' if resource.otp_enabled? %>
20
7
  <%= render :partial => 'trusted_devices' if trusted_devices_enabled? %>
8
+
9
+ <% unless otp_mandatory_on?(resource) %>
10
+ <%= button_to I18n.t('disable_link', :scope => 'devise.otp.otp_tokens'), otp_token_path_for(resource), :method => :delete, :data => { "turbo-method": "DELETE" } %>
11
+ <% end %>
12
+ <% else %>
13
+ <%= link_to I18n.t('enable_link', :scope => 'devise.otp.otp_tokens'), edit_otp_token_path_for(resource) %>
21
14
  <% end %>
@@ -5,12 +5,13 @@ en:
5
5
  remember: Remember me
6
6
  submit_token:
7
7
  title: 'Check Token'
8
- explain: "You're getting this because you enabled two-factors authentication on your account"
9
- prompt: 'Please enter your two-factors authentication token:'
8
+ explain: "You're getting this because you enabled Two-Factor Authentication on your account"
9
+ prompt: 'Please enter your Two-Factor Authentication token:'
10
10
  recovery_prompt: 'Please enter your recovery code:'
11
11
  submit: 'Submit Token'
12
12
  recovery_link: "I don't have my device, I want to use a recovery code"
13
13
  otp_credentials:
14
+ otp_session_invalid: Session invalid. Please start again.
14
15
  token_invalid: 'The token you provided was invalid.'
15
16
  token_blank: 'You need to type in the token you generated with your device.'
16
17
  need_to_refresh_credentials: 'We need to check your credentials before you can change these settings.'
@@ -21,22 +22,22 @@ en:
21
22
  explain: 'In order to ensure this is safe, please enter your password again.'
22
23
  go_on: 'Continue...'
23
24
  identity: 'Identity:'
24
- token: 'Your two-factors authentication token'
25
+ token: 'Your Two-Factor Authentication token'
25
26
  token_secret:
26
27
  title: 'Your token secret'
27
28
  explain: 'Take a photo of this QR code with your mobile'
28
29
  manual_provisioning: 'Manual provisioning code'
29
- reset_otp: 'Reset your Two Factors Authentication status'
30
- reset_explain: 'This will reset your credentials, and disable two-factors authentication.'
31
- reset_explain_warn: 'You will need to enroll your mobile device again.'
32
30
  otp_tokens:
33
- title: 'Two-factors Authentication:'
34
- explain: 'Two factors authentication adds an additional layer of security to your account. When logging in you will be asked for a code that you can generate on a physical device, like your phone.'
35
- enable_request: 'Would you like to enable Two Factors Authenticator?'
36
- status: 'Enable Two-Factors Authentication.'
37
- submit: 'Continue...'
38
- successfully_updated: 'Your two-factors authentication settings have been updated.'
39
- successfully_reset_creds: 'Your two-factors credentials has been reset.'
31
+ title: 'Two-Factor Authentication:'
32
+ enable_link: 'Enable Two-Factor Authentication'
33
+ disable_link: 'Disable Two-Factor Authentication'
34
+ reset_link: 'Reset Token Secret'
35
+ reset_explain: 'Resetting your token secret will temporarilly disable Two-Factor Authentication.'
36
+ reset_explain_warn: 'To re-enable Two-Factor Authentication, you will need to re-enroll your mobile device with the new token secret.'
37
+ successfully_updated: 'Your Two-Factor Authentication settings have been updated.'
38
+ could_not_confirm: 'The Confirmation Code you entered did not match the QR code shown below.'
39
+ successfully_disabled_otp: 'Two-Factor Authentication has been disabled.'
40
+ successfully_reset_otp: 'Your token secret has been reset. Please confirm your new token secret below.'
40
41
  successfully_set_persistence: 'Your device is now trusted.'
41
42
  successfully_cleared_persistence: 'Your device has been removed from the list of trusted devices.'
42
43
  successfully_reset_persistence: 'Your list of trusted devices has been cleared.'
@@ -48,9 +49,17 @@ en:
48
49
  code: 'Recovery Code'
49
50
  codes_list: 'Here is the list of your recovery codes'
50
51
  download_codes: 'Download recovery codes'
52
+ edit_otp_token:
53
+ title: 'Enable Two-factor Authentication'
54
+ explain: 'Two-Factor Authentication adds an additional layer of security to your account. When logging in you will be asked for a code that you can generate on a physical device, like your phone.'
55
+ lead_in: 'To Enable Two-Factor Authentication:'
56
+ step_1: '1. Open your authenticator app and scan the QR code shown below:'
57
+ step_2: '2. Enter the 6-digit code shown in your authenticator app below:'
58
+ confirmation_code: "Confirmation Code"
59
+ submit: 'Continue...'
51
60
  trusted_browsers:
52
61
  title: 'Trusted Browsers'
53
- explain: 'If you set your browser as trusted, you will not be asked to provide a Two-factor authentication token when logging in from that browser.'
62
+ explain: 'If you set your browser as trusted, you will not be asked to provide a Two-Factor Authentication token when logging in from that browser.'
54
63
  browser_trusted: 'Your browser is trusted.'
55
64
  browser_not_trusted: 'Your browser is not trusted.'
56
65
  trust_remove: 'Remove this browser from the list of trusted browsers'
data/devise-otp.gemspec CHANGED
@@ -14,17 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.files = `git ls-files`.split($/)
15
15
  gem.require_paths = ["lib"]
16
16
 
17
- gem.add_runtime_dependency "rails", ">= 6.1", "< 7.2"
18
- gem.add_runtime_dependency "devise", ">= 4.8.0", "< 5.0"
19
- gem.add_runtime_dependency "rotp", ">= 2.0.0"
20
-
21
- gem.add_development_dependency "capybara"
22
- gem.add_development_dependency "cuprite"
23
- gem.add_development_dependency "minitest-reporters", ">= 0.5.0"
24
- gem.add_development_dependency "puma"
25
- gem.add_development_dependency "rdoc"
26
- gem.add_development_dependency "shoulda"
27
- gem.add_development_dependency "sprockets-rails"
28
- gem.add_development_dependency "sqlite3"
29
- gem.add_development_dependency "standardrb"
17
+ gem.add_dependency "rails", ">= 7.0", "< 8.0"
18
+ gem.add_dependency "devise", ">= 4.8.0", "< 5.0"
19
+ gem.add_dependency "rotp", ">= 2.0.0"
30
20
  end
data/docs/QR_CODES.md CHANGED
@@ -6,43 +6,4 @@ To do that, add the the following line to your `application.js` file:
6
6
 
7
7
  //= require devise-otp
8
8
 
9
- You can change this behavior by overriding the `otp_authenticator_token_image` method in your view helper to call `otp_authenticator_token_image_google`:
10
-
11
- ```ruby
12
- def otp_authenticator_token_image(resource)
13
- otp_authenticator_token_image_google(resource.otp_provisioning_uri)
14
- end
15
- ```
16
-
17
- This will call [Google API](https://github.com/wmlele/devise-otp/tree/master/lib/devise_otp_authenticatable/controllers/helpers.rb#L160) to render the QR code.
18
-
19
- If your application is configured to use CSP policies, you'll need to authorize `chart.googleapis.com`. Here's an example with [secure_headers](https://github.com/github/secure_headers)):
20
-
21
- ```ruby
22
- config.csp[:img_src] << 'chart.googleapis.com'
23
- ```
24
-
25
- A third option consists in installing [jquery-qrcode]https://github.com/jeromeetienne/jquery-qrcode with Yarn or [shakapacker](https://github.com/shakacode/shakapacker) and overriding `otp_authenticator_token_image` to render some HTML :
26
-
27
- ```ruby
28
- def otp_authenticator_token_image(resource)
29
- tag(:span, data: { toggle: 'qrcode', otp_url: resource.otp_provisioning_uri, width: 192, height: 192, render: 'canvas' })
30
- end
31
- ```
32
- The QR code is then rendered by `jquery-qrcode` by setting a JS listener in your `application.js` :
33
-
34
- ```js
35
- $(document).on('turbo:load', function() {
36
- return $('[data-toggle=qrcode]').each(function() {
37
- var data;
38
- data = $(this).data();
39
- return $(this).qrcode({
40
- text: data['otpUrl'],
41
- width: data['width'],
42
- height: data['height'],
43
- render: data['render']
44
- });
45
- });
46
- });
47
- ```
48
- This way you don't rely on external services to render the QR codes.
9
+ You can change this behavior by overriding the `otp_authenticator_token_image` method in your view helper.