rodauth-rails 1.14.1 → 1.15.1
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/README.md +48 -46
- data/lib/generators/rodauth/install_generator.rb +7 -23
- data/lib/generators/rodauth/mailer/email_auth.erb +6 -0
- data/lib/generators/rodauth/mailer/otp_disabled.erb +6 -0
- data/lib/generators/rodauth/mailer/otp_locked_out.erb +6 -0
- data/lib/generators/rodauth/mailer/otp_setup.erb +6 -0
- data/lib/generators/rodauth/mailer/otp_unlock_failed.erb +6 -0
- data/lib/generators/rodauth/mailer/otp_unlocked.erb +6 -0
- data/lib/generators/rodauth/mailer/password_changed.erb +6 -0
- data/lib/generators/rodauth/mailer/reset_password.erb +6 -0
- data/lib/generators/rodauth/mailer/reset_password_notify.erb +6 -0
- data/lib/generators/rodauth/mailer/unlock_account.erb +6 -0
- data/lib/generators/rodauth/mailer/verify_account.erb +6 -0
- data/lib/generators/rodauth/mailer/verify_login_change.erb +7 -0
- data/lib/generators/rodauth/mailer/webauthn_authenticator_added.erb +6 -0
- data/lib/generators/rodauth/mailer/webauthn_authenticator_removed.erb +6 -0
- data/lib/generators/rodauth/mailer_generator.rb +126 -0
- data/lib/generators/rodauth/migration/active_record/audit_logging.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/jwt_refresh.erb +0 -1
- data/lib/generators/rodauth/migration/active_record/otp_unlock.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/audit_logging.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/jwt_refresh.erb +1 -1
- data/lib/generators/rodauth/migration/sequel/otp_unlock.erb +6 -0
- data/lib/generators/rodauth/migration_generator.rb +4 -3
- data/lib/generators/rodauth/templates/INSTRUCTIONS +17 -38
- data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb.tt +4 -50
- data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb.tt +5 -29
- data/lib/generators/rodauth/templates/app/models/account.rb.tt +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_unlock.html.erb +21 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_unlock_not_available.html.erb +5 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock.html.erb +22 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock_not_available.html.erb +14 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb +1 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +1 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_disabled.text.erb +2 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_locked_out.text.erb +9 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_setup.text.erb +2 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlock_failed.text.erb +8 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlocked.text.erb +2 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_added.text.erb +3 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_removed.text.erb +3 -0
- data/lib/generators/rodauth/views_generator.rb +2 -1
- data/lib/rodauth/rails/feature/base.rb +2 -1
- data/lib/rodauth/rails/feature/instrumentation.rb +23 -7
- data/lib/rodauth/rails/feature/internal_request.rb +16 -6
- data/lib/rodauth/rails/version.rb +1 -1
- data/rodauth-rails.gemspec +4 -4
- metadata +35 -8
- data/CHANGELOG.md +0 -570
| @@ -1,61 +1,15 @@ | |
| 1 1 | 
             
            class RodauthMailer < ApplicationMailer
         | 
| 2 2 | 
             
              default to: -> { @rodauth.email_to }, from: -> { @rodauth.email_from }
         | 
| 3 3 |  | 
| 4 | 
            -
             | 
| 5 | 
            -
                @rodauth = rodauth(name, account_id) { @verify_account_key_value = key }
         | 
| 6 | 
            -
                @account = @rodauth.rails_account
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                mail subject: @rodauth.email_subject_prefix + @rodauth.verify_account_email_subject
         | 
| 9 | 
            -
              end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
              def reset_password(name, account_id, key)
         | 
| 12 | 
            -
                @rodauth = rodauth(name, account_id) { @reset_password_key_value = key }
         | 
| 13 | 
            -
                @account = @rodauth.rails_account
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                mail subject: @rodauth.email_subject_prefix + @rodauth.reset_password_email_subject
         | 
| 16 | 
            -
              end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
              def verify_login_change(name, account_id, key)
         | 
| 19 | 
            -
                @rodauth = rodauth(name, account_id) { @verify_login_change_key_value = key }
         | 
| 20 | 
            -
                @account = @rodauth.rails_account
         | 
| 21 | 
            -
                @new_email = @account.login_change_key.login
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                mail to: @new_email, subject: @rodauth.email_subject_prefix + @rodauth.verify_login_change_email_subject
         | 
| 24 | 
            -
              end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
              def password_changed(name, account_id)
         | 
| 27 | 
            -
                @rodauth = rodauth(name, account_id)
         | 
| 28 | 
            -
                @account = @rodauth.rails_account
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                mail subject: @rodauth.email_subject_prefix + @rodauth.password_changed_email_subject
         | 
| 31 | 
            -
              end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
              # def reset_password_notify(name, account_id)
         | 
| 34 | 
            -
              #   @rodauth = rodauth(name, account_id)
         | 
| 35 | 
            -
              #   @account = @rodauth.rails_account
         | 
| 36 | 
            -
             | 
| 37 | 
            -
              #   mail subject: @rodauth.email_subject_prefix + @rodauth.reset_password_notify_email_subject
         | 
| 38 | 
            -
              # end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              # def email_auth(name, account_id, key)
         | 
| 41 | 
            -
              #   @rodauth = rodauth(name, account_id) { @email_auth_key_value = key }
         | 
| 42 | 
            -
              #   @account = @rodauth.rails_account
         | 
| 43 | 
            -
             | 
| 44 | 
            -
              #   mail subject: @rodauth.email_subject_prefix + @rodauth.email_auth_email_subject
         | 
| 45 | 
            -
              # end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
              # def unlock_account(name, account_id, key)
         | 
| 48 | 
            -
              #   @rodauth = rodauth(name, account_id) { @unlock_account_key_value = key }
         | 
| 49 | 
            -
              #   @account = @rodauth.rails_account
         | 
| 50 | 
            -
             | 
| 51 | 
            -
              #   mail subject: @rodauth.email_subject_prefix + @rodauth.unlock_account_email_subject
         | 
| 52 | 
            -
              # end
         | 
| 4 | 
            +
            <%= mailer_content -%>
         | 
| 53 5 |  | 
| 54 6 | 
             
              private
         | 
| 55 7 |  | 
| 8 | 
            +
              # Default URL options are inherited from Action Mailer, but you can override them
         | 
| 9 | 
            +
              # ad-hoc by modifying the `rodauth.rails_url_options` hash.
         | 
| 56 10 | 
             
              def rodauth(name, account_id, &block)
         | 
| 57 11 | 
             
                instance = RodauthApp.rodauth(name).allocate
         | 
| 58 | 
            -
                instance. | 
| 12 | 
            +
                instance.account_from_id(account_id)
         | 
| 59 13 | 
             
                instance.instance_eval(&block) if block
         | 
| 60 14 | 
             
                instance
         | 
| 61 15 | 
             
              end
         | 
| @@ -5,23 +5,24 @@ class RodauthMain < Rodauth::Rails::Auth | |
| 5 5 | 
             
                # List of authentication features that are loaded.
         | 
| 6 6 | 
             
                enable :create_account, :verify_account, :verify_account_grace_period,
         | 
| 7 7 | 
             
                  :login, :logout<%= ", :remember" unless jwt? %><%= ", :json" if json? %><%= ", :jwt" if jwt? %>,
         | 
| 8 | 
            -
                  :reset_password, :change_password, : | 
| 9 | 
            -
                  : | 
| 8 | 
            +
                  :reset_password, :change_password, :change_login, :verify_login_change,
         | 
| 9 | 
            +
                  :close_account<%= ", :argon2" if argon2? %>
         | 
| 10 10 |  | 
| 11 11 | 
             
                # See the Rodauth documentation for the list of available config options:
         | 
| 12 12 | 
             
                # http://rodauth.jeremyevans.net/documentation.html
         | 
| 13 13 |  | 
| 14 14 | 
             
                # ==> General
         | 
| 15 | 
            -
            <% if  | 
| 15 | 
            +
            <% if activerecord? && !sequel? -%>
         | 
| 16 16 | 
             
                # Initialize Sequel and have it reuse Active Record's database connection.
         | 
| 17 17 | 
             
            <% if RUBY_ENGINE == "jruby" -%>
         | 
| 18 18 | 
             
                db Sequel.connect("jdbc:<%= sequel_adapter %>://", extensions: :activerecord_connection, keep_reference: false)
         | 
| 19 19 | 
             
            <% else -%>
         | 
| 20 20 | 
             
                db Sequel.<%= sequel_adapter %>(extensions: :activerecord_connection, keep_reference: false)
         | 
| 21 21 | 
             
            <% end -%>
         | 
| 22 | 
            -
             | 
| 22 | 
            +
            <% if activerecord? -%>
         | 
| 23 23 | 
             
                # Avoid DB query that checks accounts table schema at boot time.
         | 
| 24 24 | 
             
                convert_token_id_to_integer? { <%= table_prefix.camelize %>.columns_hash["id"].type == :integer }
         | 
| 25 | 
            +
            <% end -%>
         | 
| 25 26 |  | 
| 26 27 | 
             
            <% end -%>
         | 
| 27 28 | 
             
                # Change prefix of table and foreign key column names from default "account"
         | 
| @@ -107,28 +108,6 @@ class RodauthMain < Rodauth::Rails::Auth | |
| 107 108 |  | 
| 108 109 | 
             
            <% if defined?(ActionMailer) -%>
         | 
| 109 110 | 
             
                # ==> Emails
         | 
| 110 | 
            -
                # Use a custom mailer for delivering authentication emails.
         | 
| 111 | 
            -
                create_reset_password_email do
         | 
| 112 | 
            -
                  RodauthMailer.reset_password(self.class.configuration_name, account_id, reset_password_key_value)
         | 
| 113 | 
            -
                end
         | 
| 114 | 
            -
                create_verify_account_email do
         | 
| 115 | 
            -
                  RodauthMailer.verify_account(self.class.configuration_name, account_id, verify_account_key_value)
         | 
| 116 | 
            -
                end
         | 
| 117 | 
            -
                create_verify_login_change_email do |_login|
         | 
| 118 | 
            -
                  RodauthMailer.verify_login_change(self.class.configuration_name, account_id, verify_login_change_key_value)
         | 
| 119 | 
            -
                end
         | 
| 120 | 
            -
                create_password_changed_email do
         | 
| 121 | 
            -
                  RodauthMailer.password_changed(self.class.configuration_name, account_id)
         | 
| 122 | 
            -
                end
         | 
| 123 | 
            -
                # create_reset_password_notify_email do
         | 
| 124 | 
            -
                #   RodauthMailer.reset_password_notify(self.class.configuration_name, account_id)
         | 
| 125 | 
            -
                # end
         | 
| 126 | 
            -
                # create_email_auth_email do
         | 
| 127 | 
            -
                #   RodauthMailer.email_auth(self.class.configuration_name, account_id, email_auth_key_value)
         | 
| 128 | 
            -
                # end
         | 
| 129 | 
            -
                # create_unlock_account_email do
         | 
| 130 | 
            -
                #   RodauthMailer.unlock_account(self.class.configuration_name, account_id, unlock_account_key_value)
         | 
| 131 | 
            -
                # end
         | 
| 132 111 | 
             
                send_email do |email|
         | 
| 133 112 | 
             
                  # queue email delivery on the mailer after the transaction commits
         | 
| 134 113 | 
             
                  db.after_commit { email.deliver_later }
         | 
| @@ -214,9 +193,6 @@ class RodauthMain < Rodauth::Rails::Auth | |
| 214 193 |  | 
| 215 194 | 
             
                # Redirect to login page after password reset.
         | 
| 216 195 | 
             
                reset_password_redirect { login_path }
         | 
| 217 | 
            -
             | 
| 218 | 
            -
                # Ensure requiring login follows login route changes.
         | 
| 219 | 
            -
                require_login_redirect { login_path }
         | 
| 220 196 | 
             
            <% end -%>
         | 
| 221 197 |  | 
| 222 198 | 
             
                # ==> Deadlines
         | 
| @@ -1,8 +1,8 @@ | |
| 1 | 
            -
            <% if  | 
| 1 | 
            +
            <% if activerecord? -%>
         | 
| 2 2 | 
             
            class <%= table_prefix.camelize %> < ApplicationRecord
         | 
| 3 3 | 
             
              include Rodauth::Rails.model
         | 
| 4 4 | 
             
            <% if ActiveRecord.version >= Gem::Version.new("7.0") -%>
         | 
| 5 | 
            -
              enum :status, unverified: 1, verified: 2, closed: 3
         | 
| 5 | 
            +
              enum :status, { unverified: 1, verified: 2, closed: 3 }
         | 
| 6 6 | 
             
            <% else -%>
         | 
| 7 7 | 
             
              enum status: { unverified: 1, verified: 2, closed: 3 }
         | 
| 8 8 | 
             
            <% end -%>
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            <%= form_with url: rodauth.otp_unlock_path, method: :post, data: { turbo: false } do |form| %>
         | 
| 2 | 
            +
              <p><%= rodauth.otp_unlock_consecutive_successes_label %>: <%= rodauth.otp_unlock_num_successes %></p>
         | 
| 3 | 
            +
              <p><%= rodauth.otp_unlock_required_consecutive_successes_label %>: <%= rodauth.otp_unlock_auths_required %></p>
         | 
| 4 | 
            +
              <p><%= rodauth.otp_unlock_next_auth_deadline_label %>: <%= rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format) %></p>
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              <div class="form-group mb-3">
         | 
| 7 | 
            +
                <%= form.label "otp-auth-code", rodauth.otp_auth_label, class: "form-label" %>
         | 
| 8 | 
            +
                <div class="row">
         | 
| 9 | 
            +
                  <div class="col-sm-3">
         | 
| 10 | 
            +
                    <%= form.text_field rodauth.otp_auth_param, value: "", id: "otp-auth-code", autocomplete: "off", inputmode: "numeric", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.otp_auth_param)}", aria: ({ invalid: true, describedby: "otp-auth-code_error_message" } if rodauth.field_error(rodauth.otp_auth_param)) %>
         | 
| 11 | 
            +
                    <%= content_tag(:span, rodauth.field_error(rodauth.otp_auth_param), class: "invalid-feedback", id: "otp-auth-code_error_message") if rodauth.field_error(rodauth.otp_auth_param) %>
         | 
| 12 | 
            +
                  </div>
         | 
| 13 | 
            +
                </div>
         | 
| 14 | 
            +
              </div>
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              <div class="form-group mb-3">
         | 
| 17 | 
            +
                <%= form.submit rodauth.otp_unlock_button, class: "btn btn-primary" %>
         | 
| 18 | 
            +
              </div>
         | 
| 19 | 
            +
            <% end %>
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            <%== rodauth.otp_unlock_form_footer %>
         | 
| @@ -0,0 +1,5 @@ | |
| 1 | 
            +
            <p><%= rodauth.otp_unlock_consecutive_successes_label %>: <%= rodauth.otp_unlock_num_successes %></p>
         | 
| 2 | 
            +
            <p><%= rodauth.otp_unlock_required_consecutive_successes_label %>: <%= rodauth.otp_unlock_auths_required %></p>
         | 
| 3 | 
            +
            <p><%= rodauth.otp_unlock_next_auth_attempt_label %>: <%= rodauth.otp_unlock_next_auth_attempt_after.strftime(rodauth.strftime_format) %></p>
         | 
| 4 | 
            +
            <p><%= rodauth.otp_unlock_next_auth_attempt_refresh_label %></p>
         | 
| 5 | 
            +
            <%== rodauth.otp_unlock_refresh_tag %>
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            <%= form_with url: rodauth.otp_unlock_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
         | 
| 2 | 
            +
              <dl class="mb-6 text-sm space-y-2">
         | 
| 3 | 
            +
                <dt class="font-semibold"><%= rodauth.otp_unlock_consecutive_successes_label %>:</dt>
         | 
| 4 | 
            +
                <dd><%= rodauth.otp_unlock_num_successes %></dd>
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                <dt class="font-semibold"><%= rodauth.otp_unlock_required_consecutive_successes_label %>:</dt>
         | 
| 7 | 
            +
                <dd><%= rodauth.otp_unlock_auths_required %></dd>
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                <dt class="font-semibold"><%= rodauth.otp_unlock_next_auth_deadline_label %>:</dt>
         | 
| 10 | 
            +
                <dd><%= rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format) %></dd>
         | 
| 11 | 
            +
              </dl>
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              <div class="mb-6">
         | 
| 14 | 
            +
                <%= form.label "otp-auth-code", rodauth.otp_auth_label, class: "block text-sm font-semibold" %>
         | 
| 15 | 
            +
                <%= form.text_field rodauth.otp_auth_param, value: "", id: "otp-auth-code", autocomplete: "off", inputmode: "numeric", required: true, class: "mt-2 text-sm w-1/2 px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.otp_auth_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "otp-auth-code_error_message" } if rodauth.field_error(rodauth.otp_auth_param)) %>
         | 
| 16 | 
            +
                <%= content_tag(:span, rodauth.field_error(rodauth.otp_auth_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "otp-auth-code_error_message") if rodauth.field_error(rodauth.otp_auth_param) %>
         | 
| 17 | 
            +
              </div>
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              <%= form.submit rodauth.otp_unlock_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
         | 
| 20 | 
            +
            <% end %>
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            <%== rodauth.otp_unlock_form_footer %>
         | 
    
        data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock_not_available.html.erb
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            <dl class="mb-6 text-sm space-y-2">
         | 
| 2 | 
            +
              <dt class="font-semibold"><%= rodauth.otp_unlock_consecutive_successes_label %>:</dt>
         | 
| 3 | 
            +
              <dd><%= rodauth.otp_unlock_num_successes %></dd>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              <dt class="font-semibold"><%= rodauth.otp_unlock_required_consecutive_successes_label %>:</dt>
         | 
| 6 | 
            +
              <dd><%= rodauth.otp_unlock_auths_required %></dd>
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              <dt class="font-semibold"><%= rodauth.otp_unlock_next_auth_attempt_label %>:</dt>
         | 
| 9 | 
            +
              <dd><%= rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format) %></dd>
         | 
| 10 | 
            +
            </dl>
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            <p class="mt-2"><%= rodauth.otp_unlock_next_auth_attempt_refresh_label %></p>
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            <%== rodauth.otp_unlock_refresh_tag %>
         | 
| @@ -10,6 +10,7 @@ | |
| 10 10 | 
             
              <fieldset class="mb-6">
         | 
| 11 11 | 
             
                <% rodauth.account_webauthn_usage.each do |id, last_use| %>
         | 
| 12 12 | 
             
                  <div class="flex items-center space-x-2">
         | 
| 13 | 
            +
                    <% last_use = last_use.strftime(rodauth.strftime_format) if last_use.is_a?(Time) %>
         | 
| 13 14 | 
             
                    <%= form.radio_button rodauth.webauthn_remove_param, id, id: "webauthn-remove-#{id}", class: "dark:bg-gray-900 dark:border-gray-600 dark:checked:bg-current dark:checked:border-current dark:checked:text-emerald-400 dark:focus:ring-emerald-400 dark:focus:ring-offset-gray-900" %>
         | 
| 14 15 | 
             
                    <%= form.label "webauthn-remove-#{id}", "Last use: #{last_use}", class: "text-sm" %>
         | 
| 15 16 | 
             
                  </div>
         | 
| @@ -10,6 +10,7 @@ | |
| 10 10 | 
             
              <fieldset class="form-group mb-3">
         | 
| 11 11 | 
             
                <% (usage = rodauth.account_webauthn_usage).each do |id, last_use| %>
         | 
| 12 12 | 
             
                  <div class="form-check">
         | 
| 13 | 
            +
                    <% last_use = last_use.strftime(rodauth.strftime_format) if last_use.is_a?(Time) %>
         | 
| 13 14 | 
             
                    <%= form.radio_button rodauth.webauthn_remove_param, id, id: "webauthn-remove-#{id}", class: "form-check-input #{"is-invalid" if rodauth.field_error(rodauth.webauthn_remove_param)}", aria: ({ invalid: true, describedby: "webauthn_remove_error_message" } if rodauth.field_error(rodauth.webauthn_remove_param)) %>
         | 
| 14 15 | 
             
                    <%= form.label "webauthn-remove-#{id}", "Last use: #{last_use}", class: "form-check-label" %>
         | 
| 15 16 | 
             
                    <%= content_tag(:span, rodauth.field_error(rodauth.webauthn_remove_param), class: "invalid-feedback", id: "webauthn_remove_error_message") if rodauth.field_error(rodauth.webauthn_remove_param) && id == usage.keys.last %>
         | 
| @@ -0,0 +1,9 @@ | |
| 1 | 
            +
            TOTP authentication has been locked out on your account due to too many
         | 
| 2 | 
            +
            consecutive authentication failures. You can attempt to unlock TOTP
         | 
| 3 | 
            +
            authentication for your account by consecutively authenticating via
         | 
| 4 | 
            +
            TOTP multiple times.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            If you did not initiate the TOTP authentication failures that
         | 
| 7 | 
            +
            caused TOTP authentication to be locked out, that means someone already
         | 
| 8 | 
            +
            has partial access to your account, but is unable to use TOTP
         | 
| 9 | 
            +
            authentication to fully authenticate themselves.
         | 
| @@ -0,0 +1,8 @@ | |
| 1 | 
            +
            Someone (hopefully you) attempted to unlock TOTP authentication for the
         | 
| 2 | 
            +
            account associated to this email address, but failed as the
         | 
| 3 | 
            +
            authentication code submitted was not correct.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            If you did not initiate the TOTP authentication failure that generated
         | 
| 6 | 
            +
            this email, that means someone already has partial access to your
         | 
| 7 | 
            +
            account, but is unable to use TOTP authentication to fully authenticate
         | 
| 8 | 
            +
            themselves.
         | 
| @@ -37,6 +37,7 @@ module Rodauth | |
| 37 37 | 
             
                      lockout:             %w[unlock_account_request unlock_account],
         | 
| 38 38 | 
             
                      two_factor_base:     %w[two_factor_manage two_factor_auth two_factor_disable],
         | 
| 39 39 | 
             
                      otp:                 %w[otp_setup otp_auth otp_disable],
         | 
| 40 | 
            +
                      otp_unlock:          %w[otp_unlock otp_unlock_not_available],
         | 
| 40 41 | 
             
                      sms_codes:           %w[sms_setup sms_confirm sms_auth sms_request sms_disable],
         | 
| 41 42 | 
             
                      recovery_codes:      %w[recovery_codes add_recovery_codes recovery_auth],
         | 
| 42 43 | 
             
                      webauthn:            %w[webauthn_setup webauthn_auth webauthn_remove],
         | 
| @@ -45,7 +46,7 @@ module Rodauth | |
| 45 46 | 
             
                    }
         | 
| 46 47 |  | 
| 47 48 | 
             
                    def create_views
         | 
| 48 | 
            -
                       | 
| 49 | 
            +
                      return unless validate_features
         | 
| 49 50 |  | 
| 50 51 | 
             
                      views.each do |view|
         | 
| 51 52 | 
             
                        copy_file view_location(view), "app/views/#{directory}/#{view}.html.erb" do |content|
         | 
| @@ -13,6 +13,7 @@ module Rodauth | |
| 13 13 | 
             
                    end
         | 
| 14 14 |  | 
| 15 15 | 
             
                    def rails_account
         | 
| 16 | 
            +
                      @rails_account = nil if account.nil? || @rails_account&.id != account_id
         | 
| 16 17 | 
             
                      @rails_account ||= instantiate_rails_account if account!
         | 
| 17 18 | 
             
                    end
         | 
| 18 19 |  | 
| @@ -59,7 +60,7 @@ module Rodauth | |
| 59 60 |  | 
| 60 61 | 
             
                    def instantiate_rails_account
         | 
| 61 62 | 
             
                      if defined?(ActiveRecord::Base) && rails_account_model < ActiveRecord::Base
         | 
| 62 | 
            -
                        if  | 
| 63 | 
            +
                        if account_id
         | 
| 63 64 | 
             
                          rails_account_model.instantiate(account.stringify_keys)
         | 
| 64 65 | 
             
                        else
         | 
| 65 66 | 
             
                          rails_account_model.new(account)
         | 
| @@ -25,7 +25,7 @@ module Rodauth | |
| 25 25 | 
             
                    def rails_render(*)
         | 
| 26 26 | 
             
                      render_output = nil
         | 
| 27 27 | 
             
                      rails_controller_instance.view_runtime = rails_controller_instance.send(:cleanup_view_runtime) do
         | 
| 28 | 
            -
                         | 
| 28 | 
            +
                        rails_benchmark { render_output = super }
         | 
| 29 29 | 
             
                      end
         | 
| 30 30 | 
             
                      render_output
         | 
| 31 31 | 
             
                    end
         | 
| @@ -49,9 +49,9 @@ module Rodauth | |
| 49 49 | 
             
                      ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
         | 
| 50 50 | 
             
                        result = catch(:halt) { yield }
         | 
| 51 51 |  | 
| 52 | 
            -
                         | 
| 53 | 
            -
                        payload[:response] =  | 
| 54 | 
            -
                        payload[:status] =  | 
| 52 | 
            +
                        rails_response = build_rails_response(result || [404, {}, []])
         | 
| 53 | 
            +
                        payload[:response] = rails_response
         | 
| 54 | 
            +
                        payload[:status] = rails_response.status
         | 
| 55 55 |  | 
| 56 56 | 
             
                        throw :halt, result if result
         | 
| 57 57 | 
             
                      rescue => error
         | 
| @@ -66,13 +66,29 @@ module Rodauth | |
| 66 66 | 
             
                      ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: rails_request) do |payload|
         | 
| 67 67 | 
             
                        result = catch(:halt) { yield }
         | 
| 68 68 |  | 
| 69 | 
            -
                         | 
| 70 | 
            -
                        payload[:status] =  | 
| 71 | 
            -
                        payload[:location] =  | 
| 69 | 
            +
                        rails_response = build_rails_response(result)
         | 
| 70 | 
            +
                        payload[:status] = rails_response.status
         | 
| 71 | 
            +
                        payload[:location] = rails_response.filtered_location
         | 
| 72 72 |  | 
| 73 73 | 
             
                        throw :halt, result
         | 
| 74 74 | 
             
                      end
         | 
| 75 75 | 
             
                    end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    def build_rails_response(args)
         | 
| 78 | 
            +
                      response = ActionDispatch::Response.new(*args)
         | 
| 79 | 
            +
                      response.request = rails_request
         | 
| 80 | 
            +
                      response
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    if ActionPack.version >= Gem::Version.new("8.0.0.beta1")
         | 
| 84 | 
            +
                      def rails_benchmark(&block)
         | 
| 85 | 
            +
                        ActiveSupport::Benchmark.realtime(:float_millisecond, &block)
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
                    else
         | 
| 88 | 
            +
                      def rails_benchmark(&block)
         | 
| 89 | 
            +
                        Benchmark.ms(&block)
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                    end
         | 
| 76 92 | 
             
                  end
         | 
| 77 93 | 
             
                end
         | 
| 78 94 | 
             
              end
         | 
| @@ -4,14 +4,18 @@ module Rodauth | |
| 4 4 | 
             
                  module InternalRequest
         | 
| 5 5 | 
             
                    extend ActiveSupport::Concern
         | 
| 6 6 |  | 
| 7 | 
            +
                    included do
         | 
| 8 | 
            +
                      auth_private_methods :rails_url_options
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 7 11 | 
             
                    def domain
         | 
| 8 | 
            -
                      return super unless missing_host? && rails_url_options
         | 
| 12 | 
            +
                      return super unless missing_host? && rails_url_options!
         | 
| 9 13 |  | 
| 10 14 | 
             
                      rails_url_options.fetch(:host)
         | 
| 11 15 | 
             
                    end
         | 
| 12 16 |  | 
| 13 17 | 
             
                    def base_url
         | 
| 14 | 
            -
                      return super unless missing_host? && domain && rails_url_options
         | 
| 18 | 
            +
                      return super unless missing_host? && domain && rails_url_options!
         | 
| 15 19 |  | 
| 16 20 | 
             
                      scheme = rails_url_options[:protocol] || "http"
         | 
| 17 21 | 
             
                      port = rails_url_options[:port]
         | 
| @@ -21,6 +25,11 @@ module Rodauth | |
| 21 25 | 
             
                      url
         | 
| 22 26 | 
             
                    end
         | 
| 23 27 |  | 
| 28 | 
            +
                    def rails_url_options
         | 
| 29 | 
            +
                      return _rails_url_options if frozen? # handle path_class_methods feature
         | 
| 30 | 
            +
                      @rails_url_options ||= _rails_url_options
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 24 33 | 
             
                    private
         | 
| 25 34 |  | 
| 26 35 | 
             
                    def rails_controller_around
         | 
| @@ -44,11 +53,12 @@ module Rodauth | |
| 44 53 | 
             
                      internal_request? && (request.host.nil? || request.host == INVALID_DOMAIN) || scope.nil?
         | 
| 45 54 | 
             
                    end
         | 
| 46 55 |  | 
| 47 | 
            -
                    def rails_url_options
         | 
| 48 | 
            -
                       | 
| 56 | 
            +
                    def rails_url_options!
         | 
| 57 | 
            +
                      rails_url_options.presence or fail Error, "There is no information to set the URL host from. Please set config.action_mailer.default_url_options in your Rails application, configure #domain and #base_url in your Rodauth configuration, or set #rails_url_options on the Rodauth instance."
         | 
| 58 | 
            +
                    end
         | 
| 49 59 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 60 | 
            +
                    def _rails_url_options
         | 
| 61 | 
            +
                      defined?(ActionMailer) ? ActionMailer::Base.default_url_options.dup : {}
         | 
| 52 62 | 
             
                    end
         | 
| 53 63 | 
             
                  end
         | 
| 54 64 | 
             
                end
         | 
    
        data/rodauth-rails.gemspec
    CHANGED
    
    | @@ -6,18 +6,18 @@ Gem::Specification.new do |spec| | |
| 6 6 | 
             
              spec.authors       = ["Janko Marohnić"]
         | 
| 7 7 | 
             
              spec.email         = ["janko.marohnic@gmail.com"]
         | 
| 8 8 |  | 
| 9 | 
            -
              spec.summary       = %q{Provides Rails integration for Rodauth.}
         | 
| 10 | 
            -
              spec.description   = %q{Provides Rails integration for Rodauth.}
         | 
| 9 | 
            +
              spec.summary       = %q{Provides Rails integration for Rodauth authentication framework.}
         | 
| 10 | 
            +
              spec.description   = %q{Provides Rails integration for Rodauth authentication framework.}
         | 
| 11 11 | 
             
              spec.homepage      = "https://github.com/janko/rodauth-rails"
         | 
| 12 12 | 
             
              spec.license       = "MIT"
         | 
| 13 13 |  | 
| 14 14 | 
             
              spec.required_ruby_version = ">= 2.5"
         | 
| 15 15 |  | 
| 16 | 
            -
              spec.files         = Dir["README.md", "LICENSE.txt", " | 
| 16 | 
            +
              spec.files         = Dir["README.md", "LICENSE.txt", "lib/**/*", "*.gemspec"]
         | 
| 17 17 | 
             
              spec.require_paths = ["lib"]
         | 
| 18 18 |  | 
| 19 19 | 
             
              spec.add_dependency "railties", ">= 5.0", "< 8"
         | 
| 20 | 
            -
              spec.add_dependency "rodauth", "~> 2. | 
| 20 | 
            +
              spec.add_dependency "rodauth", "~> 2.36"
         | 
| 21 21 | 
             
              spec.add_dependency "roda", "~> 3.76"
         | 
| 22 22 | 
             
              spec.add_dependency "sequel-activerecord_connection", "~> 1.1"
         | 
| 23 23 | 
             
              spec.add_dependency "rodauth-model", "~> 0.2"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rodauth-rails
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.15.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Janko Marohnić
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-10-26 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: railties
         | 
| @@ -36,14 +36,14 @@ dependencies: | |
| 36 36 | 
             
                requirements:
         | 
| 37 37 | 
             
                - - "~>"
         | 
| 38 38 | 
             
                  - !ruby/object:Gem::Version
         | 
| 39 | 
            -
                    version: '2. | 
| 39 | 
            +
                    version: '2.36'
         | 
| 40 40 | 
             
              type: :runtime
         | 
| 41 41 | 
             
              prerelease: false
         | 
| 42 42 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 43 43 | 
             
                requirements:
         | 
| 44 44 | 
             
                - - "~>"
         | 
| 45 45 | 
             
                  - !ruby/object:Gem::Version
         | 
| 46 | 
            -
                    version: '2. | 
| 46 | 
            +
                    version: '2.36'
         | 
| 47 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 48 | 
             
              name: roda
         | 
| 49 49 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -170,17 +170,31 @@ dependencies: | |
| 170 170 | 
             
                - - ">="
         | 
| 171 171 | 
             
                  - !ruby/object:Gem::Version
         | 
| 172 172 | 
             
                    version: '0'
         | 
| 173 | 
            -
            description: Provides Rails integration for Rodauth.
         | 
| 173 | 
            +
            description: Provides Rails integration for Rodauth authentication framework.
         | 
| 174 174 | 
             
            email:
         | 
| 175 175 | 
             
            - janko.marohnic@gmail.com
         | 
| 176 176 | 
             
            executables: []
         | 
| 177 177 | 
             
            extensions: []
         | 
| 178 178 | 
             
            extra_rdoc_files: []
         | 
| 179 179 | 
             
            files:
         | 
| 180 | 
            -
            - CHANGELOG.md
         | 
| 181 180 | 
             
            - LICENSE.txt
         | 
| 182 181 | 
             
            - README.md
         | 
| 183 182 | 
             
            - lib/generators/rodauth/install_generator.rb
         | 
| 183 | 
            +
            - lib/generators/rodauth/mailer/email_auth.erb
         | 
| 184 | 
            +
            - lib/generators/rodauth/mailer/otp_disabled.erb
         | 
| 185 | 
            +
            - lib/generators/rodauth/mailer/otp_locked_out.erb
         | 
| 186 | 
            +
            - lib/generators/rodauth/mailer/otp_setup.erb
         | 
| 187 | 
            +
            - lib/generators/rodauth/mailer/otp_unlock_failed.erb
         | 
| 188 | 
            +
            - lib/generators/rodauth/mailer/otp_unlocked.erb
         | 
| 189 | 
            +
            - lib/generators/rodauth/mailer/password_changed.erb
         | 
| 190 | 
            +
            - lib/generators/rodauth/mailer/reset_password.erb
         | 
| 191 | 
            +
            - lib/generators/rodauth/mailer/reset_password_notify.erb
         | 
| 192 | 
            +
            - lib/generators/rodauth/mailer/unlock_account.erb
         | 
| 193 | 
            +
            - lib/generators/rodauth/mailer/verify_account.erb
         | 
| 194 | 
            +
            - lib/generators/rodauth/mailer/verify_login_change.erb
         | 
| 195 | 
            +
            - lib/generators/rodauth/mailer/webauthn_authenticator_added.erb
         | 
| 196 | 
            +
            - lib/generators/rodauth/mailer/webauthn_authenticator_removed.erb
         | 
| 197 | 
            +
            - lib/generators/rodauth/mailer_generator.rb
         | 
| 184 198 | 
             
            - lib/generators/rodauth/migration/active_record/account_expiration.erb
         | 
| 185 199 | 
             
            - lib/generators/rodauth/migration/active_record/active_sessions.erb
         | 
| 186 200 | 
             
            - lib/generators/rodauth/migration/active_record/audit_logging.erb
         | 
| @@ -190,6 +204,7 @@ files: | |
| 190 204 | 
             
            - lib/generators/rodauth/migration/active_record/jwt_refresh.erb
         | 
| 191 205 | 
             
            - lib/generators/rodauth/migration/active_record/lockout.erb
         | 
| 192 206 | 
             
            - lib/generators/rodauth/migration/active_record/otp.erb
         | 
| 207 | 
            +
            - lib/generators/rodauth/migration/active_record/otp_unlock.erb
         | 
| 193 208 | 
             
            - lib/generators/rodauth/migration/active_record/password_expiration.erb
         | 
| 194 209 | 
             
            - lib/generators/rodauth/migration/active_record/recovery_codes.erb
         | 
| 195 210 | 
             
            - lib/generators/rodauth/migration/active_record/remember.erb
         | 
| @@ -208,6 +223,7 @@ files: | |
| 208 223 | 
             
            - lib/generators/rodauth/migration/sequel/jwt_refresh.erb
         | 
| 209 224 | 
             
            - lib/generators/rodauth/migration/sequel/lockout.erb
         | 
| 210 225 | 
             
            - lib/generators/rodauth/migration/sequel/otp.erb
         | 
| 226 | 
            +
            - lib/generators/rodauth/migration/sequel/otp_unlock.erb
         | 
| 211 227 | 
             
            - lib/generators/rodauth/migration/sequel/password_expiration.erb
         | 
| 212 228 | 
             
            - lib/generators/rodauth/migration/sequel/recovery_codes.erb
         | 
| 213 229 | 
             
            - lib/generators/rodauth/migration/sequel/remember.erb
         | 
| @@ -240,6 +256,8 @@ files: | |
| 240 256 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb
         | 
| 241 257 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb
         | 
| 242 258 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb
         | 
| 259 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth/otp_unlock.html.erb
         | 
| 260 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth/otp_unlock_not_available.html.erb
         | 
| 243 261 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb
         | 
| 244 262 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/recovery_codes.html.erb
         | 
| 245 263 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb
         | 
| @@ -266,6 +284,8 @@ files: | |
| 266 284 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_auth.html.erb
         | 
| 267 285 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_disable.html.erb
         | 
| 268 286 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_setup.html.erb
         | 
| 287 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock.html.erb
         | 
| 288 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock_not_available.html.erb
         | 
| 269 289 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_auth.html.erb
         | 
| 270 290 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_codes.html.erb
         | 
| 271 291 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/tailwind/remember.html.erb
         | 
| @@ -301,12 +321,19 @@ files: | |
| 301 321 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb
         | 
| 302 322 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb
         | 
| 303 323 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb
         | 
| 324 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_disabled.text.erb
         | 
| 325 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_locked_out.text.erb
         | 
| 326 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_setup.text.erb
         | 
| 327 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlock_failed.text.erb
         | 
| 328 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlocked.text.erb
         | 
| 304 329 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/password_changed.text.erb
         | 
| 305 330 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb
         | 
| 306 331 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password_notify.text.erb
         | 
| 307 332 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb
         | 
| 308 333 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb
         | 
| 309 334 | 
             
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb
         | 
| 335 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_added.text.erb
         | 
| 336 | 
            +
            - lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_removed.text.erb
         | 
| 310 337 | 
             
            - lib/generators/rodauth/templates/config/initializers/rodauth.rb.tt
         | 
| 311 338 | 
             
            - lib/generators/rodauth/templates/db/migrate/create_rodauth.rb.tt
         | 
| 312 339 | 
             
            - lib/generators/rodauth/templates/test/fixtures/accounts.yml.tt
         | 
| @@ -352,8 +379,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 352 379 | 
             
                - !ruby/object:Gem::Version
         | 
| 353 380 | 
             
                  version: '0'
         | 
| 354 381 | 
             
            requirements: []
         | 
| 355 | 
            -
            rubygems_version: 3.5. | 
| 382 | 
            +
            rubygems_version: 3.5.11
         | 
| 356 383 | 
             
            signing_key:
         | 
| 357 384 | 
             
            specification_version: 4
         | 
| 358 | 
            -
            summary: Provides Rails integration for Rodauth.
         | 
| 385 | 
            +
            summary: Provides Rails integration for Rodauth authentication framework.
         | 
| 359 386 | 
             
            test_files: []
         |