rodauth 1.22.0 → 1.23.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/CHANGELOG +12 -0
- data/README.rdoc +5 -3
- data/doc/email_base.rdoc +1 -0
- data/doc/release_notes/1.23.0.txt +32 -0
- data/lib/rodauth.rb +5 -2
- data/lib/rodauth/features/base.rb +8 -0
- data/lib/rodauth/features/change_password_notify.rb +1 -1
- data/lib/rodauth/features/create_account.rb +1 -1
- data/lib/rodauth/features/email_auth.rb +3 -4
- data/lib/rodauth/features/email_base.rb +7 -2
- data/lib/rodauth/features/lockout.rb +1 -1
- data/lib/rodauth/features/login.rb +6 -2
- data/lib/rodauth/features/otp.rb +6 -3
- data/lib/rodauth/features/password_expiration.rb +1 -1
- data/lib/rodauth/features/recovery_codes.rb +3 -3
- data/lib/rodauth/features/reset_password.rb +2 -2
- data/lib/rodauth/features/sms_codes.rb +5 -5
- data/lib/rodauth/features/verify_account.rb +2 -2
- data/lib/rodauth/features/verify_login_change.rb +1 -1
- data/lib/rodauth/version.rb +1 -1
- data/templates/email-auth-request-form.str +2 -2
- data/templates/reset-password-request.str +3 -3
- data/templates/unlock-account-request.str +3 -3
- data/templates/verify-account-resend.str +3 -3
- metadata +5 -43
- data/Rakefile +0 -179
- data/spec/account_expiration_spec.rb +0 -225
- data/spec/all.rb +0 -1
- data/spec/change_login_spec.rb +0 -156
- data/spec/change_password_notify_spec.rb +0 -33
- data/spec/change_password_spec.rb +0 -202
- data/spec/close_account_spec.rb +0 -162
- data/spec/confirm_password_spec.rb +0 -70
- data/spec/create_account_spec.rb +0 -127
- data/spec/disallow_common_passwords_spec.rb +0 -93
- data/spec/disallow_password_reuse_spec.rb +0 -179
- data/spec/email_auth_spec.rb +0 -285
- data/spec/http_basic_auth_spec.rb +0 -143
- data/spec/jwt_cors_spec.rb +0 -57
- data/spec/jwt_refresh_spec.rb +0 -256
- data/spec/jwt_spec.rb +0 -235
- data/spec/lockout_spec.rb +0 -250
- data/spec/login_spec.rb +0 -328
- data/spec/migrate/001_tables.rb +0 -184
- data/spec/migrate/002_account_password_hash_column.rb +0 -11
- data/spec/migrate_password/001_tables.rb +0 -73
- data/spec/migrate_travis/001_tables.rb +0 -141
- data/spec/password_complexity_spec.rb +0 -109
- data/spec/password_expiration_spec.rb +0 -244
- data/spec/password_grace_period_spec.rb +0 -93
- data/spec/remember_spec.rb +0 -451
- data/spec/reset_password_spec.rb +0 -229
- data/spec/rodauth_spec.rb +0 -343
- data/spec/session_expiration_spec.rb +0 -58
- data/spec/single_session_spec.rb +0 -127
- data/spec/spec_helper.rb +0 -327
- data/spec/two_factor_spec.rb +0 -1462
- data/spec/update_password_hash_spec.rb +0 -40
- data/spec/verify_account_grace_period_spec.rb +0 -171
- data/spec/verify_account_spec.rb +0 -240
- data/spec/verify_change_login_spec.rb +0 -46
- data/spec/verify_login_change_spec.rb +0 -232
- data/spec/views/layout-other.str +0 -11
- data/spec/views/layout.str +0 -11
- data/spec/views/login.str +0 -21
data/spec/two_factor_spec.rb
DELETED
@@ -1,1462 +0,0 @@
|
|
1
|
-
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
require 'rotp'
|
4
|
-
|
5
|
-
describe 'Rodauth OTP feature' do
|
6
|
-
secret_length = (ROTP::Base32.respond_to?(:random_base32) ? ROTP::Base32.random_base32 : ROTP::Base32.random).length
|
7
|
-
|
8
|
-
def reset_otp_last_use
|
9
|
-
DB[:account_otp_keys].update(:last_use=>Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, :seconds=>600))
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should allow two factor authentication setup, login, recovery, removal" do
|
13
|
-
sms_phone = sms_message = nil
|
14
|
-
hmac_secret = '123'
|
15
|
-
rodauth do
|
16
|
-
enable :login, :logout, :otp, :recovery_codes, :sms_codes
|
17
|
-
otp_drift 10
|
18
|
-
hmac_secret do
|
19
|
-
hmac_secret
|
20
|
-
end
|
21
|
-
sms_send do |phone, msg|
|
22
|
-
proc{super(phone, msg)}.must_raise NotImplementedError
|
23
|
-
sms_phone = phone
|
24
|
-
sms_message = msg
|
25
|
-
end
|
26
|
-
end
|
27
|
-
roda do |r|
|
28
|
-
r.rodauth
|
29
|
-
|
30
|
-
r.redirect '/login' unless rodauth.logged_in?
|
31
|
-
|
32
|
-
if rodauth.two_factor_authentication_setup?
|
33
|
-
r.redirect '/otp-auth' unless rodauth.authenticated?
|
34
|
-
view :content=>"With OTP"
|
35
|
-
else
|
36
|
-
view :content=>"Without OTP"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
login
|
41
|
-
page.html.must_include('Without OTP')
|
42
|
-
|
43
|
-
%w'/otp-disable /recovery-auth /recovery-codes /sms-setup /sms-disable /sms-confirm /sms-request /sms-auth /otp-auth'.each do |path|
|
44
|
-
visit path
|
45
|
-
page.find('#error_flash').text.must_equal 'This account has not been setup for two factor authentication'
|
46
|
-
page.current_path.must_equal '/otp-setup'
|
47
|
-
end
|
48
|
-
|
49
|
-
page.title.must_equal 'Setup Two Factor Authentication'
|
50
|
-
page.html.must_include '<svg'
|
51
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
52
|
-
totp = ROTP::TOTP.new(secret)
|
53
|
-
fill_in 'Password', :with=>'asdf'
|
54
|
-
click_button 'Setup Two Factor Authentication'
|
55
|
-
page.find('#error_flash').text.must_equal 'Error setting up two factor authentication'
|
56
|
-
page.html.must_include 'invalid password'
|
57
|
-
|
58
|
-
fill_in 'Password', :with=>'0123456789'
|
59
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
60
|
-
click_button 'Setup Two Factor Authentication'
|
61
|
-
page.find('#error_flash').text.must_equal 'Error setting up two factor authentication'
|
62
|
-
page.html.must_include 'Invalid authentication code'
|
63
|
-
|
64
|
-
hmac_secret = "321"
|
65
|
-
fill_in 'Password', :with=>'0123456789'
|
66
|
-
fill_in 'Authentication Code', :with=>totp.now
|
67
|
-
click_button 'Setup Two Factor Authentication'
|
68
|
-
page.find('#error_flash').text.must_equal 'Error setting up two factor authentication'
|
69
|
-
|
70
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
71
|
-
totp = ROTP::TOTP.new(secret)
|
72
|
-
fill_in 'Password', :with=>'0123456789'
|
73
|
-
fill_in 'Authentication Code', :with=>totp.now
|
74
|
-
click_button 'Setup Two Factor Authentication'
|
75
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication is now setup'
|
76
|
-
page.current_path.must_equal '/'
|
77
|
-
page.html.must_include 'With OTP'
|
78
|
-
|
79
|
-
logout
|
80
|
-
login
|
81
|
-
page.current_path.must_equal '/otp-auth'
|
82
|
-
|
83
|
-
page.find_by_id('otp-auth-code')[:autocomplete].must_equal 'off'
|
84
|
-
|
85
|
-
%w'/otp-disable /recovery-codes /otp-setup /sms-setup /sms-disable /sms-confirm'.each do |path|
|
86
|
-
visit path
|
87
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
88
|
-
page.current_path.must_equal '/otp-auth'
|
89
|
-
end
|
90
|
-
|
91
|
-
page.title.must_equal 'Enter Authentication Code'
|
92
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
93
|
-
click_button 'Authenticate via 2nd Factor'
|
94
|
-
page.find('#error_flash').text.must_equal 'Error logging in via two factor authentication'
|
95
|
-
page.html.must_include 'Invalid authentication code'
|
96
|
-
|
97
|
-
fill_in 'Authentication Code', :with=>"#{totp.now[0..2]} #{totp.now[3..-1]}"
|
98
|
-
click_button 'Authenticate via 2nd Factor'
|
99
|
-
page.find('#error_flash').text.must_equal 'Error logging in via two factor authentication'
|
100
|
-
page.html.must_include 'Invalid authentication code'
|
101
|
-
reset_otp_last_use
|
102
|
-
|
103
|
-
hmac_secret = '123'
|
104
|
-
fill_in 'Authentication Code', :with=>"#{totp.now[0..2]} #{totp.now[3..-1]}"
|
105
|
-
click_button 'Authenticate via 2nd Factor'
|
106
|
-
page.find('#error_flash').text.must_equal 'Error logging in via two factor authentication'
|
107
|
-
page.html.must_include 'Invalid authentication code'
|
108
|
-
reset_otp_last_use
|
109
|
-
|
110
|
-
hmac_secret = '321'
|
111
|
-
fill_in 'Authentication Code', :with=>"#{totp.now[0..2]} #{totp.now[3..-1]}"
|
112
|
-
click_button 'Authenticate via 2nd Factor'
|
113
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
114
|
-
page.html.must_include 'With OTP'
|
115
|
-
reset_otp_last_use
|
116
|
-
|
117
|
-
visit '/otp-setup'
|
118
|
-
page.find('#error_flash').text.must_equal 'You have already setup two factor authentication'
|
119
|
-
|
120
|
-
%w'/otp-auth /recovery-auth /sms-request /sms-auth'.each do |path|
|
121
|
-
visit path
|
122
|
-
page.find('#error_flash').text.must_equal 'Already authenticated via 2nd factor'
|
123
|
-
end
|
124
|
-
|
125
|
-
visit '/sms-disable'
|
126
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has not been setup yet.'
|
127
|
-
|
128
|
-
visit '/sms-setup'
|
129
|
-
page.title.must_equal 'Setup SMS Backup Number'
|
130
|
-
fill_in 'Password', :with=>'012345678'
|
131
|
-
fill_in 'Phone Number', :with=>'(123) 456'
|
132
|
-
click_button 'Setup SMS Backup Number'
|
133
|
-
page.find('#error_flash').text.must_equal 'Error setting up SMS authentication'
|
134
|
-
page.html.must_include 'invalid password'
|
135
|
-
|
136
|
-
fill_in 'Password', :with=>'0123456789'
|
137
|
-
click_button 'Setup SMS Backup Number'
|
138
|
-
page.find('#error_flash').text.must_equal 'Error setting up SMS authentication'
|
139
|
-
page.html.must_include 'invalid SMS phone number'
|
140
|
-
|
141
|
-
fill_in 'Password', :with=>'0123456789'
|
142
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
143
|
-
click_button 'Setup SMS Backup Number'
|
144
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication needs confirmation.'
|
145
|
-
sms_phone.must_equal '1234567890'
|
146
|
-
sms_message.must_match(/\ASMS confirmation code for www\.example\.com is \d{12}\z/)
|
147
|
-
|
148
|
-
page.title.must_equal 'Confirm SMS Backup Number'
|
149
|
-
fill_in 'SMS Code', :with=>"asdf"
|
150
|
-
click_button 'Confirm SMS Backup Number'
|
151
|
-
page.find('#error_flash').text.must_equal 'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'
|
152
|
-
|
153
|
-
fill_in 'Password', :with=>'0123456789'
|
154
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
155
|
-
click_button 'Setup SMS Backup Number'
|
156
|
-
|
157
|
-
visit '/sms-setup'
|
158
|
-
page.find('#error_flash').text.must_equal 'SMS authentication needs confirmation.'
|
159
|
-
page.title.must_equal 'Confirm SMS Backup Number'
|
160
|
-
|
161
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
162
|
-
sms_code = sms_message[/\d{12}\z/]
|
163
|
-
fill_in 'SMS Code', :with=>sms_code
|
164
|
-
click_button 'Confirm SMS Backup Number'
|
165
|
-
page.find('#error_flash').text.must_equal 'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'
|
166
|
-
|
167
|
-
fill_in 'Password', :with=>'0123456789'
|
168
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
169
|
-
click_button 'Setup SMS Backup Number'
|
170
|
-
sms_code = sms_message[/\d{12}\z/]
|
171
|
-
fill_in 'SMS Code', :with=>sms_code
|
172
|
-
click_button 'Confirm SMS Backup Number'
|
173
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication has been setup.'
|
174
|
-
|
175
|
-
%w'/sms-setup /sms-confirm'.each do |path|
|
176
|
-
visit path
|
177
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has already been setup.'
|
178
|
-
page.current_path.must_equal '/'
|
179
|
-
end
|
180
|
-
|
181
|
-
logout
|
182
|
-
login
|
183
|
-
|
184
|
-
visit '/sms-auth'
|
185
|
-
page.current_path.must_equal '/sms-request'
|
186
|
-
page.find('#error_flash').text.must_equal 'No current SMS code for this account'
|
187
|
-
|
188
|
-
sms_phone = sms_message = nil
|
189
|
-
page.title.must_equal 'Send SMS Code'
|
190
|
-
click_button 'Send SMS Code'
|
191
|
-
sms_phone.must_equal '1234567890'
|
192
|
-
sms_message.must_match(/\ASMS authentication code for www\.example\.com is \d{6}\z/)
|
193
|
-
sms_code = sms_message[/\d{6}\z/]
|
194
|
-
|
195
|
-
fill_in 'SMS Code', :with=>"asdf"
|
196
|
-
click_button 'Authenticate via SMS Code'
|
197
|
-
page.html.must_include 'invalid SMS code'
|
198
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
199
|
-
|
200
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
201
|
-
fill_in 'SMS Code', :with=>sms_code
|
202
|
-
click_button 'Authenticate via SMS Code'
|
203
|
-
page.find('#error_flash').text.must_equal 'No current SMS code for this account'
|
204
|
-
|
205
|
-
click_button 'Send SMS Code'
|
206
|
-
sms_code = sms_message[/\d{6}\z/]
|
207
|
-
fill_in 'SMS Code', :with=>sms_code
|
208
|
-
click_button 'Authenticate via SMS Code'
|
209
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
210
|
-
|
211
|
-
logout
|
212
|
-
login
|
213
|
-
|
214
|
-
visit '/sms-request'
|
215
|
-
click_button 'Send SMS Code'
|
216
|
-
|
217
|
-
5.times do
|
218
|
-
click_button 'Authenticate via SMS Code'
|
219
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
220
|
-
page.current_path.must_equal '/sms-auth'
|
221
|
-
end
|
222
|
-
|
223
|
-
click_button 'Authenticate via SMS Code'
|
224
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has been locked out.'
|
225
|
-
page.current_path.must_equal '/otp-auth'
|
226
|
-
|
227
|
-
visit '/sms-request'
|
228
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has been locked out.'
|
229
|
-
page.current_path.must_equal '/otp-auth'
|
230
|
-
|
231
|
-
fill_in 'Authentication Code', :with=>totp.now
|
232
|
-
click_button 'Authenticate via 2nd Factor'
|
233
|
-
|
234
|
-
visit '/sms-disable'
|
235
|
-
page.title.must_equal 'Disable Backup SMS Authentication'
|
236
|
-
fill_in 'Password', :with=>'012345678'
|
237
|
-
click_button 'Disable Backup SMS Authentication'
|
238
|
-
page.find('#error_flash').text.must_equal 'Error disabling SMS authentication'
|
239
|
-
page.html.must_include 'invalid password'
|
240
|
-
|
241
|
-
fill_in 'Password', :with=>'0123456789'
|
242
|
-
click_button 'Disable Backup SMS Authentication'
|
243
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication has been disabled.'
|
244
|
-
page.current_path.must_equal '/'
|
245
|
-
|
246
|
-
visit '/sms-setup'
|
247
|
-
page.title.must_equal 'Setup SMS Backup Number'
|
248
|
-
fill_in 'Password', :with=>'0123456789'
|
249
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
250
|
-
click_button 'Setup SMS Backup Number'
|
251
|
-
sms_code = sms_message[/\d{12}\z/]
|
252
|
-
fill_in 'SMS Code', :with=>sms_code
|
253
|
-
click_button 'Confirm SMS Backup Number'
|
254
|
-
|
255
|
-
visit '/recovery-codes'
|
256
|
-
page.title.must_equal 'View Authentication Recovery Codes'
|
257
|
-
fill_in 'Password', :with=>'012345678'
|
258
|
-
click_button 'View Authentication Recovery Codes'
|
259
|
-
page.find('#error_flash').text.must_equal 'Unable to view recovery codes.'
|
260
|
-
page.html.must_include 'invalid password'
|
261
|
-
|
262
|
-
fill_in 'Password', :with=>'0123456789'
|
263
|
-
click_button 'View Authentication Recovery Codes'
|
264
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
265
|
-
recovery_codes = find('#recovery-codes').text.split
|
266
|
-
recovery_codes.length.must_equal 16
|
267
|
-
recovery_code = recovery_codes.first
|
268
|
-
|
269
|
-
logout
|
270
|
-
login
|
271
|
-
|
272
|
-
5.times do
|
273
|
-
page.title.must_equal 'Enter Authentication Code'
|
274
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
275
|
-
click_button 'Authenticate via 2nd Factor'
|
276
|
-
page.find('#error_flash').text.must_equal 'Error logging in via two factor authentication'
|
277
|
-
page.html.must_include 'Invalid authentication code'
|
278
|
-
end
|
279
|
-
|
280
|
-
page.title.must_equal 'Enter Authentication Code'
|
281
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
282
|
-
click_button 'Authenticate via 2nd Factor'
|
283
|
-
page.find('#error_flash').text.must_equal 'Authentication code use locked out due to numerous failures. Can use recovery code to unlock. Can use SMS code to unlock.'
|
284
|
-
|
285
|
-
click_button 'Send SMS Code'
|
286
|
-
|
287
|
-
5.times do
|
288
|
-
click_button 'Authenticate via SMS Code'
|
289
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
290
|
-
end
|
291
|
-
|
292
|
-
click_button 'Authenticate via SMS Code'
|
293
|
-
page.find('#error_flash').text.must_equal 'Authentication code use locked out due to numerous failures. Can use recovery code to unlock.'
|
294
|
-
|
295
|
-
page.title.must_equal 'Enter Authentication Recovery Code'
|
296
|
-
fill_in 'Recovery Code', :with=>"asdf"
|
297
|
-
click_button 'Authenticate via Recovery Code'
|
298
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via recovery code.'
|
299
|
-
page.html.must_include 'Invalid recovery code'
|
300
|
-
|
301
|
-
fill_in 'Recovery Code', :with=>recovery_code
|
302
|
-
click_button 'Authenticate via Recovery Code'
|
303
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
304
|
-
page.html.must_include 'With OTP'
|
305
|
-
|
306
|
-
visit '/recovery-codes'
|
307
|
-
fill_in 'Password', :with=>'0123456789'
|
308
|
-
click_button 'View Authentication Recovery Codes'
|
309
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
310
|
-
page.html.wont_include(recovery_code)
|
311
|
-
find('#recovery-codes').text.split.length.must_equal 15
|
312
|
-
|
313
|
-
click_button 'Add Authentication Recovery Codes'
|
314
|
-
page.find('#error_flash').text.must_equal 'Unable to add recovery codes.'
|
315
|
-
page.html.must_include 'invalid password'
|
316
|
-
|
317
|
-
fill_in 'Password', :with=>'0123456789'
|
318
|
-
click_button 'View Authentication Recovery Codes'
|
319
|
-
find('#recovery-codes').text.split.length.must_equal 15
|
320
|
-
fill_in 'Password', :with=>'0123456789'
|
321
|
-
click_button 'Add Authentication Recovery Codes'
|
322
|
-
page.find('#notice_flash').text.must_equal 'Additional authentication recovery codes have been added.'
|
323
|
-
find('#recovery-codes').text.split.length.must_equal 16
|
324
|
-
page.html.wont_include('Add Additional Authentication Recovery Codes')
|
325
|
-
|
326
|
-
visit '/otp-disable'
|
327
|
-
fill_in 'Password', :with=>'012345678'
|
328
|
-
click_button 'Disable Two Factor Authentication'
|
329
|
-
page.find('#error_flash').text.must_equal 'Error disabling up two factor authentication'
|
330
|
-
page.html.must_include 'invalid password'
|
331
|
-
|
332
|
-
fill_in 'Password', :with=>'0123456789'
|
333
|
-
click_button 'Disable Two Factor Authentication'
|
334
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication has been disabled'
|
335
|
-
page.html.must_include 'Without OTP'
|
336
|
-
[:account_otp_keys, :account_recovery_codes, :account_sms_codes].each do |t|
|
337
|
-
DB[t].count.must_equal 0
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
it "should allow namespaced two factor authentication without password requirements" do
|
342
|
-
rodauth do
|
343
|
-
enable :login, :logout, :otp, :recovery_codes
|
344
|
-
otp_drift 10
|
345
|
-
two_factor_modifications_require_password? false
|
346
|
-
otp_digits 8
|
347
|
-
prefix "/auth"
|
348
|
-
end
|
349
|
-
roda do |r|
|
350
|
-
r.on "auth" do
|
351
|
-
r.rodauth
|
352
|
-
end
|
353
|
-
|
354
|
-
r.redirect '/auth/login' unless rodauth.logged_in?
|
355
|
-
|
356
|
-
if rodauth.two_factor_authentication_setup?
|
357
|
-
r.redirect '/auth/otp-auth' unless rodauth.two_factor_authenticated?
|
358
|
-
view :content=>"With OTP"
|
359
|
-
else
|
360
|
-
view :content=>"Without OTP"
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
login
|
365
|
-
page.html.must_include('Without OTP')
|
366
|
-
|
367
|
-
%w'/auth/otp-disable /auth/recovery-auth /auth/recovery-codes /auth/otp-auth'.each do
|
368
|
-
visit '/auth/otp-disable'
|
369
|
-
page.find('#error_flash').text.must_equal 'This account has not been setup for two factor authentication'
|
370
|
-
page.current_path.must_equal '/auth/otp-setup'
|
371
|
-
end
|
372
|
-
|
373
|
-
page.title.must_equal 'Setup Two Factor Authentication'
|
374
|
-
page.html.must_include '<svg'
|
375
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
376
|
-
totp = ROTP::TOTP.new(secret, :digits=>8)
|
377
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
378
|
-
click_button 'Setup Two Factor Authentication'
|
379
|
-
page.find('#error_flash').text.must_equal 'Error setting up two factor authentication'
|
380
|
-
page.html.must_include 'Invalid authentication code'
|
381
|
-
|
382
|
-
fill_in 'Authentication Code', :with=>totp.now
|
383
|
-
click_button 'Setup Two Factor Authentication'
|
384
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication is now setup'
|
385
|
-
page.current_path.must_equal '/'
|
386
|
-
page.html.must_include 'With OTP'
|
387
|
-
reset_otp_last_use
|
388
|
-
|
389
|
-
visit '/auth/logout'
|
390
|
-
click_button 'Logout'
|
391
|
-
login(:visit=>false)
|
392
|
-
|
393
|
-
page.current_path.must_equal '/auth/otp-auth'
|
394
|
-
|
395
|
-
visit '/auth/otp-disable'
|
396
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
397
|
-
page.current_path.must_equal '/auth/otp-auth'
|
398
|
-
|
399
|
-
visit '/auth/recovery-codes'
|
400
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
401
|
-
page.current_path.must_equal '/auth/otp-auth'
|
402
|
-
|
403
|
-
visit '/auth/otp-setup'
|
404
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
405
|
-
page.current_path.must_equal '/auth/otp-auth'
|
406
|
-
|
407
|
-
page.title.must_equal 'Enter Authentication Code'
|
408
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
409
|
-
click_button 'Authenticate via 2nd Factor'
|
410
|
-
page.find('#error_flash').text.must_equal 'Error logging in via two factor authentication'
|
411
|
-
page.html.must_include 'Invalid authentication code'
|
412
|
-
|
413
|
-
fill_in 'Authentication Code', :with=>totp.now
|
414
|
-
click_button 'Authenticate via 2nd Factor'
|
415
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
416
|
-
page.html.must_include 'With OTP'
|
417
|
-
reset_otp_last_use
|
418
|
-
|
419
|
-
visit '/auth/otp-auth'
|
420
|
-
page.find('#error_flash').text.must_equal 'Already authenticated via 2nd factor'
|
421
|
-
|
422
|
-
visit '/auth/otp-setup'
|
423
|
-
page.find('#error_flash').text.must_equal 'You have already setup two factor authentication'
|
424
|
-
|
425
|
-
visit '/auth/recovery-auth'
|
426
|
-
page.find('#error_flash').text.must_equal 'Already authenticated via 2nd factor'
|
427
|
-
|
428
|
-
visit '/auth/recovery-codes'
|
429
|
-
page.title.must_equal 'View Authentication Recovery Codes'
|
430
|
-
click_button 'View Authentication Recovery Codes'
|
431
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
432
|
-
recovery_codes = find('#recovery-codes').text.split
|
433
|
-
recovery_codes.length.must_equal 16
|
434
|
-
recovery_code = recovery_codes.first
|
435
|
-
|
436
|
-
visit '/auth/logout'
|
437
|
-
click_button 'Logout'
|
438
|
-
login(:visit=>false)
|
439
|
-
|
440
|
-
5.times do
|
441
|
-
page.title.must_equal 'Enter Authentication Code'
|
442
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
443
|
-
click_button 'Authenticate via 2nd Factor'
|
444
|
-
page.find('#error_flash').text.must_equal 'Error logging in via two factor authentication'
|
445
|
-
page.html.must_include 'Invalid authentication code'
|
446
|
-
end
|
447
|
-
|
448
|
-
page.title.must_equal 'Enter Authentication Code'
|
449
|
-
fill_in 'Authentication Code', :with=>"asdf"
|
450
|
-
click_button 'Authenticate via 2nd Factor'
|
451
|
-
|
452
|
-
page.find('#error_flash').text.must_equal 'Authentication code use locked out due to numerous failures. Can use recovery code to unlock.'
|
453
|
-
page.title.must_equal 'Enter Authentication Recovery Code'
|
454
|
-
fill_in 'Recovery Code', :with=>"asdf"
|
455
|
-
click_button 'Authenticate via Recovery Code'
|
456
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via recovery code.'
|
457
|
-
page.html.must_include 'Invalid recovery code'
|
458
|
-
fill_in 'Recovery Code', :with=>recovery_code
|
459
|
-
click_button 'Authenticate via Recovery Code'
|
460
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
461
|
-
page.html.must_include 'With OTP'
|
462
|
-
|
463
|
-
visit '/auth/recovery-codes'
|
464
|
-
click_button 'View Authentication Recovery Codes'
|
465
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
466
|
-
page.html.wont_include(recovery_code)
|
467
|
-
find('#recovery-codes').text.split.length.must_equal 15
|
468
|
-
click_button 'Add Authentication Recovery Codes'
|
469
|
-
page.find('#notice_flash').text.must_equal 'Additional authentication recovery codes have been added.'
|
470
|
-
find('#recovery-codes').text.split.length.must_equal 16
|
471
|
-
page.html.wont_include('Add Additional Authentication Recovery Codes')
|
472
|
-
|
473
|
-
visit '/auth/otp-disable'
|
474
|
-
click_button 'Disable Two Factor Authentication'
|
475
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication has been disabled'
|
476
|
-
page.html.must_include 'Without OTP'
|
477
|
-
[:account_otp_keys, :account_recovery_codes].each do |t|
|
478
|
-
DB[t].count.must_equal 0
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
it "should require login and OTP authentication to perform certain actions if user signed up for OTP" do
|
483
|
-
rodauth do
|
484
|
-
enable :login, :logout, :change_password, :change_login, :close_account, :otp, :recovery_codes
|
485
|
-
otp_drift 10
|
486
|
-
end
|
487
|
-
roda do |r|
|
488
|
-
r.rodauth
|
489
|
-
|
490
|
-
r.is "a" do
|
491
|
-
rodauth.require_authentication
|
492
|
-
view(:content=>"a")
|
493
|
-
end
|
494
|
-
|
495
|
-
view(:content=>"b")
|
496
|
-
end
|
497
|
-
|
498
|
-
%w'/change-password /change-login /close-account /a'.each do |path|
|
499
|
-
visit '/change-password'
|
500
|
-
page.current_path.must_equal '/login'
|
501
|
-
end
|
502
|
-
|
503
|
-
login
|
504
|
-
|
505
|
-
%w'/change-password /change-login /close-account /a'.each do |path|
|
506
|
-
visit path
|
507
|
-
page.current_path.must_equal path
|
508
|
-
end
|
509
|
-
|
510
|
-
visit '/otp-setup'
|
511
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
512
|
-
totp = ROTP::TOTP.new(secret)
|
513
|
-
fill_in 'Password', :with=>'0123456789'
|
514
|
-
fill_in 'Authentication Code', :with=>totp.now
|
515
|
-
click_button 'Setup Two Factor Authentication'
|
516
|
-
page.current_path.must_equal '/'
|
517
|
-
|
518
|
-
logout
|
519
|
-
login
|
520
|
-
|
521
|
-
%w'/change-password /change-login /close-account /a'.each do |path|
|
522
|
-
visit path
|
523
|
-
page.current_path.must_equal '/otp-auth'
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
it "should handle attempts to insert a duplicate recovery code" do
|
528
|
-
keys = ['a', 'a', 'b']
|
529
|
-
interval = 1000000
|
530
|
-
rodauth do
|
531
|
-
enable :login, :logout, :otp, :recovery_codes
|
532
|
-
otp_interval interval
|
533
|
-
recovery_codes_limit 2
|
534
|
-
new_recovery_code{keys.shift}
|
535
|
-
end
|
536
|
-
roda do |r|
|
537
|
-
r.rodauth
|
538
|
-
|
539
|
-
r.redirect '/login' unless rodauth.logged_in?
|
540
|
-
|
541
|
-
if rodauth.two_factor_authentication_setup?
|
542
|
-
r.redirect '/otp-auth' unless rodauth.authenticated?
|
543
|
-
view :content=>"With OTP"
|
544
|
-
else
|
545
|
-
view :content=>"Without OTP"
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
login
|
550
|
-
page.html.must_include('Without OTP')
|
551
|
-
|
552
|
-
visit '/otp-auth'
|
553
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
554
|
-
totp = ROTP::TOTP.new(secret, :interval=>interval)
|
555
|
-
fill_in 'Password', :with=>'0123456789'
|
556
|
-
fill_in 'Authentication Code', :with=>totp.now
|
557
|
-
click_button 'Setup Two Factor Authentication'
|
558
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication is now setup'
|
559
|
-
page.current_path.must_equal '/'
|
560
|
-
DB[:account_recovery_codes].select_order_map(:code).must_equal ['a', 'b']
|
561
|
-
end
|
562
|
-
|
563
|
-
it "should handle two factor lockout when using rodauth.require_two_factor_setup and rodauth.require_authentication" do
|
564
|
-
rodauth do
|
565
|
-
enable :login, :logout, :otp
|
566
|
-
otp_drift 10
|
567
|
-
end
|
568
|
-
roda do |r|
|
569
|
-
r.rodauth
|
570
|
-
rodauth.require_authentication
|
571
|
-
rodauth.require_two_factor_setup
|
572
|
-
|
573
|
-
view :content=>"Logged in"
|
574
|
-
end
|
575
|
-
|
576
|
-
login
|
577
|
-
page.title.must_equal 'Setup Two Factor Authentication'
|
578
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
579
|
-
totp = ROTP::TOTP.new(secret)
|
580
|
-
fill_in 'Password', :with=>'0123456789'
|
581
|
-
fill_in 'Authentication Code', :with=>totp.now
|
582
|
-
click_button 'Setup Two Factor Authentication'
|
583
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication is now setup'
|
584
|
-
page.current_path.must_equal '/'
|
585
|
-
page.html.must_include 'Logged in'
|
586
|
-
reset_otp_last_use
|
587
|
-
|
588
|
-
logout
|
589
|
-
login
|
590
|
-
|
591
|
-
6.times do
|
592
|
-
page.title.must_equal 'Enter Authentication Code'
|
593
|
-
fill_in 'Authentication Code', :with=>'foo'
|
594
|
-
click_button 'Authenticate via 2nd Factor'
|
595
|
-
end
|
596
|
-
page.find('#error_flash').text.must_equal 'Authentication code use locked out due to numerous failures.'
|
597
|
-
page.title.must_include 'Login'
|
598
|
-
page.current_path.must_equal '/login'
|
599
|
-
end
|
600
|
-
|
601
|
-
it "should allow two factor authentication setup, login, removal without recovery" do
|
602
|
-
rodauth do
|
603
|
-
enable :login, :logout, :otp
|
604
|
-
otp_drift 10
|
605
|
-
otp_lockout_redirect '/'
|
606
|
-
end
|
607
|
-
roda do |r|
|
608
|
-
r.rodauth
|
609
|
-
|
610
|
-
r.redirect '/login' unless rodauth.logged_in?
|
611
|
-
|
612
|
-
if rodauth.two_factor_authentication_setup?
|
613
|
-
if rodauth.otp_locked_out?
|
614
|
-
view :content=>"OTP Locked Out"
|
615
|
-
else
|
616
|
-
r.redirect '/otp-auth' unless rodauth.authenticated?
|
617
|
-
view :content=>"With OTP"
|
618
|
-
end
|
619
|
-
else
|
620
|
-
view :content=>"Without OTP"
|
621
|
-
end
|
622
|
-
end
|
623
|
-
|
624
|
-
visit '/recovery-auth'
|
625
|
-
page.current_path.must_equal '/login'
|
626
|
-
visit '/recovery-codes'
|
627
|
-
page.current_path.must_equal '/login'
|
628
|
-
|
629
|
-
login
|
630
|
-
page.html.must_include('Without OTP')
|
631
|
-
|
632
|
-
visit '/otp-setup'
|
633
|
-
page.title.must_equal 'Setup Two Factor Authentication'
|
634
|
-
page.html.must_include '<svg'
|
635
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
636
|
-
totp = ROTP::TOTP.new(secret)
|
637
|
-
fill_in 'Password', :with=>'0123456789'
|
638
|
-
fill_in 'Authentication Code', :with=>totp.now
|
639
|
-
click_button 'Setup Two Factor Authentication'
|
640
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication is now setup'
|
641
|
-
page.current_path.must_equal '/'
|
642
|
-
page.html.must_include 'With OTP'
|
643
|
-
reset_otp_last_use
|
644
|
-
|
645
|
-
logout
|
646
|
-
login
|
647
|
-
|
648
|
-
visit '/otp-auth'
|
649
|
-
6.times do
|
650
|
-
page.title.must_equal 'Enter Authentication Code'
|
651
|
-
fill_in 'Authentication Code', :with=>'foo'
|
652
|
-
click_button 'Authenticate via 2nd Factor'
|
653
|
-
end
|
654
|
-
page.find('#error_flash').text.must_equal 'Authentication code use locked out due to numerous failures.'
|
655
|
-
page.body.must_include 'OTP Locked Out'
|
656
|
-
page.current_path.must_equal '/'
|
657
|
-
DB[:account_otp_keys].update(:num_failures=>0)
|
658
|
-
|
659
|
-
visit '/otp-auth'
|
660
|
-
page.title.must_equal 'Enter Authentication Code'
|
661
|
-
page.html.wont_include 'Authenticate using recovery code'
|
662
|
-
fill_in 'Authentication Code', :with=>totp.now
|
663
|
-
click_button 'Authenticate via 2nd Factor'
|
664
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
665
|
-
page.html.must_include 'With OTP'
|
666
|
-
|
667
|
-
visit '/otp-disable'
|
668
|
-
fill_in 'Password', :with=>'0123456789'
|
669
|
-
click_button 'Disable Two Factor Authentication'
|
670
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication has been disabled'
|
671
|
-
page.html.must_include 'Without OTP'
|
672
|
-
DB[:account_otp_keys].count.must_equal 0
|
673
|
-
end
|
674
|
-
|
675
|
-
it "should remove otp data when closing accounts" do
|
676
|
-
rodauth do
|
677
|
-
enable :login, :logout, :otp, :recovery_codes, :sms_codes, :close_account
|
678
|
-
otp_drift 10
|
679
|
-
two_factor_modifications_require_password? false
|
680
|
-
close_account_requires_password? false
|
681
|
-
sms_send{|*|}
|
682
|
-
end
|
683
|
-
roda do |r|
|
684
|
-
r.rodauth
|
685
|
-
r.root{view :content=>"With OTP"}
|
686
|
-
end
|
687
|
-
|
688
|
-
login
|
689
|
-
|
690
|
-
visit '/otp-setup'
|
691
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
692
|
-
totp = ROTP::TOTP.new(secret)
|
693
|
-
fill_in 'Authentication Code', :with=>totp.now
|
694
|
-
click_button 'Setup Two Factor Authentication'
|
695
|
-
|
696
|
-
visit '/sms-setup'
|
697
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
698
|
-
click_button 'Setup SMS Backup Number'
|
699
|
-
|
700
|
-
DB[:account_otp_keys].count.must_equal 1
|
701
|
-
DB[:account_recovery_codes].count.must_equal 16
|
702
|
-
DB[:account_sms_codes].count.must_equal 1
|
703
|
-
visit '/close-account'
|
704
|
-
click_button 'Close Account'
|
705
|
-
[:account_otp_keys, :account_recovery_codes, :account_sms_codes].each do |t|
|
706
|
-
DB[t].count.must_equal 0
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
it "should have recovery_codes and sms_codes work when used without otp" do
|
711
|
-
sms_code, sms_phone, sms_message = nil
|
712
|
-
rodauth do
|
713
|
-
enable :login, :logout, :recovery_codes, :sms_codes
|
714
|
-
sms_send do |phone, msg|
|
715
|
-
sms_phone = phone
|
716
|
-
sms_message = msg
|
717
|
-
sms_code = msg[/\d+\z/]
|
718
|
-
end
|
719
|
-
end
|
720
|
-
roda do |r|
|
721
|
-
r.rodauth
|
722
|
-
|
723
|
-
r.redirect '/login' unless rodauth.logged_in?
|
724
|
-
|
725
|
-
if rodauth.two_factor_authentication_setup?
|
726
|
-
r.redirect '/sms-request' unless rodauth.authenticated?
|
727
|
-
view :content=>"With OTP"
|
728
|
-
else
|
729
|
-
view :content=>"Without OTP"
|
730
|
-
end
|
731
|
-
end
|
732
|
-
|
733
|
-
login
|
734
|
-
page.html.must_include('Without OTP')
|
735
|
-
|
736
|
-
%w'/recovery-auth /recovery-codes'.each do |path|
|
737
|
-
visit path
|
738
|
-
page.find('#error_flash').text.must_equal 'This account has not been setup for two factor authentication'
|
739
|
-
page.current_path.must_equal '/sms-setup'
|
740
|
-
end
|
741
|
-
|
742
|
-
%w'/sms-disable /sms-request /sms-auth'.each do |path|
|
743
|
-
visit path
|
744
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has not been setup yet.'
|
745
|
-
page.current_path.must_equal '/sms-setup'
|
746
|
-
end
|
747
|
-
|
748
|
-
visit '/sms-setup'
|
749
|
-
page.title.must_equal 'Setup SMS Backup Number'
|
750
|
-
fill_in 'Password', :with=>'012345678'
|
751
|
-
fill_in 'Phone Number', :with=>'(123) 456'
|
752
|
-
click_button 'Setup SMS Backup Number'
|
753
|
-
page.find('#error_flash').text.must_equal 'Error setting up SMS authentication'
|
754
|
-
page.html.must_include 'invalid password'
|
755
|
-
|
756
|
-
fill_in 'Password', :with=>'0123456789'
|
757
|
-
click_button 'Setup SMS Backup Number'
|
758
|
-
page.find('#error_flash').text.must_equal 'Error setting up SMS authentication'
|
759
|
-
page.html.must_include 'invalid SMS phone number'
|
760
|
-
|
761
|
-
fill_in 'Password', :with=>'0123456789'
|
762
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
763
|
-
click_button 'Setup SMS Backup Number'
|
764
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication needs confirmation.'
|
765
|
-
sms_phone.must_equal '1234567890'
|
766
|
-
sms_message.must_match(/\ASMS confirmation code for www\.example\.com is \d{12}\z/)
|
767
|
-
|
768
|
-
page.title.must_equal 'Confirm SMS Backup Number'
|
769
|
-
fill_in 'SMS Code', :with=>"asdf"
|
770
|
-
click_button 'Confirm SMS Backup Number'
|
771
|
-
page.find('#error_flash').text.must_equal 'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'
|
772
|
-
|
773
|
-
fill_in 'Password', :with=>'0123456789'
|
774
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
775
|
-
click_button 'Setup SMS Backup Number'
|
776
|
-
|
777
|
-
visit '/sms-setup'
|
778
|
-
page.find('#error_flash').text.must_equal 'SMS authentication needs confirmation.'
|
779
|
-
page.title.must_equal 'Confirm SMS Backup Number'
|
780
|
-
|
781
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
782
|
-
fill_in 'SMS Code', :with=>sms_code
|
783
|
-
click_button 'Confirm SMS Backup Number'
|
784
|
-
page.find('#error_flash').text.must_equal 'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'
|
785
|
-
|
786
|
-
fill_in 'Password', :with=>'0123456789'
|
787
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
788
|
-
click_button 'Setup SMS Backup Number'
|
789
|
-
fill_in 'SMS Code', :with=>sms_code
|
790
|
-
click_button 'Confirm SMS Backup Number'
|
791
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
792
|
-
|
793
|
-
visit '/recovery-codes'
|
794
|
-
page.title.must_equal 'View Authentication Recovery Codes'
|
795
|
-
fill_in 'Password', :with=>'012345678'
|
796
|
-
click_button 'View Authentication Recovery Codes'
|
797
|
-
page.find('#error_flash').text.must_equal 'Unable to view recovery codes.'
|
798
|
-
page.html.must_include 'invalid password'
|
799
|
-
|
800
|
-
fill_in 'Password', :with=>'0123456789'
|
801
|
-
click_button 'View Authentication Recovery Codes'
|
802
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
803
|
-
recovery_codes = find('#recovery-codes').text.split
|
804
|
-
recovery_codes.length.must_equal 16
|
805
|
-
recovery_code = recovery_codes.first
|
806
|
-
|
807
|
-
logout
|
808
|
-
login
|
809
|
-
page.current_path.must_equal '/sms-request'
|
810
|
-
|
811
|
-
%w'/recovery-codes /sms-setup /sms-disable /sms-confirm'.each do |path|
|
812
|
-
visit path
|
813
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
814
|
-
page.current_path.must_equal '/sms-request'
|
815
|
-
end
|
816
|
-
|
817
|
-
visit '/sms-auth'
|
818
|
-
page.current_path.must_equal '/sms-request'
|
819
|
-
page.find('#error_flash').text.must_equal 'No current SMS code for this account'
|
820
|
-
|
821
|
-
sms_phone = sms_message = nil
|
822
|
-
page.title.must_equal 'Send SMS Code'
|
823
|
-
click_button 'Send SMS Code'
|
824
|
-
sms_phone.must_equal '1234567890'
|
825
|
-
sms_message.must_match(/\ASMS authentication code for www\.example\.com is \d{6}\z/)
|
826
|
-
|
827
|
-
fill_in 'SMS Code', :with=>"asdf"
|
828
|
-
click_button 'Authenticate via SMS Code'
|
829
|
-
page.html.must_include 'invalid SMS code'
|
830
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
831
|
-
|
832
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
833
|
-
fill_in 'SMS Code', :with=>sms_code
|
834
|
-
click_button 'Authenticate via SMS Code'
|
835
|
-
page.find('#error_flash').text.must_equal 'No current SMS code for this account'
|
836
|
-
|
837
|
-
click_button 'Send SMS Code'
|
838
|
-
fill_in 'SMS Code', :with=>sms_code
|
839
|
-
click_button 'Authenticate via SMS Code'
|
840
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
841
|
-
|
842
|
-
%w'/recovery-auth /sms-request /sms-auth'.each do |path|
|
843
|
-
visit path
|
844
|
-
page.find('#error_flash').text.must_equal 'Already authenticated via 2nd factor'
|
845
|
-
end
|
846
|
-
|
847
|
-
%w'/sms-setup /sms-confirm'.each do |path|
|
848
|
-
visit path
|
849
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has already been setup.'
|
850
|
-
page.current_path.must_equal '/'
|
851
|
-
end
|
852
|
-
|
853
|
-
logout
|
854
|
-
login
|
855
|
-
|
856
|
-
click_button 'Send SMS Code'
|
857
|
-
|
858
|
-
5.times do
|
859
|
-
click_button 'Authenticate via SMS Code'
|
860
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
861
|
-
page.current_path.must_equal '/sms-auth'
|
862
|
-
end
|
863
|
-
|
864
|
-
click_button 'Authenticate via SMS Code'
|
865
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has been locked out.'
|
866
|
-
page.current_path.must_equal '/recovery-auth'
|
867
|
-
|
868
|
-
visit '/sms-request'
|
869
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has been locked out.'
|
870
|
-
page.current_path.must_equal '/recovery-auth'
|
871
|
-
|
872
|
-
page.title.must_equal 'Enter Authentication Recovery Code'
|
873
|
-
fill_in 'Recovery Code', :with=>"asdf"
|
874
|
-
click_button 'Authenticate via Recovery Code'
|
875
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via recovery code.'
|
876
|
-
page.html.must_include 'Invalid recovery code'
|
877
|
-
|
878
|
-
fill_in 'Recovery Code', :with=>recovery_code
|
879
|
-
click_button 'Authenticate via Recovery Code'
|
880
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
881
|
-
page.html.must_include 'With OTP'
|
882
|
-
|
883
|
-
visit '/recovery-codes'
|
884
|
-
fill_in 'Password', :with=>'0123456789'
|
885
|
-
click_button 'View Authentication Recovery Codes'
|
886
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
887
|
-
page.html.wont_include(recovery_code)
|
888
|
-
find('#recovery-codes').text.split.length.must_equal 15
|
889
|
-
|
890
|
-
click_button 'Add Authentication Recovery Codes'
|
891
|
-
page.find('#error_flash').text.must_equal 'Unable to add recovery codes.'
|
892
|
-
page.html.must_include 'invalid password'
|
893
|
-
|
894
|
-
visit '/sms-disable'
|
895
|
-
page.title.must_equal 'Disable Backup SMS Authentication'
|
896
|
-
fill_in 'Password', :with=>'012345678'
|
897
|
-
click_button 'Disable Backup SMS Authentication'
|
898
|
-
page.find('#error_flash').text.must_equal 'Error disabling SMS authentication'
|
899
|
-
page.html.must_include 'invalid password'
|
900
|
-
|
901
|
-
fill_in 'Password', :with=>'0123456789'
|
902
|
-
click_button 'Disable Backup SMS Authentication'
|
903
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication has been disabled.'
|
904
|
-
page.current_path.must_equal '/'
|
905
|
-
|
906
|
-
[:account_recovery_codes, :account_sms_codes].each do |t|
|
907
|
-
DB[t].count.must_equal 0
|
908
|
-
end
|
909
|
-
end
|
910
|
-
|
911
|
-
it "should have recovery_codes work when used by itself" do
|
912
|
-
rodauth do
|
913
|
-
enable :login, :logout, :recovery_codes
|
914
|
-
end
|
915
|
-
roda do |r|
|
916
|
-
r.rodauth
|
917
|
-
|
918
|
-
r.redirect '/login' unless rodauth.logged_in?
|
919
|
-
|
920
|
-
if rodauth.two_factor_authentication_setup?
|
921
|
-
r.redirect '/recovery-auth' unless rodauth.authenticated?
|
922
|
-
view :content=>"With OTP"
|
923
|
-
else
|
924
|
-
view :content=>"Without OTP"
|
925
|
-
end
|
926
|
-
end
|
927
|
-
|
928
|
-
login
|
929
|
-
page.html.must_include('Without OTP')
|
930
|
-
|
931
|
-
visit '/recovery-auth'
|
932
|
-
page.find('#error_flash').text.must_equal 'This account has not been setup for two factor authentication'
|
933
|
-
page.current_path.must_equal '/recovery-codes'
|
934
|
-
|
935
|
-
page.title.must_equal 'View Authentication Recovery Codes'
|
936
|
-
fill_in 'Password', :with=>'012345678'
|
937
|
-
click_button 'View Authentication Recovery Codes'
|
938
|
-
page.find('#error_flash').text.must_equal 'Unable to view recovery codes.'
|
939
|
-
page.html.must_include 'invalid password'
|
940
|
-
|
941
|
-
fill_in 'Password', :with=>'0123456789'
|
942
|
-
click_button 'View Authentication Recovery Codes'
|
943
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
944
|
-
recovery_codes = find('#recovery-codes').text.split
|
945
|
-
recovery_codes.length.must_equal 0
|
946
|
-
fill_in 'Password', :with=>'0123456789'
|
947
|
-
click_button 'Add Authentication Recovery Codes'
|
948
|
-
recovery_codes = find('#recovery-codes').text.split
|
949
|
-
recovery_codes.length.must_equal 16
|
950
|
-
recovery_code = recovery_codes.first
|
951
|
-
|
952
|
-
logout
|
953
|
-
login
|
954
|
-
page.current_path.must_equal '/recovery-auth'
|
955
|
-
|
956
|
-
visit '/recovery-codes'
|
957
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
958
|
-
page.current_path.must_equal '/recovery-auth'
|
959
|
-
|
960
|
-
page.title.must_equal 'Enter Authentication Recovery Code'
|
961
|
-
fill_in 'Recovery Code', :with=>"asdf"
|
962
|
-
click_button 'Authenticate via Recovery Code'
|
963
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via recovery code.'
|
964
|
-
page.html.must_include 'Invalid recovery code'
|
965
|
-
|
966
|
-
fill_in 'Recovery Code', :with=>recovery_code
|
967
|
-
click_button 'Authenticate via Recovery Code'
|
968
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
969
|
-
page.html.must_include 'With OTP'
|
970
|
-
|
971
|
-
visit '/recovery-codes'
|
972
|
-
fill_in 'Password', :with=>'0123456789'
|
973
|
-
click_button 'View Authentication Recovery Codes'
|
974
|
-
page.title.must_equal 'Authentication Recovery Codes'
|
975
|
-
page.html.wont_include(recovery_code)
|
976
|
-
page.html.wont_include('Add Authentication Recovery Codes')
|
977
|
-
find('#recovery-codes').text.split.length.must_equal 16
|
978
|
-
end
|
979
|
-
|
980
|
-
it "should have sms_codes work when used by itself" do
|
981
|
-
sms_code, sms_phone, sms_message = nil
|
982
|
-
rodauth do
|
983
|
-
enable :login, :logout, :sms_codes
|
984
|
-
sms_send do |phone, msg|
|
985
|
-
sms_phone = phone
|
986
|
-
sms_message = msg
|
987
|
-
sms_code = msg[/\d+\z/]
|
988
|
-
end
|
989
|
-
end
|
990
|
-
roda do |r|
|
991
|
-
r.rodauth
|
992
|
-
|
993
|
-
r.redirect '/login' unless rodauth.logged_in?
|
994
|
-
|
995
|
-
if rodauth.two_factor_authentication_setup?
|
996
|
-
if rodauth.sms_locked_out?
|
997
|
-
view :content=>"With SMS Locked Out"
|
998
|
-
else
|
999
|
-
rodauth.require_two_factor_authenticated
|
1000
|
-
view :content=>"With OTP"
|
1001
|
-
end
|
1002
|
-
else
|
1003
|
-
view :content=>"Without OTP"
|
1004
|
-
end
|
1005
|
-
end
|
1006
|
-
|
1007
|
-
login
|
1008
|
-
page.html.must_include('Without OTP')
|
1009
|
-
|
1010
|
-
%w'/sms-disable /sms-request /sms-auth'.each do |path|
|
1011
|
-
visit path
|
1012
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has not been setup yet.'
|
1013
|
-
page.current_path.must_equal '/sms-setup'
|
1014
|
-
end
|
1015
|
-
|
1016
|
-
visit '/sms-setup'
|
1017
|
-
page.title.must_equal 'Setup SMS Backup Number'
|
1018
|
-
fill_in 'Password', :with=>'012345678'
|
1019
|
-
fill_in 'Phone Number', :with=>'(123) 456'
|
1020
|
-
click_button 'Setup SMS Backup Number'
|
1021
|
-
page.find('#error_flash').text.must_equal 'Error setting up SMS authentication'
|
1022
|
-
page.html.must_include 'invalid password'
|
1023
|
-
|
1024
|
-
fill_in 'Password', :with=>'0123456789'
|
1025
|
-
click_button 'Setup SMS Backup Number'
|
1026
|
-
page.find('#error_flash').text.must_equal 'Error setting up SMS authentication'
|
1027
|
-
page.html.must_include 'invalid SMS phone number'
|
1028
|
-
|
1029
|
-
fill_in 'Password', :with=>'0123456789'
|
1030
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
1031
|
-
click_button 'Setup SMS Backup Number'
|
1032
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication needs confirmation.'
|
1033
|
-
sms_phone.must_equal '1234567890'
|
1034
|
-
sms_message.must_match(/\ASMS confirmation code for www\.example\.com is \d{12}\z/)
|
1035
|
-
|
1036
|
-
page.title.must_equal 'Confirm SMS Backup Number'
|
1037
|
-
fill_in 'SMS Code', :with=>"asdf"
|
1038
|
-
click_button 'Confirm SMS Backup Number'
|
1039
|
-
page.find('#error_flash').text.must_equal 'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'
|
1040
|
-
|
1041
|
-
fill_in 'Password', :with=>'0123456789'
|
1042
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
1043
|
-
click_button 'Setup SMS Backup Number'
|
1044
|
-
|
1045
|
-
visit '/sms-setup'
|
1046
|
-
page.find('#error_flash').text.must_equal 'SMS authentication needs confirmation.'
|
1047
|
-
page.title.must_equal 'Confirm SMS Backup Number'
|
1048
|
-
|
1049
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
1050
|
-
fill_in 'SMS Code', :with=>sms_code
|
1051
|
-
click_button 'Confirm SMS Backup Number'
|
1052
|
-
page.find('#error_flash').text.must_equal 'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'
|
1053
|
-
|
1054
|
-
fill_in 'Password', :with=>'0123456789'
|
1055
|
-
fill_in 'Phone Number', :with=>'(123) 456-7890'
|
1056
|
-
click_button 'Setup SMS Backup Number'
|
1057
|
-
fill_in 'SMS Code', :with=>sms_code
|
1058
|
-
click_button 'Confirm SMS Backup Number'
|
1059
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
1060
|
-
|
1061
|
-
logout
|
1062
|
-
login
|
1063
|
-
page.current_path.must_equal '/sms-request'
|
1064
|
-
|
1065
|
-
%w'/sms-setup /sms-disable /sms-confirm'.each do |path|
|
1066
|
-
visit path
|
1067
|
-
page.find('#error_flash').text.must_equal 'You need to authenticate via 2nd factor before continuing.'
|
1068
|
-
page.current_path.must_equal '/sms-request'
|
1069
|
-
end
|
1070
|
-
|
1071
|
-
visit '/sms-auth'
|
1072
|
-
page.current_path.must_equal '/sms-request'
|
1073
|
-
page.find('#error_flash').text.must_equal 'No current SMS code for this account'
|
1074
|
-
|
1075
|
-
sms_phone = sms_message = nil
|
1076
|
-
page.title.must_equal 'Send SMS Code'
|
1077
|
-
click_button 'Send SMS Code'
|
1078
|
-
sms_phone.must_equal '1234567890'
|
1079
|
-
sms_message.must_match(/\ASMS authentication code for www\.example\.com is \d{6}\z/)
|
1080
|
-
|
1081
|
-
fill_in 'SMS Code', :with=>"asdf"
|
1082
|
-
click_button 'Authenticate via SMS Code'
|
1083
|
-
page.html.must_include 'invalid SMS code'
|
1084
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
1085
|
-
|
1086
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
1087
|
-
fill_in 'SMS Code', :with=>sms_code
|
1088
|
-
click_button 'Authenticate via SMS Code'
|
1089
|
-
page.find('#error_flash').text.must_equal 'No current SMS code for this account'
|
1090
|
-
|
1091
|
-
click_button 'Send SMS Code'
|
1092
|
-
fill_in 'SMS Code', :with=>sms_code
|
1093
|
-
click_button 'Authenticate via SMS Code'
|
1094
|
-
page.find('#notice_flash').text.must_equal 'You have been authenticated via 2nd factor'
|
1095
|
-
|
1096
|
-
%w'/sms-request /sms-auth'.each do |path|
|
1097
|
-
visit path
|
1098
|
-
page.find('#error_flash').text.must_equal 'Already authenticated via 2nd factor'
|
1099
|
-
end
|
1100
|
-
|
1101
|
-
%w'/sms-setup /sms-confirm'.each do |path|
|
1102
|
-
visit path
|
1103
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has already been setup.'
|
1104
|
-
page.current_path.must_equal '/'
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
logout
|
1108
|
-
login
|
1109
|
-
|
1110
|
-
click_button 'Send SMS Code'
|
1111
|
-
|
1112
|
-
5.times do
|
1113
|
-
click_button 'Authenticate via SMS Code'
|
1114
|
-
page.find('#error_flash').text.must_equal 'Error authenticating via SMS code.'
|
1115
|
-
page.current_path.must_equal '/sms-auth'
|
1116
|
-
end
|
1117
|
-
|
1118
|
-
click_button 'Authenticate via SMS Code'
|
1119
|
-
page.body.must_include "With SMS Locked Out"
|
1120
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has been locked out.'
|
1121
|
-
page.current_path.must_equal '/'
|
1122
|
-
|
1123
|
-
visit '/sms-request'
|
1124
|
-
page.find('#error_flash').text.must_equal 'SMS authentication has been locked out.'
|
1125
|
-
page.current_path.must_equal '/'
|
1126
|
-
|
1127
|
-
DB[:account_sms_codes].update(:num_failures=>0)
|
1128
|
-
visit '/sms-request'
|
1129
|
-
click_button 'Send SMS Code'
|
1130
|
-
fill_in 'SMS Code', :with=>sms_code
|
1131
|
-
click_button 'Authenticate via SMS Code'
|
1132
|
-
|
1133
|
-
visit '/sms-disable'
|
1134
|
-
page.title.must_equal 'Disable Backup SMS Authentication'
|
1135
|
-
fill_in 'Password', :with=>'012345678'
|
1136
|
-
click_button 'Disable Backup SMS Authentication'
|
1137
|
-
page.find('#error_flash').text.must_equal 'Error disabling SMS authentication'
|
1138
|
-
page.html.must_include 'invalid password'
|
1139
|
-
|
1140
|
-
fill_in 'Password', :with=>'0123456789'
|
1141
|
-
click_button 'Disable Backup SMS Authentication'
|
1142
|
-
page.find('#notice_flash').text.must_equal 'SMS authentication has been disabled.'
|
1143
|
-
page.current_path.must_equal '/'
|
1144
|
-
|
1145
|
-
DB[:account_sms_codes].count.must_equal 0
|
1146
|
-
end
|
1147
|
-
|
1148
|
-
it "should allow two factor authentication via jwt" do
|
1149
|
-
hmac_secret = sms_phone = sms_message = sms_code = nil
|
1150
|
-
rodauth do
|
1151
|
-
enable :login, :logout, :otp, :recovery_codes, :sms_codes
|
1152
|
-
otp_drift 10
|
1153
|
-
hmac_secret do
|
1154
|
-
hmac_secret
|
1155
|
-
end
|
1156
|
-
sms_send do |phone, msg|
|
1157
|
-
sms_phone = phone
|
1158
|
-
sms_message = msg
|
1159
|
-
sms_code = msg[/\d+\z/]
|
1160
|
-
end
|
1161
|
-
end
|
1162
|
-
roda(:jwt) do |r|
|
1163
|
-
r.rodauth
|
1164
|
-
|
1165
|
-
if rodauth.logged_in?
|
1166
|
-
if rodauth.two_factor_authentication_setup?
|
1167
|
-
if rodauth.authenticated?
|
1168
|
-
[1]
|
1169
|
-
else
|
1170
|
-
[2]
|
1171
|
-
end
|
1172
|
-
else
|
1173
|
-
[3]
|
1174
|
-
end
|
1175
|
-
else
|
1176
|
-
[4]
|
1177
|
-
end
|
1178
|
-
end
|
1179
|
-
|
1180
|
-
json_request.must_equal [200, [4]]
|
1181
|
-
json_login
|
1182
|
-
json_request.must_equal [200, [3]]
|
1183
|
-
|
1184
|
-
%w'/otp-disable /recovery-auth /recovery-codes /sms-setup /sms-confirm /otp-auth'.each do |path|
|
1185
|
-
json_request(path).must_equal [403, {'error'=>'This account has not been setup for two factor authentication'}]
|
1186
|
-
end
|
1187
|
-
%w'/sms-disable /sms-request /sms-auth'.each do |path|
|
1188
|
-
json_request(path).must_equal [403, {'error'=>'SMS authentication has not been setup yet.'}]
|
1189
|
-
end
|
1190
|
-
|
1191
|
-
secret = (ROTP::Base32.respond_to?(:random_base32) ? ROTP::Base32.random_base32 : ROTP::Base32.random).downcase
|
1192
|
-
totp = ROTP::TOTP.new(secret)
|
1193
|
-
|
1194
|
-
res = json_request('/otp-setup', :password=>'123456', :otp_secret=>secret)
|
1195
|
-
res.must_equal [401, {'error'=>'Error setting up two factor authentication', "field-error"=>["password", 'invalid password']}]
|
1196
|
-
|
1197
|
-
res = json_request('/otp-setup', :password=>'0123456789', :otp=>'adsf', :otp_secret=>secret)
|
1198
|
-
res.must_equal [401, {'error'=>'Error setting up two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1199
|
-
|
1200
|
-
res = json_request('/otp-setup', :password=>'0123456789', :otp=>'adsf', :otp_secret=>'asdf')
|
1201
|
-
res.must_equal [422, {'error'=>'Error setting up two factor authentication', "field-error"=>["otp_secret", 'invalid secret']}]
|
1202
|
-
|
1203
|
-
res = json_request('/otp-setup', :password=>'0123456789', :otp=>totp.now, :otp_secret=>secret)
|
1204
|
-
res.must_equal [200, {'success'=>'Two factor authentication is now setup'}]
|
1205
|
-
reset_otp_last_use
|
1206
|
-
|
1207
|
-
json_logout
|
1208
|
-
json_login
|
1209
|
-
json_request.must_equal [200, [2]]
|
1210
|
-
|
1211
|
-
%w'/otp-disable /recovery-codes /otp-setup /sms-setup /sms-disable /sms-confirm'.each do |path|
|
1212
|
-
json_request(path).must_equal [401, {'error'=>'You need to authenticate via 2nd factor before continuing.'}]
|
1213
|
-
end
|
1214
|
-
|
1215
|
-
res = json_request('/otp-auth', :otp=>'adsf')
|
1216
|
-
res.must_equal [401, {'error'=>'Error logging in via two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1217
|
-
|
1218
|
-
res = json_request('/otp-auth', :otp=>totp.now)
|
1219
|
-
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
1220
|
-
json_request.must_equal [200, [1]]
|
1221
|
-
reset_otp_last_use
|
1222
|
-
|
1223
|
-
res = json_request('/otp-setup')
|
1224
|
-
res.must_equal [400, {'error'=>'You have already setup two factor authentication'}]
|
1225
|
-
|
1226
|
-
%w'/otp-auth /recovery-auth /sms-request /sms-auth'.each do |path|
|
1227
|
-
res = json_request(path)
|
1228
|
-
res.must_equal [403, {'error'=>'Already authenticated via 2nd factor'}]
|
1229
|
-
end
|
1230
|
-
|
1231
|
-
res = json_request('/sms-disable')
|
1232
|
-
res.must_equal [403, {'error'=>'SMS authentication has not been setup yet.'}]
|
1233
|
-
|
1234
|
-
res = json_request('/sms-setup', :password=>'012345678', "sms-phone"=>'(123) 456')
|
1235
|
-
res.must_equal [401, {'error'=>'Error setting up SMS authentication', "field-error"=>["password", 'invalid password']}]
|
1236
|
-
|
1237
|
-
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 456')
|
1238
|
-
res.must_equal [422, {'error'=>'Error setting up SMS authentication', "field-error"=>["sms-phone", 'invalid SMS phone number']}]
|
1239
|
-
|
1240
|
-
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1241
|
-
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
1242
|
-
|
1243
|
-
sms_phone.must_equal '1234567890'
|
1244
|
-
sms_message.must_match(/\ASMS confirmation code for example\.com: is \d{12}\z/)
|
1245
|
-
|
1246
|
-
res = json_request('/sms-confirm', :sms_code=>'asdf')
|
1247
|
-
res.must_equal [401, {'error'=>'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'}]
|
1248
|
-
|
1249
|
-
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1250
|
-
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
1251
|
-
|
1252
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
1253
|
-
res = json_request('/sms-confirm', :sms_code=>sms_code)
|
1254
|
-
res.must_equal [401, {'error'=>'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'}]
|
1255
|
-
|
1256
|
-
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1257
|
-
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
1258
|
-
|
1259
|
-
res = json_request('/sms-confirm', "sms-code"=>sms_code)
|
1260
|
-
res.must_equal [200, {'success'=>'SMS authentication has been setup.'}]
|
1261
|
-
|
1262
|
-
%w'/sms-setup /sms-confirm'.each do |path|
|
1263
|
-
res = json_request(path)
|
1264
|
-
res.must_equal [403, {'error'=>'SMS authentication has already been setup.'}]
|
1265
|
-
end
|
1266
|
-
|
1267
|
-
json_logout
|
1268
|
-
json_login
|
1269
|
-
|
1270
|
-
res = json_request('/sms-auth')
|
1271
|
-
res.must_equal [401, {'error'=>'No current SMS code for this account'}]
|
1272
|
-
|
1273
|
-
sms_phone = sms_message = nil
|
1274
|
-
res = json_request('/sms-request')
|
1275
|
-
res.must_equal [200, {'success'=>'SMS authentication code has been sent.'}]
|
1276
|
-
sms_phone.must_equal '1234567890'
|
1277
|
-
sms_message.must_match(/\ASMS authentication code for example\.com: is \d{6}\z/)
|
1278
|
-
|
1279
|
-
res = json_request('/sms-auth')
|
1280
|
-
res.must_equal [401, {'error'=>'Error authenticating via SMS code.', "field-error"=>["sms-code", "invalid SMS code"]}]
|
1281
|
-
|
1282
|
-
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
1283
|
-
res = json_request('/sms-auth')
|
1284
|
-
res.must_equal [401, {'error'=>'No current SMS code for this account'}]
|
1285
|
-
|
1286
|
-
res = json_request('/sms-request')
|
1287
|
-
res.must_equal [200, {'success'=>'SMS authentication code has been sent.'}]
|
1288
|
-
|
1289
|
-
res = json_request('/sms-auth', 'sms-code'=>sms_code)
|
1290
|
-
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
1291
|
-
json_request.must_equal [200, [1]]
|
1292
|
-
|
1293
|
-
json_logout
|
1294
|
-
json_login
|
1295
|
-
|
1296
|
-
res = json_request('/sms-request')
|
1297
|
-
res.must_equal [200, {'success'=>'SMS authentication code has been sent.'}]
|
1298
|
-
|
1299
|
-
5.times do
|
1300
|
-
res = json_request('/sms-auth')
|
1301
|
-
res.must_equal [401, {'error'=>'Error authenticating via SMS code.', "field-error"=>["sms-code", "invalid SMS code"]}]
|
1302
|
-
end
|
1303
|
-
|
1304
|
-
res = json_request('/sms-auth')
|
1305
|
-
res.must_equal [403, {'error'=>'SMS authentication has been locked out.'}]
|
1306
|
-
|
1307
|
-
res = json_request('/sms-request')
|
1308
|
-
res.must_equal [403, {'error'=>'SMS authentication has been locked out.'}]
|
1309
|
-
|
1310
|
-
res = json_request('/otp-auth', :otp=>totp.now)
|
1311
|
-
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
1312
|
-
json_request.must_equal [200, [1]]
|
1313
|
-
|
1314
|
-
res = json_request('/sms-disable', :password=>'012345678')
|
1315
|
-
res.must_equal [401, {'error'=>'Error disabling SMS authentication', "field-error"=>["password", 'invalid password']}]
|
1316
|
-
|
1317
|
-
res = json_request('/sms-disable', :password=>'0123456789')
|
1318
|
-
res.must_equal [200, {'success'=>'SMS authentication has been disabled.'}]
|
1319
|
-
|
1320
|
-
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1321
|
-
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
1322
|
-
|
1323
|
-
res = json_request('/sms-confirm', "sms-code"=>sms_code)
|
1324
|
-
res.must_equal [200, {'success'=>'SMS authentication has been setup.'}]
|
1325
|
-
|
1326
|
-
res = json_request('/recovery-codes', :password=>'asdf')
|
1327
|
-
res.must_equal [401, {'error'=>'Unable to view recovery codes.', "field-error"=>["password", 'invalid password']}]
|
1328
|
-
|
1329
|
-
res = json_request('/recovery-codes', :password=>'0123456789')
|
1330
|
-
codes = res[1].delete('codes')
|
1331
|
-
codes.sort.must_equal DB[:account_recovery_codes].select_map(:code).sort
|
1332
|
-
res.must_equal [200, {'success'=>''}]
|
1333
|
-
|
1334
|
-
json_logout
|
1335
|
-
json_login
|
1336
|
-
|
1337
|
-
5.times do
|
1338
|
-
res = json_request('/otp-auth', :otp=>'asdf')
|
1339
|
-
res.must_equal [401, {'error'=>'Error logging in via two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1340
|
-
end
|
1341
|
-
|
1342
|
-
res = json_request('/otp-auth', :otp=>'asdf')
|
1343
|
-
res.must_equal [403, {'error'=>'Authentication code use locked out due to numerous failures. Can use recovery code to unlock. Can use SMS code to unlock.'}]
|
1344
|
-
|
1345
|
-
res = json_request('/sms-request')
|
1346
|
-
5.times do
|
1347
|
-
res = json_request('/sms-auth')
|
1348
|
-
res.must_equal [401, {'error'=>'Error authenticating via SMS code.', "field-error"=>["sms-code", "invalid SMS code"]}]
|
1349
|
-
end
|
1350
|
-
|
1351
|
-
res = json_request('/otp-auth', :otp=>'asdf')
|
1352
|
-
res.must_equal [403, {'error'=>'Authentication code use locked out due to numerous failures. Can use recovery code to unlock.'}]
|
1353
|
-
|
1354
|
-
res = json_request('/sms-auth')
|
1355
|
-
res.must_equal [403, {'error'=>'SMS authentication has been locked out.'}]
|
1356
|
-
|
1357
|
-
res = json_request('/recovery-auth', 'recovery-code'=>'adsf')
|
1358
|
-
res.must_equal [401, {'error'=>'Error authenticating via recovery code.', "field-error"=>["recovery-code", "Invalid recovery code"]}]
|
1359
|
-
|
1360
|
-
res = json_request('/recovery-auth', 'recovery-code'=>codes.first)
|
1361
|
-
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
1362
|
-
json_request.must_equal [200, [1]]
|
1363
|
-
|
1364
|
-
res = json_request('/recovery-codes', :password=>'0123456789')
|
1365
|
-
codes2 = res[1].delete('codes')
|
1366
|
-
codes2.sort.must_equal codes[1..-1].sort
|
1367
|
-
res.must_equal [200, {'success'=>''}]
|
1368
|
-
|
1369
|
-
res = json_request('/recovery-codes', :password=>'012345678', :add=>'1')
|
1370
|
-
res.must_equal [401, {'error'=>'Unable to add recovery codes.', "field-error"=>["password", 'invalid password']}]
|
1371
|
-
|
1372
|
-
res = json_request('/recovery-codes', :password=>'0123456789', :add=>'1')
|
1373
|
-
codes3 = res[1].delete('codes')
|
1374
|
-
(codes3 - codes2).length.must_equal 1
|
1375
|
-
res.must_equal [200, {'success'=>'Additional authentication recovery codes have been added.'}]
|
1376
|
-
|
1377
|
-
res = json_request('/otp-disable', :password=>'012345678')
|
1378
|
-
res.must_equal [401, {'error'=>'Error disabling up two factor authentication', "field-error"=>["password", 'invalid password']}]
|
1379
|
-
|
1380
|
-
res = json_request('/otp-disable', :password=>'0123456789')
|
1381
|
-
res.must_equal [200, {'success'=>'Two factor authentication has been disabled'}]
|
1382
|
-
|
1383
|
-
[:account_otp_keys, :account_recovery_codes, :account_sms_codes].each do |t|
|
1384
|
-
DB[t].count.must_equal 0
|
1385
|
-
end
|
1386
|
-
|
1387
|
-
hmac_secret = "123"
|
1388
|
-
res = json_request('/otp-setup')
|
1389
|
-
secret = res[1].delete("otp_secret")
|
1390
|
-
raw_secret = res[1].delete("otp_raw_secret")
|
1391
|
-
res.must_equal [422, {'error'=>'Error setting up two factor authentication', "field-error"=>["otp_secret", 'invalid secret']}]
|
1392
|
-
|
1393
|
-
totp = ROTP::TOTP.new(secret)
|
1394
|
-
hmac_secret = "321"
|
1395
|
-
res = json_request('/otp-setup', :password=>'0123456789', :otp=>totp.now, :otp_secret=>secret, :otp_raw_secret=>raw_secret)
|
1396
|
-
res.must_equal [422, {'error'=>'Error setting up two factor authentication', "field-error"=>["otp_secret", 'invalid secret']}]
|
1397
|
-
|
1398
|
-
reset_otp_last_use
|
1399
|
-
hmac_secret = "123"
|
1400
|
-
res = json_request('/otp-setup', :password=>'0123456789', :otp=>totp.now, :otp_secret=>secret, :otp_raw_secret=>raw_secret)
|
1401
|
-
res.must_equal [200, {'success'=>'Two factor authentication is now setup'}]
|
1402
|
-
reset_otp_last_use
|
1403
|
-
|
1404
|
-
json_logout
|
1405
|
-
json_login
|
1406
|
-
|
1407
|
-
hmac_secret = "321"
|
1408
|
-
res = json_request('/otp-auth', :otp=>totp.now)
|
1409
|
-
res.must_equal [401, {'error'=>'Error logging in via two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1410
|
-
|
1411
|
-
hmac_secret = "123"
|
1412
|
-
res = json_request('/otp-auth', :otp=>totp.now)
|
1413
|
-
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
1414
|
-
json_request.must_equal [200, [1]]
|
1415
|
-
end
|
1416
|
-
|
1417
|
-
it "should allow two factor authentication setup, login, recovery, removal" do
|
1418
|
-
warning = nil
|
1419
|
-
before_called = false
|
1420
|
-
rodauth do
|
1421
|
-
enable :login, :otp, :logout
|
1422
|
-
(class << self; self end).send(:define_method, :warn){|w| warning = w}
|
1423
|
-
before_otp_authentication_route{before_called = true}
|
1424
|
-
warning.must_equal "before_otp_authentication_route is deprecated, switch to before_otp_auth_route"
|
1425
|
-
otp_drift 10
|
1426
|
-
end
|
1427
|
-
roda do |r|
|
1428
|
-
r.rodauth
|
1429
|
-
|
1430
|
-
r.redirect '/login' unless rodauth.logged_in?
|
1431
|
-
|
1432
|
-
if rodauth.two_factor_authentication_setup?
|
1433
|
-
r.redirect '/otp-auth' unless rodauth.authenticated?
|
1434
|
-
view :content=>"With OTP"
|
1435
|
-
else
|
1436
|
-
view :content=>"Without OTP"
|
1437
|
-
end
|
1438
|
-
end
|
1439
|
-
|
1440
|
-
login
|
1441
|
-
page.html.must_include('Without OTP')
|
1442
|
-
|
1443
|
-
visit '/otp-auth'
|
1444
|
-
before_called.must_equal false
|
1445
|
-
page.current_path.must_equal '/otp-setup'
|
1446
|
-
|
1447
|
-
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
|
1448
|
-
totp = ROTP::TOTP.new(secret)
|
1449
|
-
fill_in 'Password', :with=>'0123456789'
|
1450
|
-
fill_in 'Authentication Code', :with=>totp.now
|
1451
|
-
click_button 'Setup Two Factor Authentication'
|
1452
|
-
page.find('#notice_flash').text.must_equal 'Two factor authentication is now setup'
|
1453
|
-
page.current_path.must_equal '/'
|
1454
|
-
page.html.must_include 'With OTP'
|
1455
|
-
|
1456
|
-
logout
|
1457
|
-
before_called.must_equal false
|
1458
|
-
login
|
1459
|
-
page.current_path.must_equal '/otp-auth'
|
1460
|
-
before_called.must_equal true
|
1461
|
-
end
|
1462
|
-
end
|