rodauth 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|