rodauth 1.21.0 → 1.22.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/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
         
     |