devise-otp 1.1.0 → 2.0.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.
- checksums.yaml +4 -4
- data/.erb_lint.yml +30 -0
- data/.rubocop.yml +18 -0
- data/CHANGELOG.md +42 -1
- data/Gemfile +7 -1
- data/README.md +5 -1
- data/app/controllers/devise_otp/devise/otp_credentials_controller.rb +30 -7
- data/app/controllers/devise_otp/devise/otp_persistence_controller.rb +53 -0
- data/app/controllers/devise_otp/devise/otp_tokens_controller.rb +2 -37
- data/app/views/devise/otp_credentials/refresh.html.erb +6 -6
- data/app/views/devise/otp_credentials/show.html.erb +10 -13
- data/app/views/devise/otp_tokens/_token_secret.html.erb +9 -9
- data/app/views/devise/otp_tokens/_trusted_devices.html.erb +7 -7
- data/app/views/devise/otp_tokens/edit.html.erb +9 -9
- data/app/views/devise/otp_tokens/recovery.html.erb +6 -6
- data/app/views/devise/otp_tokens/show.html.erb +6 -6
- data/bin/erb_lint +27 -0
- data/bin/rubocop +27 -0
- data/config/locales/en.yml +9 -7
- data/devise-otp.gemspec +3 -1
- data/lib/devise/strategies/database_authenticatable.rb +4 -17
- data/lib/devise-otp/version.rb +1 -1
- data/lib/devise-otp.rb +0 -1
- data/lib/devise_otp_authenticatable/controllers/helpers.rb +1 -2
- data/lib/devise_otp_authenticatable/controllers/public_helpers.rb +1 -2
- data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +7 -2
- data/lib/devise_otp_authenticatable/hooks/refreshable.rb +0 -1
- data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +18 -12
- data/lib/devise_otp_authenticatable/routes.rb +7 -7
- data/test/dummy/app/controllers/admin_posts_controller.rb +0 -1
- data/test/dummy/app/controllers/base_controller.rb +0 -2
- data/test/dummy/app/controllers/non_otp_posts_controller.rb +0 -1
- data/test/dummy/app/models/admin.rb +0 -1
- data/test/dummy/app/models/lockable_user.rb +8 -0
- data/test/dummy/app/models/non_otp_user.rb +0 -1
- data/test/dummy/app/models/rememberable_user.rb +8 -0
- data/test/dummy/app/views/layouts/application.html.erb +4 -2
- data/test/dummy/app/views/posts/show.html.erb +0 -1
- data/test/dummy/app/views/shared/_navbar.html.erb +15 -0
- data/test/dummy/config/application.rb +3 -0
- data/test/dummy/config/initializers/devise.rb +2 -2
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/db/migrate/20250731000001_create_lockable_users.rb +9 -0
- data/test/dummy/db/migrate/20250731000002_add_devise_to_lockable_users.rb +52 -0
- data/test/dummy/db/migrate/20250731000003_devise_otp_add_to_lockable_users.rb +28 -0
- data/test/dummy/db/migrate/20250817221304_create_rememberable_users.rb +50 -0
- data/test/dummy/db/migrate/20250818030305_add_devise_otp_to_rememberable_users.rb +28 -0
- data/test/dummy/db/schema.rb +67 -1
- data/test/integration/disable_token_test.rb +0 -2
- data/test/integration/enable_otp_form_test.rb +12 -1
- data/test/integration/lockable_test.rb +143 -0
- data/test/integration/non_otp_user_models_test.rb +0 -2
- data/test/integration/otp_drift_test.rb +35 -0
- data/test/integration/persistence_test.rb +59 -4
- data/test/integration/refresh_test.rb +23 -14
- data/test/integration/rememberable_test.rb +143 -0
- data/test/integration/reset_token_test.rb +0 -2
- data/test/integration/sign_in_test.rb +15 -8
- data/test/integration/trackable_test.rb +0 -2
- data/test/integration_tests_helper.rb +22 -1
- data/test/models/otp_authenticatable_test.rb +48 -11
- data/test/test_helper.rb +1 -0
- metadata +34 -4
|
@@ -6,6 +6,10 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
6
6
|
new_user
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def teardown
|
|
10
|
+
Timecop.return
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
test "new users do not have a secret set" do
|
|
10
14
|
user = User.first
|
|
11
15
|
|
|
@@ -15,7 +19,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
test "new users have OTP disabled by default" do
|
|
18
|
-
|
|
22
|
+
assert_not User.first.otp_enabled
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
test "populating otp secrets should populate all required fields" do
|
|
@@ -42,8 +46,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
42
46
|
user.enable_otp!
|
|
43
47
|
user.generate_otp_challenge!
|
|
44
48
|
user.update(
|
|
45
|
-
:
|
|
46
|
-
:
|
|
49
|
+
otp_failed_attempts: 1,
|
|
50
|
+
otp_recovery_counter: 1
|
|
47
51
|
)
|
|
48
52
|
|
|
49
53
|
|
|
@@ -75,7 +79,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
75
79
|
|
|
76
80
|
u.reset_otp_persistence!
|
|
77
81
|
assert(otp_auth_secret == u.otp_auth_secret)
|
|
78
|
-
|
|
82
|
+
assert_not (otp_persistence_seed == u.otp_persistence_seed)
|
|
79
83
|
assert u.otp_enabled
|
|
80
84
|
end
|
|
81
85
|
|
|
@@ -95,7 +99,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
95
99
|
u.populate_otp_secrets!
|
|
96
100
|
u.enable_otp!
|
|
97
101
|
challenge = u.generate_otp_challenge!(1.second)
|
|
98
|
-
|
|
102
|
+
|
|
103
|
+
Timecop.travel(Time.now + 2)
|
|
99
104
|
|
|
100
105
|
w = User.find_valid_otp_challenge(challenge)
|
|
101
106
|
assert_nil w
|
|
@@ -106,7 +111,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
106
111
|
u.populate_otp_secrets!
|
|
107
112
|
u.enable_otp!
|
|
108
113
|
challenge = u.generate_otp_challenge!(1.second)
|
|
109
|
-
|
|
114
|
+
Timecop.travel(Time.now + 2)
|
|
110
115
|
assert_equal false, u.otp_challenge_valid?
|
|
111
116
|
end
|
|
112
117
|
|
|
@@ -129,17 +134,49 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
|
129
134
|
assert_equal true, u.validate_otp_token(token)
|
|
130
135
|
end
|
|
131
136
|
|
|
132
|
-
test "generated otp token
|
|
137
|
+
test "generated otp token within the drift window should be valid for the user" do
|
|
133
138
|
u = User.first
|
|
134
139
|
u.populate_otp_secrets!
|
|
135
140
|
u.enable_otp!
|
|
136
141
|
|
|
137
142
|
secret = u.otp_auth_secret
|
|
143
|
+
token = ROTP::TOTP.new(secret).at(Time.now)
|
|
138
144
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
Timecop.freeze(Time.now + 90)
|
|
146
|
+
assert_equal true, u.valid_otp_token?(token)
|
|
147
|
+
|
|
148
|
+
Timecop.return
|
|
149
|
+
Timecop.freeze(Time.now - 90)
|
|
150
|
+
assert_equal true, u.valid_otp_token?(token)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
test "generated otp token outside of drift window should NOT be valid for the user" do
|
|
154
|
+
# Since the otp_drift_window defines steps (not just time), and these steps
|
|
155
|
+
# begin at the 30 and 60 second marks of each minute, it is possible for
|
|
156
|
+
# an OTP token to be used up to 119 seconds after generation with a 3 step
|
|
157
|
+
# drift window. For example, a token generated at 12:00:00PM could be used
|
|
158
|
+
# within the timeframe for any of the following steps:
|
|
159
|
+
# - 12:00:00~12:00:29 (current step)
|
|
160
|
+
# - 12:00:30~12:00:59 (drift of 1 step)
|
|
161
|
+
# - 12:01:00~12:00:29 (drift of 2 steps)
|
|
162
|
+
# - 12:01:30~12:01:59 (drift of 3 steps)
|
|
163
|
+
#
|
|
164
|
+
# As a result, we need to test for 120 seconds to ensure that the test
|
|
165
|
+
# always passes.
|
|
166
|
+
|
|
167
|
+
u = User.first
|
|
168
|
+
u.populate_otp_secrets!
|
|
169
|
+
u.enable_otp!
|
|
170
|
+
|
|
171
|
+
secret = u.otp_auth_secret
|
|
172
|
+
token = ROTP::TOTP.new(secret).at(Time.now)
|
|
173
|
+
|
|
174
|
+
Timecop.freeze(Time.now + 120)
|
|
175
|
+
assert_equal false, u.valid_otp_token?(token)
|
|
176
|
+
|
|
177
|
+
Timecop.return
|
|
178
|
+
Timecop.freeze(Time.now - 120)
|
|
179
|
+
assert_equal false, u.valid_otp_token?(token)
|
|
143
180
|
end
|
|
144
181
|
|
|
145
182
|
test "recovery secrets should be valid, and valid only once" do
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise-otp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lele Forzani
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2025-
|
|
13
|
+
date: 2025-10-21 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rails
|
|
@@ -66,14 +66,28 @@ dependencies:
|
|
|
66
66
|
requirements:
|
|
67
67
|
- - "~>"
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '
|
|
69
|
+
version: '3.0'
|
|
70
70
|
type: :runtime
|
|
71
71
|
prerelease: false
|
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
|
73
73
|
requirements:
|
|
74
74
|
- - "~>"
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '
|
|
76
|
+
version: '3.0'
|
|
77
|
+
- !ruby/object:Gem::Dependency
|
|
78
|
+
name: timecop
|
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - "~>"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: 0.9.10
|
|
84
|
+
type: :development
|
|
85
|
+
prerelease: false
|
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - "~>"
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: 0.9.10
|
|
77
91
|
description: OTP authentication for Devise
|
|
78
92
|
email:
|
|
79
93
|
- lele@windmill.it
|
|
@@ -83,8 +97,10 @@ executables: []
|
|
|
83
97
|
extensions: []
|
|
84
98
|
extra_rdoc_files: []
|
|
85
99
|
files:
|
|
100
|
+
- ".erb_lint.yml"
|
|
86
101
|
- ".github/workflows/ci.yml"
|
|
87
102
|
- ".gitignore"
|
|
103
|
+
- ".rubocop.yml"
|
|
88
104
|
- Appraisals
|
|
89
105
|
- CHANGELOG.md
|
|
90
106
|
- Gemfile
|
|
@@ -93,6 +109,7 @@ files:
|
|
|
93
109
|
- Rakefile
|
|
94
110
|
- app/assets/stylesheets/devise-otp.css
|
|
95
111
|
- app/controllers/devise_otp/devise/otp_credentials_controller.rb
|
|
112
|
+
- app/controllers/devise_otp/devise/otp_persistence_controller.rb
|
|
96
113
|
- app/controllers/devise_otp/devise/otp_tokens_controller.rb
|
|
97
114
|
- app/views/devise/otp_credentials/refresh.html.erb
|
|
98
115
|
- app/views/devise/otp_credentials/show.html.erb
|
|
@@ -102,6 +119,8 @@ files:
|
|
|
102
119
|
- app/views/devise/otp_tokens/recovery.html.erb
|
|
103
120
|
- app/views/devise/otp_tokens/recovery_codes.text.erb
|
|
104
121
|
- app/views/devise/otp_tokens/show.html.erb
|
|
122
|
+
- bin/erb_lint
|
|
123
|
+
- bin/rubocop
|
|
105
124
|
- config/locales/en.yml
|
|
106
125
|
- devise-otp.gemspec
|
|
107
126
|
- gemfiles/rails_7.1.gemfile
|
|
@@ -136,8 +155,10 @@ files:
|
|
|
136
155
|
- test/dummy/app/helpers/posts_helper.rb
|
|
137
156
|
- test/dummy/app/mailers/.gitkeep
|
|
138
157
|
- test/dummy/app/models/admin.rb
|
|
158
|
+
- test/dummy/app/models/lockable_user.rb
|
|
139
159
|
- test/dummy/app/models/non_otp_user.rb
|
|
140
160
|
- test/dummy/app/models/post.rb
|
|
161
|
+
- test/dummy/app/models/rememberable_user.rb
|
|
141
162
|
- test/dummy/app/models/user.rb
|
|
142
163
|
- test/dummy/app/views/admin_posts/index.html.erb
|
|
143
164
|
- test/dummy/app/views/base/home.html.erb
|
|
@@ -148,6 +169,7 @@ files:
|
|
|
148
169
|
- test/dummy/app/views/posts/index.html.erb
|
|
149
170
|
- test/dummy/app/views/posts/new.html.erb
|
|
150
171
|
- test/dummy/app/views/posts/show.html.erb
|
|
172
|
+
- test/dummy/app/views/shared/_navbar.html.erb
|
|
151
173
|
- test/dummy/config.ru
|
|
152
174
|
- test/dummy/config/application.rb
|
|
153
175
|
- test/dummy/config/boot.rb
|
|
@@ -174,6 +196,11 @@ files:
|
|
|
174
196
|
- test/dummy/db/migrate/20240604000003_devise_otp_add_to_admins.rb
|
|
175
197
|
- test/dummy/db/migrate/20250718092451_create_non_otp_users.rb
|
|
176
198
|
- test/dummy/db/migrate/20250718092536_add_devise_to_non_otp_users.rb
|
|
199
|
+
- test/dummy/db/migrate/20250731000001_create_lockable_users.rb
|
|
200
|
+
- test/dummy/db/migrate/20250731000002_add_devise_to_lockable_users.rb
|
|
201
|
+
- test/dummy/db/migrate/20250731000003_devise_otp_add_to_lockable_users.rb
|
|
202
|
+
- test/dummy/db/migrate/20250817221304_create_rememberable_users.rb
|
|
203
|
+
- test/dummy/db/migrate/20250818030305_add_devise_otp_to_rememberable_users.rb
|
|
177
204
|
- test/dummy/db/schema.rb
|
|
178
205
|
- test/dummy/db/seeds.rb
|
|
179
206
|
- test/dummy/lib/assets/.gitkeep
|
|
@@ -184,9 +211,12 @@ files:
|
|
|
184
211
|
- test/dummy/script/rails
|
|
185
212
|
- test/integration/disable_token_test.rb
|
|
186
213
|
- test/integration/enable_otp_form_test.rb
|
|
214
|
+
- test/integration/lockable_test.rb
|
|
187
215
|
- test/integration/non_otp_user_models_test.rb
|
|
216
|
+
- test/integration/otp_drift_test.rb
|
|
188
217
|
- test/integration/persistence_test.rb
|
|
189
218
|
- test/integration/refresh_test.rb
|
|
219
|
+
- test/integration/rememberable_test.rb
|
|
190
220
|
- test/integration/reset_token_test.rb
|
|
191
221
|
- test/integration/sign_in_test.rb
|
|
192
222
|
- test/integration/trackable_test.rb
|