rodauth 1.21.0 → 1.22.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/README.rdoc +3 -1
- data/doc/base.rdoc +1 -1
- data/doc/jwt_cors.rdoc +23 -0
- data/doc/release_notes/1.22.0.txt +11 -0
- data/lib/rodauth/features/jwt_cors.rb +53 -0
- data/lib/rodauth/version.rb +1 -1
- data/spec/disallow_common_passwords_spec.rb +1 -1
- data/spec/jwt_cors_spec.rb +57 -0
- data/spec/spec_helper.rb +2 -2
- data/templates/email-auth-email.str +1 -1
- data/templates/reset-password-email.str +1 -1
- data/templates/unlock-account-email.str +1 -1
- data/templates/verify-account-email.str +1 -1
- data/templates/verify-login-change-email.str +2 -1
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3379479fde31b70cd341f7d862088a1c29f850a4293bbf08a4ddced9f152a026
|
4
|
+
data.tar.gz: 3ec8abcd54a4da0a230d8d5a064d75febc247ee6ed35e3282b9b3ab2e1dde21d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6617d309568f8292c01d00343fdea602692fc6ca0be38d9846f0e8415a6da13cee10007e3286e03e2eb9170f693d4b2420861167225aab35ebea87b111627819
|
7
|
+
data.tar.gz: 30efd30667ccb724c0796aa58123beb4d2711ac92a032be21d900ae74391901521d60b6b4806463494fc748461ecac3adb28ce3bf7c1956e6dcd4b72b0fea963
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
=== 1.22.0 (2019-10-29)
|
2
|
+
|
3
|
+
* Add jwt_cors feature to handle Cross-Origin Resource Sharing when using the jwt feature (jeremyevans)
|
4
|
+
|
5
|
+
* Add space before newline after links in email, fixing issues with some webmail providers with broken autolinkers (jeremyevans)
|
6
|
+
|
1
7
|
=== 1.21.0 (2019-07-24)
|
2
8
|
|
3
9
|
* Support rotp 5.1 in the otp feature (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -42,6 +42,7 @@ hashes by protecting access via database functions.
|
|
42
42
|
* Single Session (Only one active session per account)
|
43
43
|
* JWT (JSON API support for all other features)
|
44
44
|
* JWT Refresh (Access & Refresh Token)
|
45
|
+
* JWT CORS (Cross-Origin Resource Sharing)
|
45
46
|
* Update Password Hash (when hash cost changes)
|
46
47
|
* HTTP Basic Auth
|
47
48
|
* Change Password Notify
|
@@ -811,7 +812,8 @@ view the appropriate file in the doc directory.
|
|
811
812
|
* {Session Expiration}[rdoc-ref:doc/session_expiration.rdoc]
|
812
813
|
* {Single Session}[rdoc-ref:doc/single_session.rdoc]
|
813
814
|
* {JWT}[rdoc-ref:doc/jwt.rdoc]
|
814
|
-
* {JWT}[rdoc-ref:doc/jwt_refresh.rdoc]
|
815
|
+
* {JWT Refresh}[rdoc-ref:doc/jwt_refresh.rdoc]
|
816
|
+
* {JWT CORS}[rdoc-ref:doc/jwt_cors.rdoc]
|
815
817
|
* {HTTP Basic Auth}[rdoc-ref:doc/http_basic_auth.rdoc]
|
816
818
|
|
817
819
|
=== Calling Rodauth in the Routing Tree
|
data/doc/base.rdoc
CHANGED
@@ -64,7 +64,7 @@ input_field_label_suffix :: The suffix to use for all labels. Useful for noting
|
|
64
64
|
input_field_error_class :: The CSS class to use for input fields with errors. Can be a
|
65
65
|
space separated string for multiple CSS classes.
|
66
66
|
input_field_error_message_class :: The CSS class to use for error messages. Can be a
|
67
|
-
|
67
|
+
space separated string for multiple CSS classes.
|
68
68
|
invalid_field_error_status :: The response status to use for invalid field
|
69
69
|
value errors, 422 by default.
|
70
70
|
invalid_key_error_status :: The response status to use for invalid key codes,
|
data/doc/jwt_cors.rdoc
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
= Documentation for JWT CORS Feature
|
2
|
+
|
3
|
+
The jwt_cors feature adds support for Cross-Origin Resource Sharing
|
4
|
+
to Rodauth's JSON API.
|
5
|
+
|
6
|
+
When this feature is used, CORS requests are handled. This includes
|
7
|
+
CORS preflight requests, which are required since Rodauth's JSON API
|
8
|
+
uses the application/json request content type.
|
9
|
+
|
10
|
+
This feature depends on the jwt feature.
|
11
|
+
|
12
|
+
== Auth Value Methods
|
13
|
+
|
14
|
+
jwt_cors_allow_origin :: Which origins are allowed to perform CORS requests. The default is +false+. This can be a String, Array of Strings, Regexp, or +true+ to allow CORS requests from any domain.
|
15
|
+
jwt_cors_allow_methods :: For allowed CORS-preflight requests, the value returned in the Access-Control-Allow-Methods header (default: 'POST'). This specifies which methods are allowed in CORS requests.
|
16
|
+
jwt_cors_allow_headers :: For allowed CORS-preflight requests, the value returned in the Access-Control-Allow-Headers header (default: 'Content-Type, Authorization, Accept'). This specifies which headers can be included in CORS requests.
|
17
|
+
jwt_cors_expose_headers :: For allowed CORS requests, the value returned in the Access-Control-Expose-Headers header (default: 'Authorization'). This specifies which headers the browser is allowed to access from a response to a CORS request.
|
18
|
+
jwt_cors_max_age :: For allowed CORS-preflight requests, the value returned in the Access-Control-Max-Age header (default: 86400). This specifies how long before the information returned should be considered stale and another CORS preflight request made.
|
19
|
+
|
20
|
+
== Auth Methods
|
21
|
+
|
22
|
+
jwt_cors_allow? :: Whether the request should be allowed. This is called for all requests for a Rodauth route that include an Origin header. It should return true or false for whether to specially handle the cross-origin request. By default, uses the +jwt_cors_allow_origin+ setting to check the origin.
|
23
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A jwt_cors feature has been added, handling Cross-Origin Resource
|
4
|
+
Sharing when using the jwt feature, including supporting CORS
|
5
|
+
preflight requests.
|
6
|
+
|
7
|
+
= Other Improvements
|
8
|
+
|
9
|
+
* Mail templates that include links (e.g. for verifying accounts),
|
10
|
+
now add a space after the link and before the newline, fixing
|
11
|
+
issues with some web mail providers that have broken auto-linkers.
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:jwt_cors, :JwtCors) do
|
5
|
+
depends :jwt
|
6
|
+
|
7
|
+
auth_value_method :jwt_cors_allow_origin, false
|
8
|
+
auth_value_method :jwt_cors_allow_methods, 'POST'
|
9
|
+
auth_value_method :jwt_cors_allow_headers, 'Content-Type, Authorization, Accept'
|
10
|
+
auth_value_method :jwt_cors_expose_headers, 'Authorization'
|
11
|
+
auth_value_method :jwt_cors_max_age, 86400
|
12
|
+
|
13
|
+
auth_methods(:jwt_cors_allow?)
|
14
|
+
|
15
|
+
def jwt_cors_allow?
|
16
|
+
if origin = request.env['HTTP_ORIGIN']
|
17
|
+
case allowed = jwt_cors_allow_origin
|
18
|
+
when String
|
19
|
+
timing_safe_eql?(origin, allowed)
|
20
|
+
when Array
|
21
|
+
allowed.any?{|s| timing_safe_eql?(origin, s)}
|
22
|
+
when Regexp
|
23
|
+
allowed =~ origin
|
24
|
+
when true
|
25
|
+
true
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def before_rodauth
|
35
|
+
if (origin = request.env['HTTP_ORIGIN']) && jwt_cors_allow?
|
36
|
+
response['Access-Control-Allow-Origin'] = origin
|
37
|
+
|
38
|
+
# Handle CORS preflight request
|
39
|
+
if request.request_method == 'OPTIONS'
|
40
|
+
response['Access-Control-Allow-Methods'] = jwt_cors_allow_methods
|
41
|
+
response['Access-Control-Allow-Headers'] = jwt_cors_allow_headers
|
42
|
+
response['Access-Control-Max-Age'] = jwt_cors_max_age.to_s
|
43
|
+
response.status = 204
|
44
|
+
request.halt(response.finish)
|
45
|
+
end
|
46
|
+
|
47
|
+
response['Access-Control-Expose-Headers'] = jwt_cors_expose_headers
|
48
|
+
end
|
49
|
+
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/rodauth/version.rb
CHANGED
@@ -18,7 +18,7 @@ describe 'Rodauth disallow common passwords feature' do
|
|
18
18
|
visit '/change-password'
|
19
19
|
|
20
20
|
bad_password_file = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'dict', 'top-10_000-passwords.txt')
|
21
|
-
File.read(bad_password_file).split.shuffle.take(5).each do |pass|
|
21
|
+
(File.read(bad_password_file).split.shuffle - ['0123456789']).take(5).each do |pass|
|
22
22
|
fill_in 'New Password', :with=>pass
|
23
23
|
fill_in 'Confirm Password', :with=>pass
|
24
24
|
click_button 'Change Password'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe 'Rodauth jwt_cors feature' do
|
4
|
+
it "should support CORS logins if allowed" do
|
5
|
+
origin = false
|
6
|
+
rodauth do
|
7
|
+
enable :login, :jwt_cors
|
8
|
+
jwt_secret '1'
|
9
|
+
json_response_success_key 'success'
|
10
|
+
jwt_cors_allow_origin{origin}
|
11
|
+
end
|
12
|
+
roda(:csrf=>false, :json=>true) do |r|
|
13
|
+
r.rodauth
|
14
|
+
rodauth.require_authentication
|
15
|
+
response['Content-Type'] = 'application/json'
|
16
|
+
'1'
|
17
|
+
end
|
18
|
+
|
19
|
+
# CORS Preflight Request
|
20
|
+
preflight_request = {
|
21
|
+
:method=>'OPTIONS',
|
22
|
+
:headers=>{
|
23
|
+
"HTTP_ACCESS_CONTROL_REQUEST_METHOD"=>"POST",
|
24
|
+
"HTTP_ORIGIN"=>"https://foo.example.com",
|
25
|
+
"HTTP_ACCESS_CONTROL_REQUEST_HEADERS"=>"content-type",
|
26
|
+
"CONTENT_TYPE"=>' application/json'
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
res = json_request("/login", preflight_request.dup)
|
31
|
+
res.must_equal [405, ["{\"error\":\"non-POST method used in JSON API\"}"]]
|
32
|
+
|
33
|
+
origin = Object.new
|
34
|
+
res = json_request("/login", preflight_request.dup)
|
35
|
+
res.must_equal [405, ["{\"error\":\"non-POST method used in JSON API\"}"]]
|
36
|
+
|
37
|
+
["https://foo.example.com", ["https://foo.example.com"], %r{https://foo.example.com}, true].each do |orig|
|
38
|
+
origin = orig
|
39
|
+
|
40
|
+
res = json_request("/login", preflight_request.merge(:include_headers=>true))
|
41
|
+
res[0].must_equal 204
|
42
|
+
res[1]['Access-Control-Allow-Origin'].must_equal "https://foo.example.com"
|
43
|
+
res[1]['Access-Control-Allow-Methods'].must_equal "POST"
|
44
|
+
res[1]['Access-Control-Allow-Headers'].must_equal "Content-Type, Authorization, Accept"
|
45
|
+
res[1]['Access-Control-Max-Age'].must_equal "86400"
|
46
|
+
res[2].must_equal []
|
47
|
+
|
48
|
+
res = json_request("/login", :login=>'foo@example.com', :password=>'0123456789', :headers=>{"HTTP_ORIGIN"=>"https://foo.example.com"}, :include_headers=>true)
|
49
|
+
res[0].must_equal 200
|
50
|
+
res[1]['Access-Control-Allow-Origin'].must_equal "https://foo.example.com"
|
51
|
+
res[1]['Access-Control-Expose-Headers'].must_equal "Authorization"
|
52
|
+
res[2].must_equal("success"=>"You have been logged in")
|
53
|
+
|
54
|
+
json_request("/foo").must_equal [200, 1]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -33,7 +33,7 @@ require 'securerandom'
|
|
33
33
|
|
34
34
|
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
35
35
|
gem 'minitest'
|
36
|
-
require 'minitest/autorun'
|
36
|
+
require 'minitest/global_expectations/autorun'
|
37
37
|
require 'minitest/hooks/default'
|
38
38
|
|
39
39
|
require 'roda'
|
@@ -198,7 +198,7 @@ class Minitest::HooksSpec
|
|
198
198
|
msgs.length.must_equal 1
|
199
199
|
msgs.first.to.first.must_equal to
|
200
200
|
|
201
|
-
link = msgs.first.body.to_s[regexp]
|
201
|
+
link = msgs.first.body.to_s.gsub(/ $/, '')[regexp]
|
202
202
|
msgs.clear
|
203
203
|
link.must_be_kind_of(String)
|
204
204
|
link
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Someone has requested a login link for the account with this email
|
2
2
|
address. If you did not request a login link, please ignore this
|
3
3
|
message. If you requested a login link, please go to
|
4
|
-
#{rodauth.email_auth_email_link}
|
4
|
+
#{rodauth.email_auth_email_link}
|
5
5
|
to login to this account.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Someone has requested a password reset for the account with this email
|
2
2
|
address. If you did not request a password reset, please ignore this
|
3
3
|
message. If you requested a password reset, please go to
|
4
|
-
#{rodauth.reset_password_email_link}
|
4
|
+
#{rodauth.reset_password_email_link}
|
5
5
|
to reset the password for the account.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Someone has requested a that the account with this email be unlocked.
|
2
2
|
If you did not request the unlocking of this account, please ignore this
|
3
3
|
message. If you requested the unlocking of this account, please go to
|
4
|
-
#{rodauth.unlock_account_email_link}
|
4
|
+
#{rodauth.unlock_account_email_link}
|
5
5
|
to unlock this account.
|
@@ -1,9 +1,10 @@
|
|
1
1
|
Someone with an account has requested their login be changed to this email address:
|
2
2
|
|
3
3
|
Old Login: #{rodauth.verify_login_change_old_login}
|
4
|
+
|
4
5
|
New Login: #{rodauth.verify_login_change_new_login}
|
5
6
|
|
6
7
|
If you did not request this login change, please ignore this message. If you
|
7
8
|
requested this login change, please go to
|
8
|
-
#{rodauth.verify_login_change_email_link}
|
9
|
+
#{rodauth.verify_login_change_email_link}
|
9
10
|
to verify the login change.
|
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.22.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: 2019-
|
11
|
+
date: 2019-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 5.0.0
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: minitest-global_expectations
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: minitest-hooks
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -232,6 +246,7 @@ extra_rdoc_files:
|
|
232
246
|
- doc/jwt_refresh.rdoc
|
233
247
|
- doc/verify_account_grace_period.rdoc
|
234
248
|
- doc/verify_login_change.rdoc
|
249
|
+
- doc/jwt_cors.rdoc
|
235
250
|
- doc/release_notes/1.17.0.txt
|
236
251
|
- doc/release_notes/1.0.0.txt
|
237
252
|
- doc/release_notes/1.1.0.txt
|
@@ -254,6 +269,7 @@ extra_rdoc_files:
|
|
254
269
|
- doc/release_notes/1.19.0.txt
|
255
270
|
- doc/release_notes/1.20.0.txt
|
256
271
|
- doc/release_notes/1.21.0.txt
|
272
|
+
- doc/release_notes/1.22.0.txt
|
257
273
|
files:
|
258
274
|
- CHANGELOG
|
259
275
|
- MIT-LICENSE
|
@@ -275,6 +291,7 @@ files:
|
|
275
291
|
- doc/http_basic_auth.rdoc
|
276
292
|
- doc/internals.rdoc
|
277
293
|
- doc/jwt.rdoc
|
294
|
+
- doc/jwt_cors.rdoc
|
278
295
|
- doc/jwt_refresh.rdoc
|
279
296
|
- doc/lockout.rdoc
|
280
297
|
- doc/login.rdoc
|
@@ -300,6 +317,7 @@ files:
|
|
300
317
|
- doc/release_notes/1.2.0.txt
|
301
318
|
- doc/release_notes/1.20.0.txt
|
302
319
|
- doc/release_notes/1.21.0.txt
|
320
|
+
- doc/release_notes/1.22.0.txt
|
303
321
|
- doc/release_notes/1.3.0.txt
|
304
322
|
- doc/release_notes/1.4.0.txt
|
305
323
|
- doc/release_notes/1.5.0.txt
|
@@ -334,6 +352,7 @@ files:
|
|
334
352
|
- lib/rodauth/features/email_base.rb
|
335
353
|
- lib/rodauth/features/http_basic_auth.rb
|
336
354
|
- lib/rodauth/features/jwt.rb
|
355
|
+
- lib/rodauth/features/jwt_cors.rb
|
337
356
|
- lib/rodauth/features/jwt_refresh.rb
|
338
357
|
- lib/rodauth/features/lockout.rb
|
339
358
|
- lib/rodauth/features/login.rb
|
@@ -369,6 +388,7 @@ files:
|
|
369
388
|
- spec/disallow_password_reuse_spec.rb
|
370
389
|
- spec/email_auth_spec.rb
|
371
390
|
- spec/http_basic_auth_spec.rb
|
391
|
+
- spec/jwt_cors_spec.rb
|
372
392
|
- spec/jwt_refresh_spec.rb
|
373
393
|
- spec/jwt_spec.rb
|
374
394
|
- spec/lockout_spec.rb
|