rodauth 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +6 -0
- data/MIT-LICENSE +1 -1
- data/doc/base.rdoc +14 -0
- data/doc/jwt.rdoc +1 -0
- data/doc/login.rdoc +2 -0
- data/doc/release_notes/1.8.0.txt +14 -0
- data/doc/sms_codes.rdoc +3 -1
- data/doc/two_factor_base.rdoc +4 -1
- data/lib/rodauth/features/base.rb +20 -0
- data/lib/rodauth/features/change_login.rb +4 -4
- data/lib/rodauth/features/change_password.rb +4 -4
- data/lib/rodauth/features/close_account.rb +1 -0
- data/lib/rodauth/features/confirm_password.rb +1 -0
- data/lib/rodauth/features/create_account.rb +5 -5
- data/lib/rodauth/features/jwt.rb +15 -0
- data/lib/rodauth/features/lockout.rb +4 -0
- data/lib/rodauth/features/login.rb +4 -3
- data/lib/rodauth/features/otp.rb +7 -3
- data/lib/rodauth/features/recovery_codes.rb +2 -0
- data/lib/rodauth/features/remember.rb +1 -0
- data/lib/rodauth/features/reset_password.rb +5 -3
- data/lib/rodauth/features/sms_codes.rb +14 -2
- data/lib/rodauth/features/two_factor_base.rb +7 -0
- data/lib/rodauth/features/verify_account.rb +4 -0
- data/lib/rodauth/version.rb +1 -1
- data/spec/change_login_spec.rb +4 -4
- data/spec/change_password_spec.rb +4 -4
- data/spec/close_account_spec.rb +1 -1
- data/spec/confirm_password_spec.rb +1 -1
- data/spec/create_account_spec.rb +4 -4
- data/spec/lockout_spec.rb +6 -6
- data/spec/login_spec.rb +26 -0
- data/spec/remember_spec.rb +1 -1
- data/spec/reset_password_spec.rb +8 -8
- data/spec/spec_helper.rb +3 -1
- data/spec/two_factor_spec.rb +30 -30
- data/spec/verify_account_spec.rb +5 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65176959334b7e6fa96008eabe2b3f6f4d69032d
|
4
|
+
data.tar.gz: 44aeba80765dfc4a4afa4f15848f4004a83d453a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0500da1e1abe0cf9ed4e3cac1ca02102ab8a388bc6726e35de3934600676e7c886ca0181d2e0bd06385d1cd43015120cf5b4a97dcbaff6ceba4dc66ae8ac9abd
|
7
|
+
data.tar.gz: 8a5eed74a9f73f0ef22a9965a8c7f74b5c5de6036fa9af62d6adced7304f01a147d3f9e16d2966a79a898766704d512079d801f5be6864929f533db9f1aacfed
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
=== 1.8.0 (2017-01-06)
|
2
|
+
|
3
|
+
* Add json_response_custom_error_status? option to jwt feature to use specific 4xx statuses instead of 400 (jeremyevans)
|
4
|
+
|
5
|
+
* Use 4xx error statuses for errors, instead of using a 200 success status (jeremyevans)
|
6
|
+
|
1
7
|
=== 1.7.0 (2016-11-22)
|
2
8
|
|
3
9
|
* Make reset password, unlock account, and verify account pages not leak keys to external servers via Referer header (jeremyevans)
|
data/MIT-LICENSE
CHANGED
data/doc/base.rdoc
CHANGED
@@ -37,13 +37,23 @@ account_select :: An array of columns to select from +accounts_table+. By
|
|
37
37
|
account_status_column :: The status id column in the account model.
|
38
38
|
account_unverified_status_value :: The representating unverified accounts.
|
39
39
|
default_redirect :: Where to redirect after most successful actions.
|
40
|
+
invalid_field_error_status :: The response status to use for invalid field
|
41
|
+
value errors, 422 by default.
|
42
|
+
invalid_key_error_status :: The response status to use for invalid key codes,
|
43
|
+
401 by default.
|
44
|
+
invalid_password_error_status :: The response status to use for invalid passwords,
|
45
|
+
401 by default.
|
40
46
|
invalid_password_message :: The error message to display when a given
|
41
47
|
password doesn't match the stored password hash.
|
48
|
+
lockout_error_status :: The response status to use a login is attempted to an account that
|
49
|
+
is locked out, 403 by default.
|
42
50
|
login_column :: The login column in the account model.
|
43
51
|
login_label :: The label to use for logins.
|
44
52
|
login_param :: The parameter name to use for logins.
|
45
53
|
modifications_require_password? :: Whether making changes to an account requires
|
46
54
|
the user reinputing their password.
|
55
|
+
no_matching_login_error_status :: The response status to use when the login is not
|
56
|
+
in the database, 401 by default.
|
47
57
|
no_matching_login_message :: The error message to display when the login
|
48
58
|
used is not in the database.
|
49
59
|
password_hash_column :: The password hash column in the password hash table.
|
@@ -61,6 +71,10 @@ set_deadline_values? :: Whether deadline values should be set. True by default
|
|
61
71
|
if you want to vary the value based on a request parameter.
|
62
72
|
template_opts :: Any template options to pass to view/render. This can be used
|
63
73
|
to set a custom layout, for example.
|
74
|
+
unmatched_field_error_status :: The response status to use when two field values should
|
75
|
+
match but do not, 422 by default.
|
76
|
+
unopen_account_error_status :: The response status to use when trying to login to an
|
77
|
+
account that isn't open, 403 by default.
|
64
78
|
use_date_arithmetic? :: Whether the date_arithmetic extension should be loaded into
|
65
79
|
the database. Defaults to whether deadline values should
|
66
80
|
be set.
|
data/doc/jwt.rdoc
CHANGED
@@ -41,6 +41,7 @@ json_non_post_error_message :: The error message to use when a JSON non-POST req
|
|
41
41
|
json_not_accepted_error_message :: The error message to display if jwt_check_accept? is true and the Accept header is present but does not match json_request_content_type_regexp.
|
42
42
|
json_request_content_type_regexp :: The regexp to use to recognize a request as a json request.
|
43
43
|
json_response_content_type :: The content type to set for json responses, application/json by default.
|
44
|
+
json_response_custom_error_status? :: Whether to use custom error statuses, instead of always using +json_response_error_status+, false by default for backwards compatibility.
|
44
45
|
json_response_error_key :: The JSON result key containing an error message, "error" by default.
|
45
46
|
json_response_error_status :: The HTTP status code to use for JSON error responses, 400 by default.
|
46
47
|
json_response_field_error_key :: The JSON result key containing an field error message, "field-error" by default.
|
data/doc/login.rdoc
CHANGED
@@ -9,6 +9,8 @@ login_additional_form_tags :: HTML fragment containing additional form
|
|
9
9
|
tags to use on the login form.
|
10
10
|
login_button :: The text to use for the login button.
|
11
11
|
login_error_flash :: The flash error to show for an unsuccesful login.
|
12
|
+
login_error_status :: The response status to use when using an invalid
|
13
|
+
login or password to login, 401 by default.
|
12
14
|
login_form_footer :: A message to display after the login form.
|
13
15
|
login_notice_flash :: The flash notice to show after successful login.
|
14
16
|
login_redirect :: Where to redirect after a sucessful login.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* When using a browser, Rodauth now uses an appropriate 401, 403,
|
4
|
+
or 422 error status for errors instead of using 200 success status.
|
5
|
+
Many configuration methods have been added to customize the status
|
6
|
+
codes used for specific types of errors.
|
7
|
+
|
8
|
+
* The json_response_custom_error_status? configuration method
|
9
|
+
has been added to the jwt feature, which if set to true makes
|
10
|
+
the jwt feature use the same error status codes for JSON API
|
11
|
+
requests that it would use for browser requests. For backward
|
12
|
+
compatibility, the default is to continue to use the 400
|
13
|
+
error status for all errors in the JSON API, but this will
|
14
|
+
change in Rodauth 2.
|
data/doc/sms_codes.rdoc
CHANGED
@@ -21,7 +21,8 @@ you prefer:
|
|
21
21
|
== Auth Value Methods
|
22
22
|
|
23
23
|
no_current_sms_code_error_flash :: The flash error to show when going to the SMS authentication page and no current SMS authentication code is available.
|
24
|
-
|
24
|
+
sms_already_setup_error_status :: The response status to use when going to a page to setup SMS authentication if SMS authentication has already been setup, 403 by default.
|
25
|
+
sms_already_setup_error_flash :: The flash error to show when going to a page to setup SMS authentication if SMS authentication has already been setup.
|
25
26
|
sms_already_setup_redirect :: Where to redirect when going to a page to setup SMS authentication if SMS authentication has already been setup.
|
26
27
|
sms_auth_additional_form_tags :: HTML fragment containing additional form tags when authenticating via SMS.
|
27
28
|
sms_auth_button :: Text to use for button on form to authenticate via SMS.
|
@@ -56,6 +57,7 @@ sms_invalid_phone_message :: The error message to show when an invalid SMS phone
|
|
56
57
|
sms_issued_at_column :: The column in the +sms_codes_table+ containing the time the SMS code was issued.
|
57
58
|
sms_lockout_error_flash :: The flash error to show when SMS authentication has been locked out due to repeated failures.
|
58
59
|
sms_lockout_redirect :: Where to redirect after SMS authentication has been locked out.
|
60
|
+
sms_needs_confirmation_error_status :: The response status to use on SMS authentication pages when SMS authentication setup needs confirmation, 403 by default.
|
59
61
|
sms_needs_confirmation_error_flash :: The flash error to show on SMS authentication pages when SMS authentication setup needs confirmation.
|
60
62
|
sms_needs_confirmation_redirect :: Where to redirect after SMS setup, when confirmation is required.
|
61
63
|
sms_needs_setup_redirect :: Where to redirect if going to an SMS authentication page when SMS authentication has not been setup.
|
data/doc/two_factor_base.rdoc
CHANGED
@@ -5,14 +5,17 @@ factor authentication features.
|
|
5
5
|
|
6
6
|
== Auth Value Methods
|
7
7
|
|
8
|
-
|
8
|
+
two_factor_already_authenticated_error_status :: The response status to use if going to a two factor authentication page when already authenticated via 2nd factor, 403 by default.
|
9
|
+
two_factor_already_authenticated_error_flash :: The flash error to show if going to a two factor authentication page when already authenticated via 2nd factor.
|
9
10
|
two_factor_already_authenticated_redirect :: Where to redirect if going to a two factor authentication page when already authenticated via 2nd factor.
|
10
11
|
two_factor_auth_notice_flash :: The flash notice to show after a successful two factor authentication.
|
11
12
|
two_factor_auth_redirect :: Whether to redirect after a successful two factor authentication.
|
12
13
|
two_factor_auth_required_redirect :: Where to redirect if going to a page requiring two factor authentication when not authenticated via 2nd factor.
|
13
14
|
two_factor_modifications_require_password? :: Whether modifications to two factor authentication require the use of passwords.
|
15
|
+
two_factor_need_authentication_error_status :: The response status to use if going to a page that requires two factor authentication when not authenticated, 401 by default.
|
14
16
|
two_factor_need_authentication_error_flash :: The flash error to show if going to a page that requires two factor authentication when not authenticated.
|
15
17
|
two_factor_need_setup_redirect :: Where to redirect if going to a two factor authentication page when two factor authentication has not been setup.
|
18
|
+
two_factor_not_setup_error_status :: The response status to use if going to a two factor authentication page when two factor authentication has not been setup, 403 by default.
|
16
19
|
two_factor_not_setup_error_flash :: The flash error to show if going to a two factor authentication page when two factor authentication has not been setup.
|
17
20
|
two_factor_session_key :: The session key used for storing a symbol indicating which type of 2nd factor was used to authenticate.
|
18
21
|
two_factor_setup_session_key :: The session key used for storing whether two factor authentication has been setup for the current account.
|
@@ -18,11 +18,16 @@ module Rodauth
|
|
18
18
|
auth_value_method :account_unverified_status_value, 1
|
19
19
|
auth_value_method :accounts_table, :accounts
|
20
20
|
auth_value_method :default_redirect, '/'
|
21
|
+
auth_value_method :invalid_field_error_status, 422
|
22
|
+
auth_value_method :invalid_key_error_status, 401
|
23
|
+
auth_value_method :invalid_password_error_status, 401
|
21
24
|
auth_value_method :invalid_password_message, "invalid password"
|
22
25
|
auth_value_method :login_column, :email
|
26
|
+
auth_value_method :lockout_error_status, 403
|
23
27
|
auth_value_method :password_hash_id_column, :id
|
24
28
|
auth_value_method :password_hash_column, :password_hash
|
25
29
|
auth_value_method :password_hash_table, :account_password_hashes
|
30
|
+
auth_value_method :no_matching_login_error_status, 401
|
26
31
|
auth_value_method :no_matching_login_message, "no matching login"
|
27
32
|
auth_value_method :login_param, 'login'
|
28
33
|
auth_value_method :login_label, 'Login'
|
@@ -35,6 +40,8 @@ module Rodauth
|
|
35
40
|
auth_value_method :skip_status_checks?, true
|
36
41
|
auth_value_method :template_opts, {}
|
37
42
|
auth_value_method :title_instance_variable, nil
|
43
|
+
auth_value_method :unmatched_field_error_status, 422
|
44
|
+
auth_value_method :unopen_account_error_status, 403
|
38
45
|
auth_value_method :unverified_account_message, "unverified account, please verify account before logging in"
|
39
46
|
|
40
47
|
redirect(:require_login){"#{prefix}/login"}
|
@@ -321,11 +328,24 @@ module Rodauth
|
|
321
328
|
catch(:rodauth_error, &block)
|
322
329
|
end
|
323
330
|
|
331
|
+
# Don't set an error status when redirecting in an error case, as a redirect status is needed.
|
332
|
+
def set_redirect_error_status(status)
|
333
|
+
end
|
334
|
+
|
335
|
+
def set_response_error_status(status)
|
336
|
+
response.status = status
|
337
|
+
end
|
338
|
+
|
324
339
|
def throw_error(field, error)
|
325
340
|
set_field_error(field, error)
|
326
341
|
throw :rodauth_error
|
327
342
|
end
|
328
343
|
|
344
|
+
def throw_error_status(status, field, error)
|
345
|
+
set_response_error_status(status)
|
346
|
+
throw_error(field, error)
|
347
|
+
end
|
348
|
+
|
329
349
|
def use_date_arithmetic?
|
330
350
|
set_deadline_values?
|
331
351
|
end
|
@@ -28,22 +28,22 @@ module Rodauth
|
|
28
28
|
r.post do
|
29
29
|
catch_error do
|
30
30
|
if change_login_requires_password? && !password_match?(param(password_param))
|
31
|
-
|
31
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
32
32
|
end
|
33
33
|
|
34
34
|
login = param(login_param)
|
35
35
|
unless login_meets_requirements?(login)
|
36
|
-
|
36
|
+
throw_error_status(invalid_field_error_status, login_param, login_does_not_meet_requirements_message)
|
37
37
|
end
|
38
38
|
|
39
39
|
if require_login_confirmation? && login != param(login_confirm_param)
|
40
|
-
|
40
|
+
throw_error_status(unmatched_field_error_status, login_param, logins_do_not_match_message)
|
41
41
|
end
|
42
42
|
|
43
43
|
transaction do
|
44
44
|
before_change_login
|
45
45
|
unless change_login(login)
|
46
|
-
|
46
|
+
throw_error_status(invalid_field_error_status, login_param, login_does_not_meet_requirements_message)
|
47
47
|
end
|
48
48
|
|
49
49
|
after_change_login
|
@@ -29,20 +29,20 @@ module Rodauth
|
|
29
29
|
r.post do
|
30
30
|
catch_error do
|
31
31
|
if change_password_requires_password? && !password_match?(param(password_param))
|
32
|
-
|
32
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
33
33
|
end
|
34
34
|
|
35
35
|
password = param(new_password_param)
|
36
36
|
if require_password_confirmation? && password != param(password_confirm_param)
|
37
|
-
|
37
|
+
throw_error_status(unmatched_field_error_status, new_password_param, passwords_do_not_match_message)
|
38
38
|
end
|
39
39
|
|
40
40
|
if password_match?(password)
|
41
|
-
|
41
|
+
throw_error_status(invalid_field_error_status, new_password_param, same_as_existing_password_message)
|
42
42
|
end
|
43
43
|
|
44
44
|
unless password_meets_requirements?(password)
|
45
|
-
|
45
|
+
throw_error_status(invalid_field_error_status, new_password_param, password_does_not_meet_requirements_message)
|
46
46
|
end
|
47
47
|
|
48
48
|
transaction do
|
@@ -46,6 +46,7 @@ module Rodauth
|
|
46
46
|
set_notice_flash close_account_notice_flash
|
47
47
|
redirect close_account_redirect
|
48
48
|
else
|
49
|
+
set_response_error_status(invalid_password_error_status)
|
49
50
|
set_field_error(password_param, invalid_password_message)
|
50
51
|
set_error_flash close_account_error_flash
|
51
52
|
close_account_view
|
@@ -32,6 +32,7 @@ module Rodauth
|
|
32
32
|
set_notice_flash confirm_password_notice_flash
|
33
33
|
redirect confirm_password_redirect
|
34
34
|
else
|
35
|
+
set_response_error_status(invalid_password_error_status)
|
35
36
|
set_field_error(password_param, invalid_password_message)
|
36
37
|
set_error_flash confirm_password_error_flash
|
37
38
|
confirm_password_view
|
@@ -46,25 +46,25 @@ module Rodauth
|
|
46
46
|
|
47
47
|
catch_error do
|
48
48
|
if require_login_confirmation? && login != param(login_confirm_param)
|
49
|
-
|
49
|
+
throw_error_status(unmatched_field_error_status, login_param, logins_do_not_match_message)
|
50
50
|
end
|
51
51
|
|
52
52
|
unless login_meets_requirements?(login)
|
53
|
-
|
53
|
+
throw_error_status(invalid_field_error_status, login_param, login_does_not_meet_requirements_message)
|
54
54
|
end
|
55
55
|
|
56
56
|
if require_password_confirmation? && password != param(password_confirm_param)
|
57
|
-
|
57
|
+
throw_error_status(unmatched_field_error_status, password_param, passwords_do_not_match_message)
|
58
58
|
end
|
59
59
|
|
60
60
|
unless password_meets_requirements?(password)
|
61
|
-
|
61
|
+
throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message)
|
62
62
|
end
|
63
63
|
|
64
64
|
transaction do
|
65
65
|
before_create_account
|
66
66
|
unless save_account
|
67
|
-
|
67
|
+
throw_error_status(invalid_field_error_status, login_param, login_does_not_meet_requirements_message)
|
68
68
|
end
|
69
69
|
|
70
70
|
unless account_password_hash_column
|
data/lib/rodauth/features/jwt.rb
CHANGED
@@ -11,6 +11,7 @@ module Rodauth
|
|
11
11
|
auth_value_method :json_request_content_type_regexp, /\bapplication\/(?:vnd\.api\+)?json\b/i
|
12
12
|
auth_value_method :json_response_content_type, 'application/json'
|
13
13
|
auth_value_method :json_response_error_status, 400
|
14
|
+
auth_value_method :json_response_custom_error_status?, false
|
14
15
|
auth_value_method :json_response_error_key, "error"
|
15
16
|
auth_value_method :json_response_field_error_key, "field-error"
|
16
17
|
auth_value_method :json_response_success_key, nil
|
@@ -193,6 +194,20 @@ module Rodauth
|
|
193
194
|
@json_request = request.content_type =~ json_request_content_type_regexp
|
194
195
|
end
|
195
196
|
|
197
|
+
def set_redirect_error_status(status)
|
198
|
+
if json_request? && json_response_custom_error_status?
|
199
|
+
response.status = status
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def set_response_error_status(status)
|
204
|
+
if json_request? && !json_response_custom_error_status?
|
205
|
+
status = json_response_error_status
|
206
|
+
end
|
207
|
+
|
208
|
+
super
|
209
|
+
end
|
210
|
+
|
196
211
|
def use_jwt?
|
197
212
|
jwt_token || only_json? || json_request?
|
198
213
|
end
|
@@ -70,6 +70,7 @@ module Rodauth
|
|
70
70
|
|
71
71
|
set_notice_flash unlock_account_request_notice_flash
|
72
72
|
else
|
73
|
+
set_redirect_error_status(no_matching_login_error_status)
|
73
74
|
set_redirect_error_flash no_matching_login_message
|
74
75
|
end
|
75
76
|
|
@@ -101,6 +102,7 @@ module Rodauth
|
|
101
102
|
r.post do
|
102
103
|
key = session[unlock_account_session_key] || param(unlock_account_key_param)
|
103
104
|
unless account_from_unlock_key(key)
|
105
|
+
set_redirect_error_status invalid_key_error_status
|
104
106
|
set_redirect_error_flash no_matching_unlock_account_key_message
|
105
107
|
redirect unlock_account_request_redirect
|
106
108
|
end
|
@@ -119,6 +121,7 @@ module Rodauth
|
|
119
121
|
set_notice_flash unlock_account_notice_flash
|
120
122
|
redirect unlock_account_redirect
|
121
123
|
else
|
124
|
+
set_response_error_status(invalid_password_error_status)
|
122
125
|
set_field_error(password_param, invalid_password_message)
|
123
126
|
set_error_flash unlock_account_error_flash
|
124
127
|
unlock_account_view
|
@@ -240,6 +243,7 @@ module Rodauth
|
|
240
243
|
end
|
241
244
|
|
242
245
|
def show_lockout_page
|
246
|
+
set_response_error_status lockout_error_status
|
243
247
|
set_error_flash login_lockout_error_flash
|
244
248
|
response.write unlock_account_request_view
|
245
249
|
request.halt
|
@@ -9,6 +9,7 @@ module Rodauth
|
|
9
9
|
button 'Login'
|
10
10
|
redirect
|
11
11
|
|
12
|
+
auth_value_method :login_error_status, 401
|
12
13
|
auth_value_method :login_form_footer, ''
|
13
14
|
|
14
15
|
route do |r|
|
@@ -24,18 +25,18 @@ module Rodauth
|
|
24
25
|
|
25
26
|
catch_error do
|
26
27
|
unless account_from_login(param(login_param))
|
27
|
-
|
28
|
+
throw_error_status(no_matching_login_error_status, login_param, no_matching_login_message)
|
28
29
|
end
|
29
30
|
|
30
31
|
before_login_attempt
|
31
32
|
|
32
33
|
unless open_account?
|
33
|
-
|
34
|
+
throw_error_status(unopen_account_error_status, login_param, unverified_account_message)
|
34
35
|
end
|
35
36
|
|
36
37
|
unless password_match?(param(password_param))
|
37
38
|
after_login_failure
|
38
|
-
|
39
|
+
throw_error_status(login_error_status, password_param, invalid_password_message)
|
39
40
|
end
|
40
41
|
|
41
42
|
transaction do
|
data/lib/rodauth/features/otp.rb
CHANGED
@@ -98,6 +98,7 @@ module Rodauth
|
|
98
98
|
require_otp_setup
|
99
99
|
|
100
100
|
if otp_locked_out?
|
101
|
+
set_response_error_status(lockout_error_status)
|
101
102
|
set_redirect_error_flash otp_lockout_error_flash
|
102
103
|
redirect otp_lockout_redirect
|
103
104
|
end
|
@@ -116,6 +117,7 @@ module Rodauth
|
|
116
117
|
|
117
118
|
otp_record_authentication_failure
|
118
119
|
after_otp_authentication_failure
|
120
|
+
set_response_error_status(invalid_key_error_status)
|
119
121
|
set_field_error(otp_auth_param, otp_invalid_auth_code_message)
|
120
122
|
set_error_flash otp_auth_error_flash
|
121
123
|
otp_auth_view
|
@@ -141,16 +143,16 @@ module Rodauth
|
|
141
143
|
secret = param(otp_setup_param)
|
142
144
|
catch_error do
|
143
145
|
unless otp_valid_key?(secret)
|
144
|
-
|
146
|
+
throw_error_status(invalid_field_error_status, otp_setup_param, otp_invalid_secret_message)
|
145
147
|
end
|
146
148
|
otp_tmp_key(secret)
|
147
149
|
|
148
150
|
unless two_factor_password_match?(param(password_param))
|
149
|
-
|
151
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
150
152
|
end
|
151
153
|
|
152
154
|
unless otp_valid_code?(param(otp_auth_param))
|
153
|
-
|
155
|
+
throw_error_status(invalid_key_error_status, otp_auth_param, otp_invalid_auth_code_message)
|
154
156
|
end
|
155
157
|
|
156
158
|
transaction do
|
@@ -189,6 +191,7 @@ module Rodauth
|
|
189
191
|
redirect otp_disable_redirect
|
190
192
|
end
|
191
193
|
|
194
|
+
set_response_error_status(invalid_password_error_status)
|
192
195
|
set_field_error(password_param, invalid_password_message)
|
193
196
|
set_error_flash otp_disable_error_flash
|
194
197
|
otp_disable_view
|
@@ -232,6 +235,7 @@ module Rodauth
|
|
232
235
|
|
233
236
|
def require_otp_setup
|
234
237
|
unless otp_exists?
|
238
|
+
set_redirect_error_status(two_factor_not_setup_error_status)
|
235
239
|
set_redirect_error_flash two_factor_not_setup_error_flash
|
236
240
|
redirect two_factor_need_setup_redirect
|
237
241
|
end
|
@@ -72,6 +72,7 @@ module Rodauth
|
|
72
72
|
two_factor_authenticate(:recovery_code)
|
73
73
|
end
|
74
74
|
|
75
|
+
set_response_error_status(invalid_key_error_status)
|
75
76
|
set_field_error(recovery_codes_param, invalid_recovery_code_message)
|
76
77
|
set_error_flash invalid_recovery_code_error_flash
|
77
78
|
recovery_auth_view
|
@@ -114,6 +115,7 @@ module Rodauth
|
|
114
115
|
set_error_flash view_recovery_codes_error_flash
|
115
116
|
end
|
116
117
|
|
118
|
+
set_response_error_status(invalid_password_error_status)
|
117
119
|
set_field_error(password_param, invalid_password_message)
|
118
120
|
recovery_codes_view
|
119
121
|
end
|
@@ -64,6 +64,7 @@ module Rodauth
|
|
64
64
|
|
65
65
|
set_notice_flash reset_password_email_sent_notice_flash
|
66
66
|
else
|
67
|
+
set_redirect_error_status(no_matching_login_error_status)
|
67
68
|
set_redirect_error_flash reset_password_request_error_flash
|
68
69
|
end
|
69
70
|
|
@@ -95,6 +96,7 @@ module Rodauth
|
|
95
96
|
r.post do
|
96
97
|
key = session[reset_password_session_key] || param(reset_password_key_param)
|
97
98
|
unless account_from_reset_password_key(key)
|
99
|
+
set_redirect_error_status(invalid_key_error_status)
|
98
100
|
set_redirect_error_flash reset_password_error_flash
|
99
101
|
redirect reset_password_email_sent_redirect
|
100
102
|
end
|
@@ -102,15 +104,15 @@ module Rodauth
|
|
102
104
|
password = param(password_param)
|
103
105
|
catch_error do
|
104
106
|
if password_match?(password)
|
105
|
-
|
107
|
+
throw_error_status(invalid_field_error_status, password_param, same_as_existing_password_message)
|
106
108
|
end
|
107
109
|
|
108
110
|
if require_password_confirmation? && password != param(password_confirm_param)
|
109
|
-
|
111
|
+
throw_error_status(unmatched_field_error_status, password_param, passwords_do_not_match_message)
|
110
112
|
end
|
111
113
|
|
112
114
|
unless password_meets_requirements?(password)
|
113
|
-
|
115
|
+
throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message)
|
114
116
|
end
|
115
117
|
|
116
118
|
transaction do
|
@@ -56,6 +56,9 @@ module Rodauth
|
|
56
56
|
view 'sms-request', 'Send SMS Code', 'sms_request'
|
57
57
|
view 'sms-setup', 'Setup SMS Backup Number', 'sms_setup'
|
58
58
|
|
59
|
+
auth_value_method :sms_already_setup_error_status, 403
|
60
|
+
auth_value_method :sms_needs_confirmation_error_status, 403
|
61
|
+
|
59
62
|
auth_value_method :sms_auth_code_length, 6
|
60
63
|
auth_value_method :sms_code_allowed_seconds, 300
|
61
64
|
auth_value_method :sms_code_column, :code
|
@@ -138,6 +141,8 @@ module Rodauth
|
|
138
141
|
if sms_code
|
139
142
|
sms_set_code(nil)
|
140
143
|
end
|
144
|
+
|
145
|
+
set_response_error_status(invalid_key_error_status)
|
141
146
|
set_redirect_error_flash no_current_sms_code_error_flash
|
142
147
|
redirect sms_request_redirect
|
143
148
|
end
|
@@ -160,6 +165,7 @@ module Rodauth
|
|
160
165
|
end
|
161
166
|
end
|
162
167
|
|
168
|
+
set_response_error_status(invalid_key_error_status)
|
163
169
|
set_field_error(sms_code_param, sms_invalid_code_message)
|
164
170
|
set_error_flash sms_invalid_code_error_flash
|
165
171
|
sms_auth_view
|
@@ -175,6 +181,7 @@ module Rodauth
|
|
175
181
|
require_sms_not_setup
|
176
182
|
|
177
183
|
if sms_needs_confirmation?
|
184
|
+
set_redirect_error_status(sms_needs_confirmation_error_status)
|
178
185
|
set_redirect_error_flash sms_needs_confirmation_error_flash
|
179
186
|
redirect sms_needs_confirmation_redirect
|
180
187
|
end
|
@@ -188,13 +195,13 @@ module Rodauth
|
|
188
195
|
r.post do
|
189
196
|
catch_error do
|
190
197
|
unless two_factor_password_match?(param(password_param))
|
191
|
-
|
198
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
192
199
|
end
|
193
200
|
|
194
201
|
phone = sms_normalize_phone(param(sms_phone_param))
|
195
202
|
|
196
203
|
unless sms_valid_phone?(phone)
|
197
|
-
|
204
|
+
throw_error_status(invalid_field_error_status, sms_phone_param, sms_invalid_phone_message)
|
198
205
|
end
|
199
206
|
|
200
207
|
transaction do
|
@@ -242,6 +249,7 @@ module Rodauth
|
|
242
249
|
end
|
243
250
|
|
244
251
|
sms_confirm_failure
|
252
|
+
set_redirect_error_status(invalid_key_error_status)
|
245
253
|
set_redirect_error_flash sms_invalid_confirmation_code_error_flash
|
246
254
|
redirect sms_needs_setup_redirect
|
247
255
|
end
|
@@ -270,6 +278,7 @@ module Rodauth
|
|
270
278
|
redirect sms_disable_redirect
|
271
279
|
end
|
272
280
|
|
281
|
+
set_response_error_status(invalid_password_error_status)
|
273
282
|
set_field_error(password_param, invalid_password_message)
|
274
283
|
set_error_flash sms_disable_error_flash
|
275
284
|
sms_disable_view
|
@@ -331,6 +340,7 @@ module Rodauth
|
|
331
340
|
|
332
341
|
def require_sms_setup
|
333
342
|
unless sms_setup?
|
343
|
+
set_redirect_error_status(two_factor_not_setup_error_status)
|
334
344
|
set_redirect_error_flash sms_not_setup_error_flash
|
335
345
|
redirect sms_needs_setup_redirect
|
336
346
|
end
|
@@ -338,6 +348,7 @@ module Rodauth
|
|
338
348
|
|
339
349
|
def require_sms_not_setup
|
340
350
|
if sms_setup?
|
351
|
+
set_redirect_error_status(sms_already_setup_error_status)
|
341
352
|
set_redirect_error_flash sms_already_setup_error_flash
|
342
353
|
redirect sms_already_setup_redirect
|
343
354
|
end
|
@@ -347,6 +358,7 @@ module Rodauth
|
|
347
358
|
require_sms_setup
|
348
359
|
|
349
360
|
if sms_locked_out?
|
361
|
+
set_redirect_error_status(lockout_error_status)
|
350
362
|
set_redirect_error_flash sms_lockout_error_flash
|
351
363
|
redirect sms_lockout_redirect
|
352
364
|
end
|
@@ -13,6 +13,10 @@ module Rodauth
|
|
13
13
|
error_flash "Already authenticated via 2nd factor", 'two_factor_already_authenticated'
|
14
14
|
error_flash "You need to authenticate via 2nd factor before continuing.", 'two_factor_need_authentication'
|
15
15
|
|
16
|
+
auth_value_method :two_factor_already_authenticated_error_status, 403
|
17
|
+
auth_value_method :two_factor_need_authentication_error_status, 401
|
18
|
+
auth_value_method :two_factor_not_setup_error_status, 403
|
19
|
+
|
16
20
|
auth_value_method :two_factor_session_key, :two_factor_auth
|
17
21
|
auth_value_method :two_factor_setup_session_key, :two_factor_auth_setup
|
18
22
|
auth_value_method :two_factor_need_setup_redirect, nil
|
@@ -46,6 +50,7 @@ module Rodauth
|
|
46
50
|
|
47
51
|
def require_two_factor_setup
|
48
52
|
unless uses_two_factor_authentication?
|
53
|
+
set_redirect_error_status(two_factor_not_setup_error_status)
|
49
54
|
set_redirect_error_flash two_factor_not_setup_error_flash
|
50
55
|
redirect two_factor_need_setup_redirect
|
51
56
|
end
|
@@ -53,6 +58,7 @@ module Rodauth
|
|
53
58
|
|
54
59
|
def require_two_factor_not_authenticated
|
55
60
|
if two_factor_authenticated?
|
61
|
+
set_redirect_error_status(two_factor_already_authenticated_error_status)
|
56
62
|
set_redirect_error_flash two_factor_already_authenticated_error_flash
|
57
63
|
redirect two_factor_already_authenticated_redirect
|
58
64
|
end
|
@@ -60,6 +66,7 @@ module Rodauth
|
|
60
66
|
|
61
67
|
def require_two_factor_authenticated
|
62
68
|
unless two_factor_authenticated?
|
69
|
+
set_redirect_error_status(two_factor_need_authentication_error_status)
|
63
70
|
set_redirect_error_flash two_factor_need_authentication_error_flash
|
64
71
|
redirect _two_factor_auth_required_redirect
|
65
72
|
end
|
@@ -64,6 +64,7 @@ module Rodauth
|
|
64
64
|
|
65
65
|
set_notice_flash verify_account_email_sent_notice_flash
|
66
66
|
else
|
67
|
+
set_redirect_error_status(no_matching_login_error_status)
|
67
68
|
set_redirect_error_flash verify_account_resend_error_flash
|
68
69
|
end
|
69
70
|
|
@@ -95,6 +96,7 @@ module Rodauth
|
|
95
96
|
r.post do
|
96
97
|
key = session[verify_account_session_key] || param(verify_account_key_param)
|
97
98
|
unless account_from_verify_account_key(key)
|
99
|
+
set_redirect_error_status(invalid_key_error_status)
|
98
100
|
set_redirect_error_flash verify_account_error_flash
|
99
101
|
redirect verify_account_redirect
|
100
102
|
end
|
@@ -137,6 +139,7 @@ module Rodauth
|
|
137
139
|
|
138
140
|
def new_account(login)
|
139
141
|
if account_from_login(login)
|
142
|
+
set_redirect_error_status(unopen_account_error_status)
|
140
143
|
set_error_flash attempt_to_create_unverified_account_notice_message
|
141
144
|
response.write resend_verify_account_view
|
142
145
|
request.halt
|
@@ -178,6 +181,7 @@ module Rodauth
|
|
178
181
|
|
179
182
|
def before_login_attempt
|
180
183
|
unless open_account?
|
184
|
+
set_redirect_error_status(unopen_account_error_status)
|
181
185
|
set_error_flash attempt_to_login_to_unverified_account_notice_message
|
182
186
|
response.write resend_verify_account_view
|
183
187
|
request.halt
|
data/lib/rodauth/version.rb
CHANGED
data/spec/change_login_spec.rb
CHANGED
@@ -128,13 +128,13 @@ describe 'Rodauth change_login feature' do
|
|
128
128
|
json_login
|
129
129
|
|
130
130
|
res = json_request('/change-login', :login=>'foobar', "login-confirm"=>'foobar')
|
131
|
-
res.must_equal [
|
131
|
+
res.must_equal [422, {'error'=>"There was an error changing your login", "field-error"=>["login", "invalid login, not a valid email address"]}]
|
132
132
|
|
133
133
|
res = json_request('/change-login', :login=>'foo@example.com', "login-confirm"=>'foo2@example.com')
|
134
|
-
res.must_equal [
|
134
|
+
res.must_equal [422, {'error'=>"There was an error changing your login", "field-error"=>["login", "logins do not match"]}]
|
135
135
|
|
136
136
|
res = json_request('/change-login', :login=>'foo2@example.com', "login-confirm"=>'foo2@example.com')
|
137
|
-
res.must_equal [
|
137
|
+
res.must_equal [422, {'error'=>"There was an error changing your login", "field-error"=>["login", "invalid login, already an account with this login"]}]
|
138
138
|
|
139
139
|
res = json_request('/change-login', :login=>'foo3@example.com', "login-confirm"=>'foo3@example.com')
|
140
140
|
res.must_equal [200, {'success'=>"Your login has been changed"}]
|
@@ -145,7 +145,7 @@ describe 'Rodauth change_login feature' do
|
|
145
145
|
require_password = true
|
146
146
|
|
147
147
|
res = json_request('/change-login', :login=>'foo4@example.com', "login-confirm"=>'foo4@example.com', :password=>'012345678')
|
148
|
-
res.must_equal [
|
148
|
+
res.must_equal [401, {'error'=>"There was an error changing your login", "field-error"=>["password", "invalid password"]}]
|
149
149
|
|
150
150
|
res = json_request('/change-login', :login=>'foo4@example.com', "login-confirm"=>'foo4@example.com', :password=>'0123456789')
|
151
151
|
res.must_equal [200, {'success'=>"Your login has been changed"}]
|
@@ -149,20 +149,20 @@ describe 'Rodauth change_password feature' do
|
|
149
149
|
json_login
|
150
150
|
|
151
151
|
res = json_request('/change-password', :password=>'0123456789', "new-password"=>'0123456', "password-confirm"=>'0123456789')
|
152
|
-
res.must_equal [
|
152
|
+
res.must_equal [422, {'error'=>"There was an error changing your password", "field-error"=>["new-password", "passwords do not match"]}]
|
153
153
|
|
154
154
|
res = json_request('/change-password', :password=>'0123456', "new-password"=>'0123456', "password-confirm"=>'0123456')
|
155
|
-
res.must_equal [
|
155
|
+
res.must_equal [401, {'error'=>"There was an error changing your password", "field-error"=>["password", "invalid password"]}]
|
156
156
|
|
157
157
|
res = json_request('/change-password', :password=>'0123456789', "new-password"=>'0123456789', "password-confirm"=>'0123456789')
|
158
|
-
res.must_equal [
|
158
|
+
res.must_equal [422, {'error'=>"There was an error changing your password", "field-error"=>["new-password", "invalid password, same as current password"]}]
|
159
159
|
|
160
160
|
res = json_request('/change-password', :password=>'0123456789', "new-password"=>'0123456', "password-confirm"=>'0123456')
|
161
161
|
res.must_equal [200, {'success'=>"Your password has been changed"}]
|
162
162
|
|
163
163
|
json_logout
|
164
164
|
res = json_login(:no_check=>true)
|
165
|
-
res.must_equal [
|
165
|
+
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
166
166
|
|
167
167
|
json_login(:pass=>'0123456')
|
168
168
|
|
data/spec/close_account_spec.rb
CHANGED
@@ -152,7 +152,7 @@ describe 'Rodauth close_account feature' do
|
|
152
152
|
json_login
|
153
153
|
|
154
154
|
res = json_request('/close-account', :password=>'0123456')
|
155
|
-
res.must_equal [
|
155
|
+
res.must_equal [401, {'error'=>"There was an error closing your account", "field-error"=>["password", "invalid password"]}]
|
156
156
|
DB[:accounts].select_map(:status_id).must_equal [2]
|
157
157
|
|
158
158
|
res = json_request('/close-account', :password=>'0123456789')
|
@@ -59,7 +59,7 @@ describe 'Rodauth confirm password feature' do
|
|
59
59
|
json_request('/reset').must_equal [200, [1]]
|
60
60
|
|
61
61
|
res = json_request('/change-password', "new-password"=>'01234567', "password-confirm"=>'01234567')
|
62
|
-
res.must_equal [
|
62
|
+
res.must_equal [401, {"field-error"=>["password", "invalid password"], "error"=>"There was an error changing your password"}]
|
63
63
|
|
64
64
|
res = json_request('/confirm-password', "password"=>'0123456')
|
65
65
|
res.must_equal [200, {'success'=>"Your password has been confirmed"}]
|
data/spec/create_account_spec.rb
CHANGED
@@ -108,16 +108,16 @@ describe 'Rodauth create_account feature' do
|
|
108
108
|
end
|
109
109
|
|
110
110
|
res = json_request('/create-account', :login=>'foo@example.com', "login-confirm"=>'foo@example.com', :password=>'0123456789', "password-confirm"=>'0123456789')
|
111
|
-
res.must_equal [
|
111
|
+
res.must_equal [422, {'error'=>"There was an error creating your account", "field-error"=>["login", "invalid login, already an account with this login"]}]
|
112
112
|
|
113
113
|
res = json_request('/create-account', :login=>'foobar', "login-confirm"=>'foobar', :password=>'0123456789', "password-confirm"=>'0123456789')
|
114
|
-
res.must_equal [
|
114
|
+
res.must_equal [422, {'error'=>"There was an error creating your account", "field-error"=>["login", "invalid login, not a valid email address"]}]
|
115
115
|
|
116
116
|
res = json_request('/create-account', :login=>'foo@example2.com', "login-confirm"=>'foobar', :password=>'0123456789', "password-confirm"=>'0123456789')
|
117
|
-
res.must_equal [
|
117
|
+
res.must_equal [422, {'error'=>"There was an error creating your account", "field-error"=>["login", "logins do not match"]}]
|
118
118
|
|
119
119
|
res = json_request('/create-account', :login=>'foo@example2.com', "login-confirm"=>'foo@example2.com', :password=>'012345678', "password-confirm"=>'0123456789')
|
120
|
-
res.must_equal [
|
120
|
+
res.must_equal [422, {'error'=>"There was an error creating your account", "field-error"=>["password", "passwords do not match"]}]
|
121
121
|
|
122
122
|
res = json_request('/create-account', :login=>'foo@example2.com', "login-confirm"=>'foo@example2.com', :password=>'0123456789', "password-confirm"=>'0123456789')
|
123
123
|
res.must_equal [200, {'success'=>"Your account has been created", 'account_id'=>DB[:accounts].max(:id)}]
|
data/spec/lockout_spec.rb
CHANGED
@@ -190,33 +190,33 @@ describe 'Rodauth lockout feature' do
|
|
190
190
|
end
|
191
191
|
|
192
192
|
res = json_request('/unlock-account-request', :login=>'foo@example.com')
|
193
|
-
res.must_equal [
|
193
|
+
res.must_equal [401, {'error'=>"no matching login"}]
|
194
194
|
|
195
195
|
res = json_login(:pass=>'1', :no_check=>true)
|
196
|
-
res.must_equal [
|
196
|
+
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
197
197
|
|
198
198
|
json_login
|
199
199
|
json_logout
|
200
200
|
|
201
201
|
2.times do
|
202
202
|
res = json_login(:pass=>'1', :no_check=>true)
|
203
|
-
res.must_equal [
|
203
|
+
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
204
204
|
end
|
205
205
|
|
206
206
|
2.times do
|
207
207
|
res = json_login(:pass=>'1', :no_check=>true)
|
208
|
-
res.must_equal [
|
208
|
+
res.must_equal [403, {'error'=>"This account is currently locked out and cannot be logged in to."}]
|
209
209
|
end
|
210
210
|
|
211
211
|
res = json_request('/unlock-account')
|
212
|
-
res.must_equal [
|
212
|
+
res.must_equal [401, {'error'=>"No matching unlock account key"}]
|
213
213
|
|
214
214
|
res = json_request('/unlock-account-request', :login=>'foo@example.com')
|
215
215
|
res.must_equal [200, {'success'=>"An email has been sent to you with a link to unlock your account"}]
|
216
216
|
|
217
217
|
link = email_link(/key=.+$/)
|
218
218
|
res = json_request('/unlock-account', :key=>link[4...-1])
|
219
|
-
res.must_equal [
|
219
|
+
res.must_equal [401, {'error'=>"No matching unlock account key"}]
|
220
220
|
|
221
221
|
res = json_request('/unlock-account', :key=>link[4..-1])
|
222
222
|
res.must_equal [200, {'success'=>"Your account has been unlocked"}]
|
data/spec/login_spec.rb
CHANGED
@@ -157,6 +157,7 @@ describe 'Rodauth login feature' do
|
|
157
157
|
it "should login and logout via jwt" do
|
158
158
|
rodauth do
|
159
159
|
enable :login, :logout
|
160
|
+
json_response_custom_error_status? false
|
160
161
|
jwt_secret{proc{super()}.must_raise ArgumentError; "1"}
|
161
162
|
end
|
162
163
|
roda(:jwt) do |r|
|
@@ -179,4 +180,29 @@ describe 'Rodauth login feature' do
|
|
179
180
|
json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
|
180
181
|
json_request.must_equal [200, 2]
|
181
182
|
end
|
183
|
+
|
184
|
+
it "should login and logout via jwt with custom error statuses" do
|
185
|
+
rodauth do
|
186
|
+
enable :login, :logout
|
187
|
+
end
|
188
|
+
roda(:jwt) do |r|
|
189
|
+
r.rodauth
|
190
|
+
response['Content-Type'] = 'application/json'
|
191
|
+
rodauth.logged_in? ? '1' : '2'
|
192
|
+
end
|
193
|
+
|
194
|
+
json_request.must_equal [200, 2]
|
195
|
+
|
196
|
+
res = json_request("/login", :login=>'foo@example2.com', :password=>'0123456789')
|
197
|
+
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["login", "no matching login"]}]
|
198
|
+
|
199
|
+
res = json_request("/login", :login=>'foo@example.com', :password=>'012345678')
|
200
|
+
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
201
|
+
|
202
|
+
json_request("/login", :login=>'foo@example.com', :password=>'0123456789').must_equal [200, {"success"=>'You have been logged in'}]
|
203
|
+
json_request.must_equal [200, 1]
|
204
|
+
|
205
|
+
json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
|
206
|
+
json_request.must_equal [200, 2]
|
207
|
+
end
|
182
208
|
end
|
data/spec/remember_spec.rb
CHANGED
@@ -404,7 +404,7 @@ describe 'Rodauth remember feature' do
|
|
404
404
|
json_request.must_equal [200, [1]]
|
405
405
|
|
406
406
|
res = json_request('/confirm-password', :password=>'123456')
|
407
|
-
res.must_equal [
|
407
|
+
res.must_equal [401, {'error'=>"There was an error confirming your password", "field-error"=>["password", "invalid password"]}]
|
408
408
|
|
409
409
|
res = json_request('/confirm-password', :password=>'0123456789')
|
410
410
|
res.must_equal [200, {'success'=>"Your password has been confirmed"}]
|
data/spec/reset_password_spec.rb
CHANGED
@@ -156,32 +156,32 @@ describe 'Rodauth reset_password feature' do
|
|
156
156
|
end
|
157
157
|
|
158
158
|
res = json_login(:pass=>'1', :no_check=>true)
|
159
|
-
res.must_equal [
|
159
|
+
res.must_equal [401, {"field-error"=>["password", "invalid password"], "error"=>"There was an error logging in"}]
|
160
160
|
|
161
161
|
res = json_request('/reset-password')
|
162
|
-
res.must_equal [
|
162
|
+
res.must_equal [401, {"error"=>"There was an error resetting your password"}]
|
163
163
|
|
164
164
|
res = json_request('/reset-password-request', :login=>'foo@example2.com')
|
165
|
-
res.must_equal [
|
165
|
+
res.must_equal [401, {"error"=>"There was an error requesting a password reset"}]
|
166
166
|
|
167
167
|
res = json_request('/reset-password-request', :login=>'foo@example.com')
|
168
168
|
res.must_equal [200, {"success"=>"An email has been sent to you with a link to reset the password for your account"}]
|
169
169
|
|
170
170
|
link = email_link(/key=.+$/)
|
171
171
|
res = json_request('/reset-password', :key=>link[4...-1])
|
172
|
-
res.must_equal [
|
172
|
+
res.must_equal [401, {"error"=>"There was an error resetting your password"}]
|
173
173
|
|
174
174
|
res = json_request('/reset-password', :key=>link[4..-1], :password=>'1', "password-confirm"=>'2')
|
175
|
-
res.must_equal [
|
175
|
+
res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", 'passwords do not match']}]
|
176
176
|
|
177
177
|
res = json_request('/reset-password', :key=>link[4..-1], :password=>'0123456789', "password-confirm"=>'0123456789')
|
178
|
-
res.must_equal [
|
178
|
+
res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", 'invalid password, same as current password']}]
|
179
179
|
|
180
180
|
res = json_request('/reset-password', :key=>link[4..-1], :password=>'1', "password-confirm"=>'1')
|
181
|
-
res.must_equal [
|
181
|
+
res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", "invalid password, does not meet requirements (minimum 6 characters)"]}]
|
182
182
|
|
183
183
|
res = json_request('/reset-password', :key=>link[4..-1], :password=>"\0ab123456", "password-confirm"=>"\0ab123456")
|
184
|
-
res.must_equal [
|
184
|
+
res.must_equal [422, {"error"=>"There was an error resetting your password", "field-error"=>["password", "invalid password, does not meet requirements (contains null byte)"]}]
|
185
185
|
|
186
186
|
res = json_request('/reset-password', :key=>link[4..-1], :password=>'0123456', "password-confirm"=>'0123456')
|
187
187
|
res.must_equal [200, {"success"=>"Your password has been reset"}]
|
data/spec/spec_helper.rb
CHANGED
@@ -41,7 +41,8 @@ require 'logger'
|
|
41
41
|
require 'tilt/string'
|
42
42
|
|
43
43
|
db_url = ENV['RODAUTH_SPEC_DB'] || 'postgres:///?user=rodauth_test&password=rodauth_test'
|
44
|
-
DB = Sequel.connect(db_url)
|
44
|
+
DB = Sequel.connect(db_url, :identifier_mangling=>false)
|
45
|
+
DB.extension(:freeze_datasets)
|
45
46
|
puts "using #{DB.database_type}"
|
46
47
|
|
47
48
|
#DB.loggers << Logger.new($stdout)
|
@@ -110,6 +111,7 @@ class Minitest::HooksSpec
|
|
110
111
|
enable :jwt
|
111
112
|
jwt_secret '1'
|
112
113
|
json_response_success_key 'success'
|
114
|
+
json_response_custom_error_status? true
|
113
115
|
end
|
114
116
|
instance_exec(&rodauth_block)
|
115
117
|
end
|
data/spec/two_factor_spec.rb
CHANGED
@@ -1114,23 +1114,23 @@ describe 'Rodauth OTP feature' do
|
|
1114
1114
|
json_request.must_equal [200, [3]]
|
1115
1115
|
|
1116
1116
|
%w'/otp-disable /recovery-auth /recovery-codes /sms-setup /sms-confirm /otp-auth'.each do |path|
|
1117
|
-
json_request(path).must_equal [
|
1117
|
+
json_request(path).must_equal [403, {'error'=>'This account has not been setup for two factor authentication'}]
|
1118
1118
|
end
|
1119
1119
|
%w'/sms-disable /sms-request /sms-auth'.each do |path|
|
1120
|
-
json_request(path).must_equal [
|
1120
|
+
json_request(path).must_equal [403, {'error'=>'SMS authentication has not been setup yet.'}]
|
1121
1121
|
end
|
1122
1122
|
|
1123
1123
|
secret = ROTP::Base32.random_base32
|
1124
1124
|
totp = ROTP::TOTP.new(secret)
|
1125
1125
|
|
1126
1126
|
res = json_request('/otp-setup', :password=>'123456', :otp_secret=>secret)
|
1127
|
-
res.must_equal [
|
1127
|
+
res.must_equal [401, {'error'=>'Error setting up two factor authentication', "field-error"=>["password", 'invalid password']}]
|
1128
1128
|
|
1129
1129
|
res = json_request('/otp-setup', :password=>'0123456789', :otp=>'adsf', :otp_secret=>secret)
|
1130
|
-
res.must_equal [
|
1130
|
+
res.must_equal [401, {'error'=>'Error setting up two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1131
1131
|
|
1132
1132
|
res = json_request('/otp-setup', :password=>'0123456789', :otp=>'adsf', :otp_secret=>'asdf')
|
1133
|
-
res.must_equal [
|
1133
|
+
res.must_equal [422, {'error'=>'Error setting up two factor authentication', "field-error"=>["otp_secret", 'invalid secret']}]
|
1134
1134
|
|
1135
1135
|
res = json_request('/otp-setup', :password=>'0123456789', :otp=>totp.now, :otp_secret=>secret)
|
1136
1136
|
res.must_equal [200, {'success'=>'Two factor authentication is now setup'}]
|
@@ -1141,11 +1141,11 @@ describe 'Rodauth OTP feature' do
|
|
1141
1141
|
json_request.must_equal [200, [2]]
|
1142
1142
|
|
1143
1143
|
%w'/otp-disable /recovery-codes /otp-setup /sms-setup /sms-disable /sms-confirm'.each do |path|
|
1144
|
-
json_request(path).must_equal [
|
1144
|
+
json_request(path).must_equal [401, {'error'=>'You need to authenticate via 2nd factor before continuing.'}]
|
1145
1145
|
end
|
1146
1146
|
|
1147
1147
|
res = json_request('/otp-auth', :otp=>'adsf')
|
1148
|
-
res.must_equal [
|
1148
|
+
res.must_equal [401, {'error'=>'Error logging in via two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1149
1149
|
|
1150
1150
|
res = json_request('/otp-auth', :otp=>totp.now)
|
1151
1151
|
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
@@ -1157,17 +1157,17 @@ describe 'Rodauth OTP feature' do
|
|
1157
1157
|
|
1158
1158
|
%w'/otp-auth /recovery-auth /sms-request /sms-auth'.each do |path|
|
1159
1159
|
res = json_request(path)
|
1160
|
-
res.must_equal [
|
1160
|
+
res.must_equal [403, {'error'=>'Already authenticated via 2nd factor'}]
|
1161
1161
|
end
|
1162
1162
|
|
1163
1163
|
res = json_request('/sms-disable')
|
1164
|
-
res.must_equal [
|
1164
|
+
res.must_equal [403, {'error'=>'SMS authentication has not been setup yet.'}]
|
1165
1165
|
|
1166
1166
|
res = json_request('/sms-setup', :password=>'012345678', "sms-phone"=>'(123) 456')
|
1167
|
-
res.must_equal [
|
1167
|
+
res.must_equal [401, {'error'=>'Error setting up SMS authentication', "field-error"=>["password", 'invalid password']}]
|
1168
1168
|
|
1169
1169
|
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 456')
|
1170
|
-
res.must_equal [
|
1170
|
+
res.must_equal [422, {'error'=>'Error setting up SMS authentication', "field-error"=>["sms-phone", 'invalid SMS phone number']}]
|
1171
1171
|
|
1172
1172
|
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1173
1173
|
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
@@ -1176,14 +1176,14 @@ describe 'Rodauth OTP feature' do
|
|
1176
1176
|
sms_message.must_match(/\ASMS confirmation code for example\.com: is \d{12}\z/)
|
1177
1177
|
|
1178
1178
|
res = json_request('/sms-confirm', :sms_code=>'asdf')
|
1179
|
-
res.must_equal [
|
1179
|
+
res.must_equal [401, {'error'=>'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'}]
|
1180
1180
|
|
1181
1181
|
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1182
1182
|
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
1183
1183
|
|
1184
1184
|
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
1185
1185
|
res = json_request('/sms-confirm', :sms_code=>sms_code)
|
1186
|
-
res.must_equal [
|
1186
|
+
res.must_equal [401, {'error'=>'Invalid or out of date SMS confirmation code used, must setup SMS authentication again.'}]
|
1187
1187
|
|
1188
1188
|
res = json_request('/sms-setup', :password=>'0123456789', "sms-phone"=>'(123) 4567 890')
|
1189
1189
|
res.must_equal [200, {'success'=>'SMS authentication needs confirmation.'}]
|
@@ -1193,14 +1193,14 @@ describe 'Rodauth OTP feature' do
|
|
1193
1193
|
|
1194
1194
|
%w'/sms-setup /sms-confirm'.each do |path|
|
1195
1195
|
res = json_request(path)
|
1196
|
-
res.must_equal [
|
1196
|
+
res.must_equal [403, {'error'=>'SMS authentication has already been setup.'}]
|
1197
1197
|
end
|
1198
1198
|
|
1199
1199
|
json_logout
|
1200
1200
|
json_login
|
1201
1201
|
|
1202
1202
|
res = json_request('/sms-auth')
|
1203
|
-
res.must_equal [
|
1203
|
+
res.must_equal [401, {'error'=>'No current SMS code for this account'}]
|
1204
1204
|
|
1205
1205
|
sms_phone = sms_message = nil
|
1206
1206
|
res = json_request('/sms-request')
|
@@ -1209,11 +1209,11 @@ describe 'Rodauth OTP feature' do
|
|
1209
1209
|
sms_message.must_match(/\ASMS authentication code for example\.com: is \d{6}\z/)
|
1210
1210
|
|
1211
1211
|
res = json_request('/sms-auth')
|
1212
|
-
res.must_equal [
|
1212
|
+
res.must_equal [401, {'error'=>'Error authenticating via SMS code.', "field-error"=>["sms-code", "invalid SMS code"]}]
|
1213
1213
|
|
1214
1214
|
DB[:account_sms_codes].update(:code_issued_at=>Time.now - 310)
|
1215
1215
|
res = json_request('/sms-auth')
|
1216
|
-
res.must_equal [
|
1216
|
+
res.must_equal [401, {'error'=>'No current SMS code for this account'}]
|
1217
1217
|
|
1218
1218
|
res = json_request('/sms-request')
|
1219
1219
|
res.must_equal [200, {'success'=>'SMS authentication code has been sent.'}]
|
@@ -1230,21 +1230,21 @@ describe 'Rodauth OTP feature' do
|
|
1230
1230
|
|
1231
1231
|
5.times do
|
1232
1232
|
res = json_request('/sms-auth')
|
1233
|
-
res.must_equal [
|
1233
|
+
res.must_equal [401, {'error'=>'Error authenticating via SMS code.', "field-error"=>["sms-code", "invalid SMS code"]}]
|
1234
1234
|
end
|
1235
1235
|
|
1236
1236
|
res = json_request('/sms-auth')
|
1237
|
-
res.must_equal [
|
1237
|
+
res.must_equal [403, {'error'=>'SMS authentication has been locked out.'}]
|
1238
1238
|
|
1239
1239
|
res = json_request('/sms-request')
|
1240
|
-
res.must_equal [
|
1240
|
+
res.must_equal [403, {'error'=>'SMS authentication has been locked out.'}]
|
1241
1241
|
|
1242
1242
|
res = json_request('/otp-auth', :otp=>totp.now)
|
1243
1243
|
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
1244
1244
|
json_request.must_equal [200, [1]]
|
1245
1245
|
|
1246
1246
|
res = json_request('/sms-disable', :password=>'012345678')
|
1247
|
-
res.must_equal [
|
1247
|
+
res.must_equal [401, {'error'=>'Error disabling SMS authentication', "field-error"=>["password", 'invalid password']}]
|
1248
1248
|
|
1249
1249
|
res = json_request('/sms-disable', :password=>'0123456789')
|
1250
1250
|
res.must_equal [200, {'success'=>'SMS authentication has been disabled.'}]
|
@@ -1256,7 +1256,7 @@ describe 'Rodauth OTP feature' do
|
|
1256
1256
|
res.must_equal [200, {'success'=>'SMS authentication has been setup.'}]
|
1257
1257
|
|
1258
1258
|
res = json_request('/recovery-codes', :password=>'asdf')
|
1259
|
-
res.must_equal [
|
1259
|
+
res.must_equal [401, {'error'=>'Unable to view recovery codes.', "field-error"=>["password", 'invalid password']}]
|
1260
1260
|
|
1261
1261
|
res = json_request('/recovery-codes', :password=>'0123456789')
|
1262
1262
|
codes = res[1].delete('codes')
|
@@ -1268,26 +1268,26 @@ describe 'Rodauth OTP feature' do
|
|
1268
1268
|
|
1269
1269
|
5.times do
|
1270
1270
|
res = json_request('/otp-auth', :otp=>'asdf')
|
1271
|
-
res.must_equal [
|
1271
|
+
res.must_equal [401, {'error'=>'Error logging in via two factor authentication', "field-error"=>["otp", 'Invalid authentication code']}]
|
1272
1272
|
end
|
1273
1273
|
|
1274
1274
|
res = json_request('/otp-auth', :otp=>'asdf')
|
1275
|
-
res.must_equal [
|
1275
|
+
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.'}]
|
1276
1276
|
|
1277
1277
|
res = json_request('/sms-request')
|
1278
1278
|
5.times do
|
1279
1279
|
res = json_request('/sms-auth')
|
1280
|
-
res.must_equal [
|
1280
|
+
res.must_equal [401, {'error'=>'Error authenticating via SMS code.', "field-error"=>["sms-code", "invalid SMS code"]}]
|
1281
1281
|
end
|
1282
1282
|
|
1283
1283
|
res = json_request('/otp-auth', :otp=>'asdf')
|
1284
|
-
res.must_equal [
|
1284
|
+
res.must_equal [403, {'error'=>'Authentication code use locked out due to numerous failures. Can use recovery code to unlock.'}]
|
1285
1285
|
|
1286
1286
|
res = json_request('/sms-auth')
|
1287
|
-
res.must_equal [
|
1287
|
+
res.must_equal [403, {'error'=>'SMS authentication has been locked out.'}]
|
1288
1288
|
|
1289
1289
|
res = json_request('/recovery-auth', 'recovery-code'=>'adsf')
|
1290
|
-
res.must_equal [
|
1290
|
+
res.must_equal [401, {'error'=>'Error authenticating via recovery code.', "field-error"=>["recovery-code", "Invalid recovery code"]}]
|
1291
1291
|
|
1292
1292
|
res = json_request('/recovery-auth', 'recovery-code'=>codes.first)
|
1293
1293
|
res.must_equal [200, {'success'=>'You have been authenticated via 2nd factor'}]
|
@@ -1299,7 +1299,7 @@ describe 'Rodauth OTP feature' do
|
|
1299
1299
|
res.must_equal [200, {'success'=>''}]
|
1300
1300
|
|
1301
1301
|
res = json_request('/recovery-codes', :password=>'012345678', :add=>'1')
|
1302
|
-
res.must_equal [
|
1302
|
+
res.must_equal [401, {'error'=>'Unable to add recovery codes.', "field-error"=>["password", 'invalid password']}]
|
1303
1303
|
|
1304
1304
|
res = json_request('/recovery-codes', :password=>'0123456789', :add=>'1')
|
1305
1305
|
codes3 = res[1].delete('codes')
|
@@ -1307,7 +1307,7 @@ describe 'Rodauth OTP feature' do
|
|
1307
1307
|
res.must_equal [200, {'success'=>'Additional authentication recovery codes have been added.'}]
|
1308
1308
|
|
1309
1309
|
res = json_request('/otp-disable', :password=>'012345678')
|
1310
|
-
res.must_equal [
|
1310
|
+
res.must_equal [401, {'error'=>'Error disabling up two factor authentication', "field-error"=>["password", 'invalid password']}]
|
1311
1311
|
|
1312
1312
|
res = json_request('/otp-disable', :password=>'0123456789')
|
1313
1313
|
res.must_equal [200, {'success'=>'Two factor authentication has been disabled'}]
|
data/spec/verify_account_spec.rb
CHANGED
@@ -116,23 +116,23 @@ describe 'Rodauth verify_account feature' do
|
|
116
116
|
link = email_link(/key=.+$/, 'foo@example2.com')
|
117
117
|
|
118
118
|
res = json_request('/verify-account-resend', :login=>'foo@example.com')
|
119
|
-
res.must_equal [
|
119
|
+
res.must_equal [401, {'error'=>"Unable to resend verify account email"}]
|
120
120
|
|
121
121
|
res = json_request('/verify-account-resend', :login=>'foo@example3.com')
|
122
|
-
res.must_equal [
|
122
|
+
res.must_equal [401, {'error'=>"Unable to resend verify account email"}]
|
123
123
|
|
124
124
|
res = json_request('/login', :login=>'foo@example2.com',:password=>'0123456789')
|
125
|
-
res.must_equal [
|
125
|
+
res.must_equal [403, {'error'=>"The account you tried to login with is currently awaiting verification"}]
|
126
126
|
|
127
127
|
res = json_request('/verify-account-resend', :login=>'foo@example2.com')
|
128
128
|
res.must_equal [200, {'success'=>"An email has been sent to you with a link to verify your account"}]
|
129
129
|
email_link(/key=.+$/, 'foo@example2.com').must_equal link
|
130
130
|
|
131
131
|
res = json_request('/verify-account')
|
132
|
-
res.must_equal [
|
132
|
+
res.must_equal [401, {'error'=>"Unable to verify account"}]
|
133
133
|
|
134
134
|
res = json_request('/verify-account', :key=>link[4...-1])
|
135
|
-
res.must_equal [
|
135
|
+
res.must_equal [401, {"error"=>"Unable to verify account"}]
|
136
136
|
|
137
137
|
res = json_request('/verify-account', :key=>link[4..-1])
|
138
138
|
res.must_equal [200, {"success"=>"Your account has been verified"}]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -234,6 +234,7 @@ extra_rdoc_files:
|
|
234
234
|
- doc/release_notes/1.5.0.txt
|
235
235
|
- doc/release_notes/1.6.0.txt
|
236
236
|
- doc/release_notes/1.7.0.txt
|
237
|
+
- doc/release_notes/1.8.0.txt
|
237
238
|
files:
|
238
239
|
- CHANGELOG
|
239
240
|
- MIT-LICENSE
|
@@ -267,6 +268,7 @@ files:
|
|
267
268
|
- doc/release_notes/1.5.0.txt
|
268
269
|
- doc/release_notes/1.6.0.txt
|
269
270
|
- doc/release_notes/1.7.0.txt
|
271
|
+
- doc/release_notes/1.8.0.txt
|
270
272
|
- doc/remember.rdoc
|
271
273
|
- doc/reset_password.rdoc
|
272
274
|
- doc/session_expiration.rdoc
|