rodauth 2.2.0 → 2.3.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 +14 -0
- data/doc/jwt_refresh.rdoc +5 -0
- data/doc/login.rdoc +7 -0
- data/doc/login_password_requirements_base.rdoc +3 -0
- data/doc/release_notes/2.3.0.txt +37 -0
- data/lib/rodauth/features/close_account.rb +8 -6
- data/lib/rodauth/features/email_auth.rb +1 -1
- data/lib/rodauth/features/jwt_refresh.rb +10 -4
- data/lib/rodauth/features/login.rb +15 -10
- data/lib/rodauth/features/login_password_requirements_base.rb +9 -4
- data/lib/rodauth/features/session_expiration.rb +1 -6
- data/lib/rodauth/features/webauthn_login.rb +1 -1
- data/lib/rodauth/migrations.rb +16 -5
- data/lib/rodauth/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ea58deac2998a11cd0cd4b17545eab5fce6fb0acd7751416fd521cbd77d6add
|
4
|
+
data.tar.gz: c113c1131f5d756fd3aa5c1dbf083f40506b8822fb1c9b67537069c9e8695fba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fa2ce1d08a9f09e21d2fb1fe5b71ea3c6fc55181452302a35929229f0fad8c3c5d3d3324d73bee9c69daf0ff7a2d14ddbb731ef994bd3317512627233a69d28
|
7
|
+
data.tar.gz: 7a188457006390730f00bc4a2ea5159c002dba66b300fd993929a5b5a491f2f4be0191c7e7c8e4b9e82b2e709b1a4e8ea8f5d335613f262671e1d68507cc2e26
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
=== 2.3.0 (2020-08-21)
|
2
|
+
|
3
|
+
* Return an error status instead of an invalid access token when trying to refresh JWT without an access token in the jwt_refresh feature (jeremyevans)
|
4
|
+
|
5
|
+
* Allow {create,drop}_database_authentication_functions to work with UUID keys (monorkin, janko) (#117)
|
6
|
+
|
7
|
+
* Add rodauth.login('login_type') for logging in after setting a valid account (janko) (#114)
|
8
|
+
|
9
|
+
* Make new refresh token available to the after_refresh_token hook by setting it in the response first (jeremyevans)
|
10
|
+
|
11
|
+
* Make the jwt_refresh plugin call before_jwt_refresh_route hook (previously the configuration method was ignored) (AlexeyMatskevich) (#110)
|
12
|
+
|
13
|
+
* Add login_email_regexp, login_not_valid_email_message, and log_valid_email? configuration methods (janko) (#107)
|
14
|
+
|
1
15
|
=== 2.2.0 (2020-07-20)
|
2
16
|
|
3
17
|
* Allow removing all jwt_refresh tokens when logging out by providing a value of "all" as the token to remove (jeremyevans)
|
data/doc/jwt_refresh.rdoc
CHANGED
@@ -19,6 +19,9 @@ when logging out, provide the refresh token when submitting the JSON request to
|
|
19
19
|
If you would like to remove all refresh tokens for the account when logging out, provide
|
20
20
|
a value of <tt>all</tt> as the token value.
|
21
21
|
|
22
|
+
When using the refresh token, you must provide a valid access token, as that contains
|
23
|
+
information about the current session, which is used to create the new access token.
|
24
|
+
|
22
25
|
This feature depends on the jwt feature.
|
23
26
|
|
24
27
|
== Auth Value Methods
|
@@ -36,6 +39,8 @@ jwt_refresh_token_key :: Name of the key in the response json holding the refres
|
|
36
39
|
jwt_refresh_token_key_column :: The column name in the +jwt_refresh_token_table+ holding the refresh token key value.
|
37
40
|
jwt_refresh_token_key_param :: Name of parameter in which the refresh token is provided when requesting a new token. Default is +refresh_token+.
|
38
41
|
jwt_refresh_token_table :: Name of the table holding refresh token keys.
|
42
|
+
jwt_refresh_without_access_token_message :: Error message when trying to refresh with providing an access token.
|
43
|
+
jwt_refresh_without_access_token_status :: The HTTP status code to use when trying to refresh without providing an access token.
|
39
44
|
|
40
45
|
== Auth Methods
|
41
46
|
|
data/doc/login.rdoc
CHANGED
@@ -3,6 +3,13 @@
|
|
3
3
|
The login feature implements a login page. It's the most commonly
|
4
4
|
used feature.
|
5
5
|
|
6
|
+
In addition to the auth methods below, it provides a +login+ method that wraps
|
7
|
+
+login_session+, running login hooks and redirecting to the configured
|
8
|
+
location.
|
9
|
+
|
10
|
+
rodauth.account #=> { id: 123, ... }
|
11
|
+
rodauth.login('password') # login the current account
|
12
|
+
|
6
13
|
== Auth Value Methods
|
7
14
|
|
8
15
|
login_additional_form_tags :: HTML fragment containing additional form tags to use on the login form.
|
@@ -9,8 +9,10 @@ already_an_account_with_this_login_message :: The error message to display when
|
|
9
9
|
login_confirm_label :: The label to use for login confirmations.
|
10
10
|
login_confirm_param :: The parameter name to use for login confirmations.
|
11
11
|
login_does_not_meet_requirements_message :: The error message to display when the login does not meet the requirements you have set.
|
12
|
+
login_email_regexp :: The regular expression used to validate whether login is a valid email address.
|
12
13
|
login_maximum_length :: The maximum length for logins, 255 by default.
|
13
14
|
login_minimum_length :: The minimum length for logins, 3 by default.
|
15
|
+
login_not_valid_email_message :: The error message to display when login is not a valid email address.
|
14
16
|
login_too_long_message :: The error message fragment to show if the login is too long.
|
15
17
|
login_too_short_message :: The error message fragment to show if the login is too short.
|
16
18
|
logins_do_not_match_message :: The error message to display when login and login confirmation do not match.
|
@@ -29,6 +31,7 @@ same_as_existing_password_message :: The error message to display when a new pas
|
|
29
31
|
== Auth Methods
|
30
32
|
|
31
33
|
login_meets_requirements?(login) :: Whether the given login meets the requirements. By default, just checks that the login is a valid email address.
|
34
|
+
login_valid_email?(login) :: Whether the login is a valid email address.
|
32
35
|
password_hash(password) :: A hash of the given password.
|
33
36
|
password_meets_requirements?(password) :: Whether the given password meets the requirements. Can be used to implement complexity requirements for passwords.
|
34
37
|
set_password(password) :: Set the password for the current account to the given password.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Configuration methods have been added for easier validation of
|
4
|
+
logins when logins must be valid email addresses (the default):
|
5
|
+
|
6
|
+
* login_valid_email?(login) can be used for full control of
|
7
|
+
determining whether the login is valid.
|
8
|
+
|
9
|
+
* login_email_regexp can be used to set the regexp used in the
|
10
|
+
default login_valid_email? check.
|
11
|
+
|
12
|
+
* login_not_valid_email_message can be used to set the field
|
13
|
+
error message if the login is not a valid email. Previously, this
|
14
|
+
value was hardcoded and not translatable.
|
15
|
+
|
16
|
+
* The {create,drop}_database_authentication_functions now work
|
17
|
+
correctly with uuid keys on PostgreSQL. All other parts of
|
18
|
+
Rodauth already worked correctly with uuid keys.
|
19
|
+
|
20
|
+
= Other Improvements
|
21
|
+
|
22
|
+
* The before_jwt_refresh_route hook is now called before the route
|
23
|
+
is taken. Previously, the configuration method had no effect.
|
24
|
+
|
25
|
+
* rodauth.login can now be used by external code to login the current
|
26
|
+
account (the account that rodauth.account returns). This should be
|
27
|
+
passed the authentication type string used to login, such as
|
28
|
+
password.
|
29
|
+
|
30
|
+
* The jwt_refresh route now returns an error for requests where a
|
31
|
+
valid access token for a logged in session is not provided. You
|
32
|
+
can use the jwt_refresh_without_access_token_message and
|
33
|
+
jwt_refresh_without_access_token_status configuration methods
|
34
|
+
to configure the error response.
|
35
|
+
|
36
|
+
* The new refresh token is now available to the after_refresh_token
|
37
|
+
hook by looking in json_response[jwt_refresh_token_key].
|
@@ -33,7 +33,11 @@ module Rodauth
|
|
33
33
|
end
|
34
34
|
|
35
35
|
r.post do
|
36
|
-
|
36
|
+
catch_error do
|
37
|
+
if close_account_requires_password? && !password_match?(param(password_param))
|
38
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
39
|
+
end
|
40
|
+
|
37
41
|
transaction do
|
38
42
|
before_close_account
|
39
43
|
close_account
|
@@ -46,12 +50,10 @@ module Rodauth
|
|
46
50
|
|
47
51
|
set_notice_flash close_account_notice_flash
|
48
52
|
redirect close_account_redirect
|
49
|
-
else
|
50
|
-
set_response_error_status(invalid_password_error_status)
|
51
|
-
set_field_error(password_param, invalid_password_message)
|
52
|
-
set_error_flash close_account_error_flash
|
53
|
-
close_account_view
|
54
53
|
end
|
54
|
+
|
55
|
+
set_error_flash close_account_error_flash
|
56
|
+
close_account_view
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -19,23 +19,29 @@ module Rodauth
|
|
19
19
|
auth_value_method :jwt_refresh_token_key_column, :key
|
20
20
|
auth_value_method :jwt_refresh_token_key_param, 'refresh_token'
|
21
21
|
auth_value_method :jwt_refresh_token_table, :account_jwt_refresh_keys
|
22
|
+
translatable_method :jwt_refresh_without_access_token_message, 'no JWT access token provided during refresh'
|
23
|
+
auth_value_method :jwt_refresh_without_access_token_status, 401
|
22
24
|
|
23
25
|
auth_private_methods(
|
24
26
|
:account_from_refresh_token
|
25
27
|
)
|
26
28
|
|
27
29
|
route do |r|
|
30
|
+
before_jwt_refresh_route
|
31
|
+
|
28
32
|
r.post do
|
29
|
-
if
|
30
|
-
|
33
|
+
if !session_value
|
34
|
+
response.status ||= jwt_refresh_without_access_token_status
|
35
|
+
json_response[json_response_error_key] = jwt_refresh_without_access_token_message
|
36
|
+
elsif (refresh_token = param_or_nil(jwt_refresh_token_key_param)) && account_from_refresh_token(refresh_token)
|
31
37
|
transaction do
|
32
38
|
before_refresh_token
|
33
39
|
formatted_token = generate_refresh_token
|
34
40
|
remove_jwt_refresh_token_key(refresh_token)
|
41
|
+
json_response[jwt_refresh_token_key] = formatted_token
|
42
|
+
json_response[jwt_access_token_key] = session_jwt
|
35
43
|
after_refresh_token
|
36
44
|
end
|
37
|
-
json_response[jwt_refresh_token_key] = formatted_token
|
38
|
-
json_response[jwt_access_token_key] = session_jwt
|
39
45
|
else
|
40
46
|
json_response[json_response_error_key] = jwt_refresh_invalid_token_message
|
41
47
|
response.status ||= json_response_error_status
|
@@ -62,7 +62,7 @@ module Rodauth
|
|
62
62
|
throw_error_status(login_error_status, password_param, invalid_password_message)
|
63
63
|
end
|
64
64
|
|
65
|
-
|
65
|
+
login('password')
|
66
66
|
end
|
67
67
|
|
68
68
|
set_error_flash login_error_flash unless skip_error_flash
|
@@ -72,6 +72,18 @@ module Rodauth
|
|
72
72
|
|
73
73
|
attr_reader :login_form_header
|
74
74
|
|
75
|
+
def login(auth_type)
|
76
|
+
saved_login_redirect = remove_session_value(login_redirect_session_key)
|
77
|
+
transaction do
|
78
|
+
before_login
|
79
|
+
login_session(auth_type)
|
80
|
+
yield if block_given?
|
81
|
+
after_login
|
82
|
+
end
|
83
|
+
set_notice_flash login_notice_flash
|
84
|
+
redirect(saved_login_redirect || login_redirect)
|
85
|
+
end
|
86
|
+
|
75
87
|
def login_required
|
76
88
|
if login_return_to_requested_location?
|
77
89
|
set_session_value(login_redirect_session_key, request.fullpath)
|
@@ -126,15 +138,8 @@ module Rodauth
|
|
126
138
|
end
|
127
139
|
|
128
140
|
def _login(auth_type)
|
129
|
-
|
130
|
-
|
131
|
-
before_login
|
132
|
-
login_session(auth_type)
|
133
|
-
yield if block_given?
|
134
|
-
after_login
|
135
|
-
end
|
136
|
-
set_notice_flash login_notice_flash
|
137
|
-
redirect(saved_login_redirect || login_redirect)
|
141
|
+
warn("Deprecated #_login method called, use #login instead.")
|
142
|
+
login(auth_type)
|
138
143
|
end
|
139
144
|
end
|
140
145
|
end
|
@@ -4,8 +4,10 @@ module Rodauth
|
|
4
4
|
Feature.define(:login_password_requirements_base, :LoginPasswordRequirementsBase) do
|
5
5
|
translatable_method :already_an_account_with_this_login_message, 'already an account with this login'
|
6
6
|
auth_value_method :login_confirm_param, 'login-confirm'
|
7
|
+
auth_value_method :login_email_regexp, /\A[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+\z/
|
7
8
|
auth_value_method :login_minimum_length, 3
|
8
9
|
auth_value_method :login_maximum_length, 255
|
10
|
+
translatable_method :login_not_valid_email_message, 'not a valid email address'
|
9
11
|
translatable_method :logins_do_not_match_message, 'logins do not match'
|
10
12
|
auth_value_method :password_confirm_param, 'password-confirm'
|
11
13
|
auth_value_method :password_minimum_length, 6
|
@@ -28,6 +30,7 @@ module Rodauth
|
|
28
30
|
|
29
31
|
auth_methods(
|
30
32
|
:login_meets_requirements?,
|
33
|
+
:login_valid_email?,
|
31
34
|
:password_hash,
|
32
35
|
:password_meets_requirements?,
|
33
36
|
:set_password
|
@@ -104,13 +107,15 @@ module Rodauth
|
|
104
107
|
|
105
108
|
def login_meets_email_requirements?(login)
|
106
109
|
return true unless require_email_address_logins?
|
107
|
-
if login
|
108
|
-
|
109
|
-
end
|
110
|
-
@login_requirement_message = 'not a valid email address'
|
110
|
+
return true if login_valid_email?(login)
|
111
|
+
@login_requirement_message = login_not_valid_email_message
|
111
112
|
return false
|
112
113
|
end
|
113
114
|
|
115
|
+
def login_valid_email?(login)
|
116
|
+
login =~ login_email_regexp
|
117
|
+
end
|
118
|
+
|
114
119
|
def password_meets_length_requirements?(password)
|
115
120
|
return true if password_minimum_length <= password.length
|
116
121
|
@password_requirement_message = password_too_short_message
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Rodauth
|
4
4
|
Feature.define(:session_expiration, :SessionExpiration) do
|
5
5
|
error_flash "This session has expired, please login again"
|
6
|
+
redirect{require_login_redirect}
|
6
7
|
|
7
8
|
auth_value_method :max_session_lifetime, 86400
|
8
9
|
session_key :session_created_session_key, :session_created_at
|
@@ -11,8 +12,6 @@ module Rodauth
|
|
11
12
|
auth_value_method :session_inactivity_timeout, 1800
|
12
13
|
session_key :session_last_activity_session_key, :last_session_activity_at
|
13
14
|
|
14
|
-
auth_value_methods :session_expiration_redirect
|
15
|
-
|
16
15
|
def check_session_expiration
|
17
16
|
return unless logged_in?
|
18
17
|
|
@@ -43,10 +42,6 @@ module Rodauth
|
|
43
42
|
redirect session_expiration_redirect
|
44
43
|
end
|
45
44
|
|
46
|
-
def session_expiration_redirect
|
47
|
-
require_login_redirect
|
48
|
-
end
|
49
|
-
|
50
45
|
def update_session
|
51
46
|
super
|
52
47
|
t = Time.now.to_i
|
data/lib/rodauth/migrations.rb
CHANGED
@@ -9,9 +9,14 @@ module Rodauth
|
|
9
9
|
case db.database_type
|
10
10
|
when :postgres
|
11
11
|
search_path = opts[:search_path] || 'public, pg_temp'
|
12
|
+
primary_key_type =
|
13
|
+
case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
|
14
|
+
when 'uuid' then :uuid
|
15
|
+
else :int8
|
16
|
+
end
|
12
17
|
|
13
18
|
db.run <<END
|
14
|
-
CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id
|
19
|
+
CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$
|
15
20
|
DECLARE salt text;
|
16
21
|
BEGIN
|
17
22
|
SELECT substr(password_hash, 0, 30) INTO salt
|
@@ -25,7 +30,7 @@ SET search_path = #{search_path};
|
|
25
30
|
END
|
26
31
|
|
27
32
|
db.run <<END
|
28
|
-
CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id
|
33
|
+
CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id #{primary_key_type}, hash text) RETURNS boolean AS $$
|
29
34
|
DECLARE valid boolean;
|
30
35
|
BEGIN
|
31
36
|
SELECT password_hash = hash INTO valid
|
@@ -100,13 +105,19 @@ END
|
|
100
105
|
end
|
101
106
|
|
102
107
|
def self.drop_database_authentication_functions(db, opts={})
|
108
|
+
table_name = opts[:table_name] || :account_password_hashes
|
103
109
|
get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
|
104
110
|
valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
|
105
111
|
|
106
112
|
case db.database_type
|
107
113
|
when :postgres
|
108
|
-
|
109
|
-
|
114
|
+
primary_key_type =
|
115
|
+
case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
|
116
|
+
when 'uuid' then :uuid
|
117
|
+
else :int8
|
118
|
+
end
|
119
|
+
db.run "DROP FUNCTION #{get_salt_name}(#{primary_key_type})"
|
120
|
+
db.run "DROP FUNCTION #{valid_hash_name}(#{primary_key_type}, text)"
|
110
121
|
when :mysql, :mssql
|
111
122
|
db.run "DROP FUNCTION #{get_salt_name}"
|
112
123
|
db.run "DROP FUNCTION #{valid_hash_name}"
|
@@ -118,6 +129,6 @@ END
|
|
118
129
|
end
|
119
130
|
|
120
131
|
def self.drop_database_previous_password_check_functions(db, opts={})
|
121
|
-
drop_database_authentication_functions(db, {:get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts))
|
132
|
+
drop_database_authentication_functions(db, {:table_name=>:account_previous_password_hashes, :get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts))
|
122
133
|
end
|
123
134
|
end
|
data/lib/rodauth/version.rb
CHANGED
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: 2.
|
4
|
+
version: 2.3.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: 2020-
|
11
|
+
date: 2020-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -304,6 +304,7 @@ extra_rdoc_files:
|
|
304
304
|
- doc/release_notes/2.0.0.txt
|
305
305
|
- doc/release_notes/2.1.0.txt
|
306
306
|
- doc/release_notes/2.2.0.txt
|
307
|
+
- doc/release_notes/2.3.0.txt
|
307
308
|
files:
|
308
309
|
- CHANGELOG
|
309
310
|
- MIT-LICENSE
|
@@ -384,6 +385,7 @@ files:
|
|
384
385
|
- doc/release_notes/2.0.0.txt
|
385
386
|
- doc/release_notes/2.1.0.txt
|
386
387
|
- doc/release_notes/2.2.0.txt
|
388
|
+
- doc/release_notes/2.3.0.txt
|
387
389
|
- doc/remember.rdoc
|
388
390
|
- doc/reset_password.rdoc
|
389
391
|
- doc/session_expiration.rdoc
|