├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── gemfiles
├── Gemfile.rails-5.1
├── Gemfile.rails-5.2
├── Gemfile.rails-6.0
├── Gemfile.rails-6.1
├── Gemfile.rails-7.0
├── Gemfile.rails-7.1
├── Gemfile.rails-7.2
└── Gemfile.rails-8.0
├── lib
├── generators
│ └── rodauth
│ │ ├── install_generator.rb
│ │ ├── mailer
│ │ ├── email_auth.erb
│ │ ├── otp_disabled.erb
│ │ ├── otp_locked_out.erb
│ │ ├── otp_setup.erb
│ │ ├── otp_unlock_failed.erb
│ │ ├── otp_unlocked.erb
│ │ ├── password_changed.erb
│ │ ├── reset_password.erb
│ │ ├── reset_password_notify.erb
│ │ ├── unlock_account.erb
│ │ ├── verify_account.erb
│ │ ├── verify_login_change.erb
│ │ ├── webauthn_authenticator_added.erb
│ │ └── webauthn_authenticator_removed.erb
│ │ ├── mailer_generator.rb
│ │ ├── migration
│ │ ├── active_record
│ │ │ ├── account_expiration.erb
│ │ │ ├── active_sessions.erb
│ │ │ ├── audit_logging.erb
│ │ │ ├── base.erb
│ │ │ ├── disallow_password_reuse.erb
│ │ │ ├── email_auth.erb
│ │ │ ├── jwt_refresh.erb
│ │ │ ├── lockout.erb
│ │ │ ├── otp.erb
│ │ │ ├── otp_unlock.erb
│ │ │ ├── password_expiration.erb
│ │ │ ├── recovery_codes.erb
│ │ │ ├── remember.erb
│ │ │ ├── reset_password.erb
│ │ │ ├── single_session.erb
│ │ │ ├── sms_codes.erb
│ │ │ ├── verify_account.erb
│ │ │ ├── verify_login_change.erb
│ │ │ └── webauthn.erb
│ │ └── sequel
│ │ │ ├── account_expiration.erb
│ │ │ ├── active_sessions.erb
│ │ │ ├── audit_logging.erb
│ │ │ ├── base.erb
│ │ │ ├── disallow_password_reuse.erb
│ │ │ ├── email_auth.erb
│ │ │ ├── jwt_refresh.erb
│ │ │ ├── lockout.erb
│ │ │ ├── otp.erb
│ │ │ ├── otp_unlock.erb
│ │ │ ├── password_expiration.erb
│ │ │ ├── recovery_codes.erb
│ │ │ ├── remember.erb
│ │ │ ├── reset_password.erb
│ │ │ ├── single_session.erb
│ │ │ ├── sms_codes.erb
│ │ │ ├── verify_account.erb
│ │ │ ├── verify_login_change.erb
│ │ │ └── webauthn.erb
│ │ ├── migration_generator.rb
│ │ ├── templates
│ │ ├── INSTRUCTIONS
│ │ ├── app
│ │ │ ├── controllers
│ │ │ │ └── rodauth_controller.rb.tt
│ │ │ ├── mailers
│ │ │ │ └── rodauth_mailer.rb.tt
│ │ │ ├── misc
│ │ │ │ ├── rodauth_app.rb.tt
│ │ │ │ └── rodauth_main.rb.tt
│ │ │ ├── models
│ │ │ │ └── account.rb.tt
│ │ │ └── views
│ │ │ │ ├── rodauth
│ │ │ │ ├── _email_auth_request_form.html.erb
│ │ │ │ ├── _login_form.html.erb
│ │ │ │ ├── _login_form_footer.html.erb
│ │ │ │ ├── add_recovery_codes.html.erb
│ │ │ │ ├── change_login.html.erb
│ │ │ │ ├── change_password.html.erb
│ │ │ │ ├── close_account.html.erb
│ │ │ │ ├── confirm_password.html.erb
│ │ │ │ ├── create_account.html.erb
│ │ │ │ ├── email_auth.html.erb
│ │ │ │ ├── login.html.erb
│ │ │ │ ├── logout.html.erb
│ │ │ │ ├── multi_phase_login.html.erb
│ │ │ │ ├── otp_auth.html.erb
│ │ │ │ ├── otp_disable.html.erb
│ │ │ │ ├── otp_setup.html.erb
│ │ │ │ ├── otp_unlock.html.erb
│ │ │ │ ├── otp_unlock_not_available.html.erb
│ │ │ │ ├── recovery_auth.html.erb
│ │ │ │ ├── recovery_codes.html.erb
│ │ │ │ ├── remember.html.erb
│ │ │ │ ├── reset_password.html.erb
│ │ │ │ ├── reset_password_request.html.erb
│ │ │ │ ├── sms_auth.html.erb
│ │ │ │ ├── sms_confirm.html.erb
│ │ │ │ ├── sms_disable.html.erb
│ │ │ │ ├── sms_request.html.erb
│ │ │ │ ├── sms_setup.html.erb
│ │ │ │ ├── tailwind
│ │ │ │ │ ├── _email_auth_request_form.html.erb
│ │ │ │ │ ├── _login_form.html.erb
│ │ │ │ │ ├── _login_form_footer.html.erb
│ │ │ │ │ ├── add_recovery_codes.html.erb
│ │ │ │ │ ├── change_login.html.erb
│ │ │ │ │ ├── change_password.html.erb
│ │ │ │ │ ├── close_account.html.erb
│ │ │ │ │ ├── confirm_password.html.erb
│ │ │ │ │ ├── create_account.html.erb
│ │ │ │ │ ├── email_auth.html.erb
│ │ │ │ │ ├── login.html.erb
│ │ │ │ │ ├── logout.html.erb
│ │ │ │ │ ├── multi_phase_login.html.erb
│ │ │ │ │ ├── otp_auth.html.erb
│ │ │ │ │ ├── otp_disable.html.erb
│ │ │ │ │ ├── otp_setup.html.erb
│ │ │ │ │ ├── otp_unlock.html.erb
│ │ │ │ │ ├── otp_unlock_not_available.html.erb
│ │ │ │ │ ├── recovery_auth.html.erb
│ │ │ │ │ ├── recovery_codes.html.erb
│ │ │ │ │ ├── remember.html.erb
│ │ │ │ │ ├── reset_password.html.erb
│ │ │ │ │ ├── reset_password_request.html.erb
│ │ │ │ │ ├── sms_auth.html.erb
│ │ │ │ │ ├── sms_confirm.html.erb
│ │ │ │ │ ├── sms_disable.html.erb
│ │ │ │ │ ├── sms_request.html.erb
│ │ │ │ │ ├── sms_setup.html.erb
│ │ │ │ │ ├── two_factor_auth.html.erb
│ │ │ │ │ ├── two_factor_disable.html.erb
│ │ │ │ │ ├── two_factor_manage.html.erb
│ │ │ │ │ ├── unlock_account.html.erb
│ │ │ │ │ ├── unlock_account_request.html.erb
│ │ │ │ │ ├── verify_account.html.erb
│ │ │ │ │ ├── verify_account_resend.html.erb
│ │ │ │ │ ├── verify_login_change.html.erb
│ │ │ │ │ ├── webauthn_auth.html.erb
│ │ │ │ │ ├── webauthn_autofill.html.erb
│ │ │ │ │ ├── webauthn_remove.html.erb
│ │ │ │ │ └── webauthn_setup.html.erb
│ │ │ │ ├── two_factor_auth.html.erb
│ │ │ │ ├── two_factor_disable.html.erb
│ │ │ │ ├── two_factor_manage.html.erb
│ │ │ │ ├── unlock_account.html.erb
│ │ │ │ ├── unlock_account_request.html.erb
│ │ │ │ ├── verify_account.html.erb
│ │ │ │ ├── verify_account_resend.html.erb
│ │ │ │ ├── verify_login_change.html.erb
│ │ │ │ ├── webauthn_auth.html.erb
│ │ │ │ ├── webauthn_autofill.html.erb
│ │ │ │ ├── webauthn_remove.html.erb
│ │ │ │ └── webauthn_setup.html.erb
│ │ │ │ └── rodauth_mailer
│ │ │ │ ├── email_auth.text.erb
│ │ │ │ ├── otp_disabled.text.erb
│ │ │ │ ├── otp_locked_out.text.erb
│ │ │ │ ├── otp_setup.text.erb
│ │ │ │ ├── otp_unlock_failed.text.erb
│ │ │ │ ├── otp_unlocked.text.erb
│ │ │ │ ├── password_changed.text.erb
│ │ │ │ ├── reset_password.text.erb
│ │ │ │ ├── reset_password_notify.text.erb
│ │ │ │ ├── unlock_account.text.erb
│ │ │ │ ├── verify_account.text.erb
│ │ │ │ ├── verify_login_change.text.erb
│ │ │ │ ├── webauthn_authenticator_added.text.erb
│ │ │ │ └── webauthn_authenticator_removed.text.erb
│ │ ├── config
│ │ │ └── initializers
│ │ │ │ └── rodauth.rb.tt
│ │ ├── db
│ │ │ └── migrate
│ │ │ │ └── create_rodauth.rb.tt
│ │ └── test
│ │ │ └── fixtures
│ │ │ └── accounts.yml.tt
│ │ └── views_generator.rb
├── rodauth-rails.rb
└── rodauth
│ ├── rails.rb
│ └── rails
│ ├── app.rb
│ ├── auth.rb
│ ├── controller_methods.rb
│ ├── feature.rb
│ ├── feature
│ ├── base.rb
│ ├── callbacks.rb
│ ├── csrf.rb
│ ├── email.rb
│ ├── instrumentation.rb
│ ├── internal_request.rb
│ └── render.rb
│ ├── mailer.rb
│ ├── middleware.rb
│ ├── railtie.rb
│ ├── tasks.rake
│ ├── tasks
│ └── routes.rb
│ ├── test.rb
│ ├── test
│ └── controller.rb
│ └── version.rb
├── rodauth-rails.gemspec
└── test
├── controllers
└── test_controller_test.rb
├── generators
├── install_generator_test.rb
├── mailer_generator_test.rb
├── migration_generator_test.rb
└── views_generator_test.rb
├── integration
├── assets_test.rb
├── callbacks_test.rb
├── configurations_test.rb
├── constraint_test.rb
├── controller_test.rb
├── csrf_test.rb
├── email_test.rb
├── flash_test.rb
├── headers_test.rb
├── instrumentation_test.rb
├── json_test.rb
├── model_test.rb
├── redirect_test.rb
├── render_test.rb
├── rescue_test.rb
└── session_test.rb
├── internal_request_test.rb
├── model_mixin_test.rb
├── rails_app
├── Rakefile
├── app
│ ├── controllers
│ │ ├── admin
│ │ │ └── rodauth_controller.rb
│ │ ├── application_controller.rb
│ │ ├── rodauth_controller.rb
│ │ └── test_controller.rb
│ ├── misc
│ │ ├── rodauth_admin.rb
│ │ ├── rodauth_app.rb
│ │ └── rodauth_main.rb
│ ├── models
│ │ ├── account.rb
│ │ └── application_record.rb
│ └── views
│ │ ├── layouts
│ │ └── application.html.erb
│ │ ├── rodauth
│ │ ├── _global_logout_field.html.erb
│ │ ├── close_account.html.erb
│ │ ├── reset_password_request.html.erb
│ │ └── verify_account.html.erb
│ │ └── test
│ │ └── template.html.erb
├── config
│ ├── application.rb
│ ├── database.yml
│ ├── environment.rb
│ ├── initializers
│ │ ├── rodauth.rb
│ │ └── sequel.rb
│ └── routes.rb
└── db
│ └── migrate
│ └── 20200411171322_create_rodauth.rb
├── rake_test.rb
├── rodauth_test.rb
└── test_helper.rb
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: janko
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ '**' ]
8 |
9 | permissions:
10 | contents: read
11 |
12 | jobs:
13 | tests:
14 | runs-on: ubuntu-latest
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | ruby: ["ruby-2.6", "ruby-2.7", "ruby-3.0", "ruby-3.1", "ruby-3.2", "ruby-3.3", "ruby-3.4", "jruby-9.4"]
19 | gemfile: ["rails-5.1", "rails-5.2", "rails-6.0", "rails-6.1", "rails-7.0", "rails-7.1", "rails-7.2", "rails-8.0"]
20 | exclude:
21 | - ruby: "ruby-3.4"
22 | gemfile: "rails-5.2"
23 | - ruby: "ruby-3.4"
24 | gemfile: "rails-5.1"
25 | - ruby: "ruby-3.3"
26 | gemfile: "rails-5.2"
27 | - ruby: "ruby-3.3"
28 | gemfile: "rails-5.1"
29 | - ruby: "ruby-3.2"
30 | gemfile: "rails-5.2"
31 | - ruby: "ruby-3.2"
32 | gemfile: "rails-5.1"
33 | - ruby: "ruby-3.1"
34 | gemfile: "rails-8.0"
35 | - ruby: "ruby-3.1"
36 | gemfile: "rails-5.2"
37 | - ruby: "ruby-3.1"
38 | gemfile: "rails-5.1"
39 | - ruby: "ruby-3.0"
40 | gemfile: "rails-8.0"
41 | - ruby: "ruby-3.0"
42 | gemfile: "rails-7.2"
43 | - ruby: "ruby-3.0"
44 | gemfile: "rails-5.2"
45 | - ruby: "ruby-3.0"
46 | gemfile: "rails-5.1"
47 | - ruby: "ruby-2.7"
48 | gemfile: "rails-8.0"
49 | - ruby: "ruby-2.7"
50 | gemfile: "rails-7.2"
51 | - ruby: "jruby-9.4"
52 | gemfile: "rails-8.0"
53 | - ruby: "jruby-9.4"
54 | gemfile: "rails-7.2"
55 | - ruby: "jruby-9.4"
56 | gemfile: "rails-7.1"
57 | - ruby: "jruby-9.4"
58 | gemfile: "rails-5.2"
59 | - ruby: "jruby-9.4"
60 | gemfile: "rails-5.1"
61 | - ruby: "ruby-2.6"
62 | gemfile: "rails-8.0"
63 | - ruby: "ruby-2.6"
64 | gemfile: "rails-7.2"
65 | - ruby: "ruby-2.6"
66 | gemfile: "rails-7.1"
67 | - ruby: "ruby-2.6"
68 | gemfile: "rails-7.0"
69 | env:
70 | BUNDLE_GEMFILE: gemfiles/Gemfile.${{ matrix.gemfile }}
71 |
72 | steps:
73 | - uses: actions/checkout@v4
74 |
75 | - name: Set up Ruby
76 | uses: ruby/setup-ruby@v1
77 | with:
78 | ruby-version: ${{ matrix.ruby }}
79 | bundler-cache: true
80 |
81 | - name: Run tests
82 | run: bundle exec rake test
83 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Gemfile.lock
2 | gemfiles/*.lock
3 | pkg/
4 | database.sqlite3
5 | tmp/
6 | .ruby-version
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | The changelog is tracked in [GitHub releases](https://github.com/janko/rodauth-rails/releases).
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 |
7 | gem "rake", "~> 13.0"
8 | gem "warning"
9 |
10 | gem "rails", "~> 8.0"
11 | gem "turbo-rails", "~> 1.4"
12 | gem "sqlite3", "~> 2.0", platforms: [:mri, :truffleruby]
13 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
14 |
15 | gem "capybara"
16 |
17 | if RUBY_VERSION >= "3.1.0"
18 | # mail gem dependencies on Ruby 3.1+
19 | gem "net-smtp"
20 | gem "net-imap"
21 | gem "net-pop"
22 |
23 | # rake gem dependency on Ruby 3.1+
24 | gem "matrix"
25 | end
26 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-2023 Janko Marohnić
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rake/testtask"
3 |
4 | Rake::TestTask.new(:test) do |t|
5 | t.libs << "test"
6 | t.pattern = "test/**/*_test.rb"
7 | t.warning = true
8 | end
9 |
10 | task default: :test
11 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-5.1:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 | gem "after_commit_everywhere", "~> 1.1"
7 |
8 | gem "rake", "~> 12.0"
9 | gem "warning"
10 |
11 | gem "rails", "~> 5.1.0"
12 | gem "sqlite3", "~> 1.4", platforms: :mri
13 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
14 |
15 | gem "capybara"
16 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-5.2:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 | gem "after_commit_everywhere", "~> 1.1"
7 |
8 | gem "rake", "~> 12.0"
9 | gem "warning"
10 |
11 | gem "rails", "~> 5.2.0"
12 | gem "sqlite3", "~> 1.4", platforms: :mri
13 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
14 |
15 | gem "capybara"
16 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-6.0:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 | gem "after_commit_everywhere", "~> 1.1"
7 |
8 | gem "rake", "~> 12.0"
9 | gem "warning"
10 |
11 | gem "rails", "~> 6.0.0"
12 | gem "turbo-rails", "~> 1.4"
13 | gem "sqlite3", "~> 1.4", platforms: :mri
14 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
15 |
16 | gem "capybara"
17 |
18 | if RUBY_VERSION >= "3.1.0"
19 | # mail gem dependencies on Ruby 3.1+
20 | gem "net-smtp"
21 | gem "net-imap"
22 | gem "net-pop"
23 |
24 | # rake gem dependency on Ruby 3.1+
25 | gem "matrix"
26 | end
27 |
28 | if RUBY_VERSION >= "3.4.0"
29 | gem "mutex_m"
30 | gem "drb"
31 | end
32 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-6.1:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 | gem "after_commit_everywhere", "~> 1.1"
7 |
8 | gem "rake", "~> 12.0"
9 | gem "warning"
10 |
11 | gem "rails", "~> 6.1.0"
12 | gem "turbo-rails", "~> 1.4"
13 | gem "sqlite3", "~> 1.4", platforms: :mri
14 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
15 |
16 | gem "capybara"
17 |
18 | if RUBY_VERSION >= "3.1.0"
19 | # mail gem dependencies on Ruby 3.1+
20 | gem "net-smtp"
21 | gem "net-imap"
22 | gem "net-pop"
23 |
24 | # rake gem dependency on Ruby 3.1+
25 | gem "matrix"
26 | end
27 |
28 | if RUBY_VERSION >= "3.4.0"
29 | gem "mutex_m"
30 | gem "drb"
31 | end
32 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-7.0:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 | gem "after_commit_everywhere", "~> 1.1"
7 |
8 | gem "rake", "~> 12.0"
9 | gem "warning"
10 |
11 | gem "rails", "~> 7.0.0"
12 | gem "turbo-rails", "~> 1.4"
13 | gem "sqlite3", "~> 1.4", platforms: :mri
14 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
15 |
16 | gem "capybara"
17 |
18 | if RUBY_VERSION >= "3.1.0"
19 | # mail gem dependencies on Ruby 3.1+
20 | gem "net-smtp"
21 | gem "net-imap"
22 | gem "net-pop"
23 |
24 | # rake gem dependency on Ruby 3.1+
25 | gem "matrix"
26 | end
27 |
28 | if RUBY_VERSION >= "3.4.0"
29 | gem "mutex_m"
30 | gem "drb"
31 | end
32 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-7.1:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 | gem "after_commit_everywhere", "~> 1.1"
7 |
8 | gem "rake", "~> 12.0"
9 | gem "warning"
10 |
11 | gem "rails", "~> 7.1.0"
12 | gem "turbo-rails", "~> 1.4"
13 | gem "sqlite3", "~> 1.4", platforms: :mri
14 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
15 |
16 | gem "capybara"
17 |
18 | if RUBY_VERSION >= "3.1.0"
19 | # mail gem dependencies on Ruby 3.1+
20 | gem "net-smtp"
21 | gem "net-imap"
22 | gem "net-pop"
23 |
24 | # rake gem dependency on Ruby 3.1+
25 | gem "matrix"
26 | end
27 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-7.2:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 |
7 | gem "rake", "~> 12.0"
8 | gem "warning"
9 |
10 | gem "rails", "~> 7.2.0"
11 | gem "sqlite3", "~> 1.4", platforms: :mri
12 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
13 |
14 | gem "capybara"
15 |
16 | if RUBY_VERSION >= "3.1.0"
17 | # mail gem dependencies on Ruby 3.1+
18 | gem "net-smtp"
19 | gem "net-imap"
20 | gem "net-pop"
21 |
22 | # rake gem dependency on Ruby 3.1+
23 | gem "matrix"
24 | end
25 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-8.0:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec path: ".."
4 |
5 | gem "sequel-activerecord_connection", "~> 2.0"
6 |
7 | gem "rake", "~> 12.0"
8 | gem "warning"
9 |
10 | gem "rails", "~> 8.0.0"
11 | gem "sqlite3", "~> 2.0", platforms: :mri
12 | gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
13 |
14 | gem "capybara"
15 |
16 | if RUBY_VERSION >= "3.1.0"
17 | # mail gem dependencies on Ruby 3.1+
18 | gem "net-smtp"
19 | gem "net-imap"
20 | gem "net-pop"
21 |
22 | # rake gem dependency on Ruby 3.1+
23 | gem "matrix"
24 | end
25 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/email_auth.erb:
--------------------------------------------------------------------------------
1 | def email_auth(name, account_id, key)
2 | @rodauth = rodauth(name, account_id) { @email_auth_key_value = key }
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.email_auth_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/otp_disabled.erb:
--------------------------------------------------------------------------------
1 | def otp_disabled(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.otp_disabled_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/otp_locked_out.erb:
--------------------------------------------------------------------------------
1 | def otp_locked_out(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.otp_locked_out_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/otp_setup.erb:
--------------------------------------------------------------------------------
1 | def otp_setup(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.otp_setup_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/otp_unlock_failed.erb:
--------------------------------------------------------------------------------
1 | def otp_unlock_failed(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.otp_unlock_failed_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/otp_unlocked.erb:
--------------------------------------------------------------------------------
1 | def otp_unlocked(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.otp_unlocked_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/password_changed.erb:
--------------------------------------------------------------------------------
1 | def password_changed(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.password_changed_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/reset_password.erb:
--------------------------------------------------------------------------------
1 | def reset_password(name, account_id, key)
2 | @rodauth = rodauth(name, account_id) { @reset_password_key_value = key }
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.reset_password_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/reset_password_notify.erb:
--------------------------------------------------------------------------------
1 | def reset_password_notify(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.reset_password_notify_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/unlock_account.erb:
--------------------------------------------------------------------------------
1 | def unlock_account(name, account_id, key)
2 | @rodauth = rodauth(name, account_id) { @unlock_account_key_value = key }
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.unlock_account_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/verify_account.erb:
--------------------------------------------------------------------------------
1 | def verify_account(name, account_id, key)
2 | @rodauth = rodauth(name, account_id) { @verify_account_key_value = key }
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.verify_account_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/verify_login_change.erb:
--------------------------------------------------------------------------------
1 | def verify_login_change(name, account_id, key)
2 | @rodauth = rodauth(name, account_id) { @verify_login_change_key_value = key }
3 | @account = @rodauth.rails_account
4 | @new_email = @account.login_change_key.login
5 |
6 | mail to: @new_email, subject: @rodauth.email_subject_prefix + @rodauth.verify_login_change_email_subject
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/webauthn_authenticator_added.erb:
--------------------------------------------------------------------------------
1 | def webauthn_authenticator_added(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.webauthn_authenticator_added_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/mailer/webauthn_authenticator_removed.erb:
--------------------------------------------------------------------------------
1 | def webauthn_authenticator_removed(name, account_id)
2 | @rodauth = rodauth(name, account_id)
3 | @account = @rodauth.rails_account
4 |
5 | mail subject: @rodauth.email_subject_prefix + @rodauth.webauthn_authenticator_removed_email_subject
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/account_expiration.erb:
--------------------------------------------------------------------------------
1 | # Used by the account expiration feature
2 | create_table :<%= table_prefix %>_activity_times, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.datetime :last_activity_at, null: false
6 | t.datetime :last_login_at, null: false
7 | t.datetime :expired_at
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/active_sessions.erb:
--------------------------------------------------------------------------------
1 | # Used by the active sessions feature
2 | create_table :<%= table_prefix %>_active_session_keys, primary_key: [:<%= table_prefix %>_id, :session_id] do |t|
3 | t.references :<%= table_prefix %>, foreign_key: true<%= primary_key_type(:type) %>
4 | t.string :session_id
5 | t.datetime :created_at, null: false, default: -> { "<%= current_timestamp %>" }
6 | t.datetime :last_use, null: false, default: -> { "<%= current_timestamp %>" }
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/audit_logging.erb:
--------------------------------------------------------------------------------
1 | # Used by the audit logging feature
2 | create_table :<%= table_prefix %>_authentication_audit_logs<%= primary_key_type %> do |t|
3 | t.references :<%= table_prefix %>, foreign_key: true, null: false<%= primary_key_type(:type) %>
4 | t.datetime :at, null: false, default: -> { "<%= current_timestamp %>" }
5 | t.text :message, null: false
6 | <% case activerecord_adapter -%>
7 | <% when "postgresql" -%>
8 | t.jsonb :metadata
9 | <% when "sqlite3", "mysql2", "trilogy" -%>
10 | t.json :metadata
11 | <% else -%>
12 | t.string :metadata
13 | <% end -%>
14 | t.index [:<%= table_prefix %>_id, :at]
15 | t.index :at
16 | end
17 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/base.erb:
--------------------------------------------------------------------------------
1 | <% if activerecord_adapter == "postgresql" -%>
2 | enable_extension "citext"
3 |
4 | <% end -%>
5 | create_table :<%= table_prefix.pluralize %><%= primary_key_type %> do |t|
6 | t.integer :status, null: false, default: 1
7 | <% case activerecord_adapter -%>
8 | <% when "postgresql" -%>
9 | t.citext :email, null: false
10 | t.check_constraint "email ~ '^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$'", name: "valid_email"
11 | <% else -%>
12 | t.string :email, null: false
13 | <% end -%>
14 | <% case activerecord_adapter -%>
15 | <% when "postgresql", "sqlite3" -%>
16 | t.index :email, unique: true, where: "status IN (1, 2)"
17 | <% else -%>
18 | t.index :email, unique: true
19 | <% end -%>
20 | t.string :password_hash
21 | end
22 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/disallow_password_reuse.erb:
--------------------------------------------------------------------------------
1 | # Used by the disallow password reuse feature
2 | create_table :<%= table_prefix %>_previous_password_hashes do |t|
3 | t.references :<%= table_prefix %>, foreign_key: true<%= primary_key_type(:type) %>
4 | t.string :password_hash, null: false
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/email_auth.erb:
--------------------------------------------------------------------------------
1 | # Used by the email auth feature
2 | create_table :<%= table_prefix %>_email_auth_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | t.datetime :deadline, null: false
7 | t.datetime :email_last_sent, null: false, default: -> { "<%= current_timestamp %>" }
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/jwt_refresh.erb:
--------------------------------------------------------------------------------
1 | # Used by the jwt refresh feature
2 | create_table :<%= table_prefix %>_jwt_refresh_keys<%= primary_key_type %> do |t|
3 | t.references :<%= table_prefix %>, foreign_key: true, null: false<%= primary_key_type(:type) %>
4 | t.string :key, null: false
5 | t.datetime :deadline, null: false
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/lockout.erb:
--------------------------------------------------------------------------------
1 | # Used by the lockout feature
2 | create_table :<%= table_prefix %>_login_failures, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.integer :number, null: false, default: 1
6 | end
7 | create_table :<%= table_prefix %>_lockouts, id: false do |t|
8 | t.<%= primary_key_type(nil) %> :id, primary_key: true
9 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
10 | t.string :key, null: false
11 | t.datetime :deadline, null: false
12 | t.datetime :email_last_sent
13 | end
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/otp.erb:
--------------------------------------------------------------------------------
1 | # Used by the otp feature
2 | create_table :<%= table_prefix %>_otp_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | t.integer :num_failures, null: false, default: 0
7 | t.datetime :last_use, null: false, default: -> { "<%= current_timestamp %>" }
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/otp_unlock.erb:
--------------------------------------------------------------------------------
1 | # Used by the otp_unlock feature
2 | create_table :<%= table_prefix %>_otp_unlocks, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.integer :num_successes, null: false, default: 1
6 | t.datetime :next_auth_attempt_after, null: false, default: -> { "<%= current_timestamp %>" }
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/password_expiration.erb:
--------------------------------------------------------------------------------
1 | # Used by the password expiration feature
2 | create_table :<%= table_prefix %>_password_change_times, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.datetime :changed_at, null: false, default: -> { "<%= current_timestamp %>" }
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/recovery_codes.erb:
--------------------------------------------------------------------------------
1 | # Used by the recovery codes feature
2 | create_table :<%= table_prefix %>_recovery_codes, primary_key: [:id, :code] do |t|
3 | t.<%= primary_key_type(nil) %> :id
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :code
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/remember.erb:
--------------------------------------------------------------------------------
1 | # Used by the remember me feature
2 | create_table :<%= table_prefix %>_remember_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | t.datetime :deadline, null: false
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/reset_password.erb:
--------------------------------------------------------------------------------
1 | # Used by the password reset feature
2 | create_table :<%= table_prefix %>_password_reset_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | t.datetime :deadline, null: false
7 | t.datetime :email_last_sent, null: false, default: -> { "<%= current_timestamp %>" }
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/single_session.erb:
--------------------------------------------------------------------------------
1 | # Used by the single session feature
2 | create_table :<%= table_prefix %>_session_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/sms_codes.erb:
--------------------------------------------------------------------------------
1 | # Used by the sms codes feature
2 | create_table :<%= table_prefix %>_sms_codes, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :phone_number, null: false
6 | t.integer :num_failures
7 | t.string :code
8 | t.datetime :code_issued_at, null: false, default: -> { "<%= current_timestamp %>" }
9 | end
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/verify_account.erb:
--------------------------------------------------------------------------------
1 | # Used by the account verification feature
2 | create_table :<%= table_prefix %>_verification_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | t.datetime :requested_at, null: false, default: -> { "<%= current_timestamp %>" }
7 | t.datetime :email_last_sent, null: false, default: -> { "<%= current_timestamp %>" }
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/verify_login_change.erb:
--------------------------------------------------------------------------------
1 | # Used by the verify login change feature
2 | create_table :<%= table_prefix %>_login_change_keys, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :key, null: false
6 | t.string :login, null: false
7 | t.datetime :deadline, null: false
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/active_record/webauthn.erb:
--------------------------------------------------------------------------------
1 | # Used by the webauthn feature
2 | create_table :<%= table_prefix %>_webauthn_user_ids, id: false do |t|
3 | t.<%= primary_key_type(nil) %> :id, primary_key: true
4 | t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5 | t.string :webauthn_id, null: false
6 | end
7 | create_table :<%= table_prefix %>_webauthn_keys, primary_key: [:<%= table_prefix %>_id, :webauthn_id] do |t|
8 | t.references :<%= table_prefix %>, foreign_key: true<%= primary_key_type(:type) %>
9 | t.string :webauthn_id
10 | t.string :public_key, null: false
11 | t.integer :sign_count, null: false
12 | t.datetime :last_use, null: false, default: -> { "<%= current_timestamp %>" }
13 | end
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/account_expiration.erb:
--------------------------------------------------------------------------------
1 | # Used by the account expiration feature
2 | create_table :<%= table_prefix %>_activity_times do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | DateTime :last_activity_at, null: false
5 | DateTime :last_login_at, null: false
6 | DateTime :expired_at
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/active_sessions.erb:
--------------------------------------------------------------------------------
1 | # Used by the active sessions feature
2 | create_table :<%= table_prefix %>_active_session_keys do
3 | foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, type: :Bignum
4 | String :session_id
5 | Time :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
6 | Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
7 | primary_key [:<%= table_prefix %>_id, :session_id]
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/audit_logging.erb:
--------------------------------------------------------------------------------
1 | # Used by the audit logging feature
2 | create_table :<%= table_prefix %>_authentication_audit_logs do
3 | primary_key :id, type: :Bignum
4 | foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, null: false, type: :Bignum
5 | DateTime :at, null: false, default: Sequel::CURRENT_TIMESTAMP
6 | String :message, null: false
7 | <% case db.database_type -%>
8 | <% when :postgres -%>
9 | jsonb :metadata
10 | <% when :sqlite, :mysql -%>
11 | json :metadata
12 | <% else -%>
13 | String :metadata
14 | <% end -%>
15 | index [:<%= table_prefix %>_id, :at]
16 | index :at
17 | end
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/base.erb:
--------------------------------------------------------------------------------
1 | <% if db.database_type == :postgres -%>
2 | begin
3 | run "CREATE EXTENSION IF NOT EXISTS citext"
4 | rescue NoMethodError # migration is being reverted
5 | end
6 |
7 | <% end -%>
8 | create_table :<%= table_prefix.pluralize %> do
9 | primary_key :id, type: :Bignum
10 | <% if db.database_type == :postgres -%>
11 | citext :email, null: false
12 | constraint :valid_email, email: /^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
13 | <% else -%>
14 | String :email, null: false
15 | <% end -%>
16 | Integer :status, null: false, default: 1
17 | <% if db.supports_partial_indexes? -%>
18 | index :email, unique: true, where: { status: [1, 2] }
19 | <% else -%>
20 | index :email, unique: true
21 | <% end -%>
22 | String :password_hash
23 | end
24 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/disallow_password_reuse.erb:
--------------------------------------------------------------------------------
1 | # Used by the disallow password reuse feature
2 | create_table :<%= table_prefix %>_previous_password_hashes do
3 | primary_key :id, type: :Bignum
4 | foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, type: :Bignum
5 | String :password_hash, null: false
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/email_auth.erb:
--------------------------------------------------------------------------------
1 | # Used by the email auth feature
2 | create_table :<%= table_prefix %>_email_auth_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | DateTime :deadline, null: false
6 | DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/jwt_refresh.erb:
--------------------------------------------------------------------------------
1 | # Used by the jwt refresh feature
2 | create_table :<%= table_prefix %>_jwt_refresh_keys do
3 | primary_key :id, type: :Bignum
4 | foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, null: false, type: :Bignum
5 | String :key, null: false
6 | DateTime :deadline, null: false
7 | index :<%= table_prefix %>_id
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/lockout.erb:
--------------------------------------------------------------------------------
1 | # Used by the lockout feature
2 | create_table :<%= table_prefix %>_login_failures do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | Integer :number, null: false, default: 1
5 | end
6 | create_table :<%= table_prefix %>_lockouts do
7 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
8 | String :key, null: false
9 | DateTime :deadline, null: false
10 | DateTime :email_last_sent
11 | end
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/otp.erb:
--------------------------------------------------------------------------------
1 | # Used by the otp feature
2 | create_table :<%= table_prefix %>_otp_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | Integer :num_failures, null: false, default: 0
6 | Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/otp_unlock.erb:
--------------------------------------------------------------------------------
1 | # Used by the otp_unlock feature
2 | create_table :<%= table_prefix %>_otp_unlocks do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | Integer :num_successes, null: false, default: 1
5 | Time :next_auth_attempt_after, null: false, default: Sequel::CURRENT_TIMESTAMP
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/password_expiration.erb:
--------------------------------------------------------------------------------
1 | # Used by the password expiration feature
2 | create_table :<%= table_prefix %>_password_change_times do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | DateTime :changed_at, null: false, default: Sequel::CURRENT_TIMESTAMP
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/recovery_codes.erb:
--------------------------------------------------------------------------------
1 | # Used by the recovery codes feature
2 | create_table :<%= table_prefix %>_recovery_codes do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, type: :Bignum
4 | String :code
5 | primary_key [:id, :code]
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/remember.erb:
--------------------------------------------------------------------------------
1 | # Used by the remember me feature
2 | create_table :<%= table_prefix %>_remember_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | DateTime :deadline, null: false
6 | end
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/reset_password.erb:
--------------------------------------------------------------------------------
1 | # Used by the password reset feature
2 | create_table :<%= table_prefix %>_password_reset_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | DateTime :deadline, null: false
6 | DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/single_session.erb:
--------------------------------------------------------------------------------
1 | # Used by the single session feature
2 | create_table :<%= table_prefix %>_session_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/sms_codes.erb:
--------------------------------------------------------------------------------
1 | # Used by the sms codes feature
2 | create_table :<%= table_prefix %>_sms_codes do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :phone_number, null: false
5 | Integer :num_failures
6 | String :code
7 | DateTime :code_issued_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/verify_account.erb:
--------------------------------------------------------------------------------
1 | # Used by the account verification feature
2 | create_table :<%= table_prefix %>_verification_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | DateTime :requested_at, null: false, default: Sequel::CURRENT_TIMESTAMP
6 | DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/verify_login_change.erb:
--------------------------------------------------------------------------------
1 | # Used by the verify login change feature
2 | create_table :<%= table_prefix %>_login_change_keys do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :key, null: false
5 | String :login, null: false
6 | DateTime :deadline, null: false
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/migration/sequel/webauthn.erb:
--------------------------------------------------------------------------------
1 | # Used by the webauthn feature
2 | create_table :<%= table_prefix %>_webauthn_user_ids do
3 | foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4 | String :webauthn_id, null: false
5 | end
6 | create_table :<%= table_prefix %>_webauthn_keys do
7 | foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, type: :Bignum
8 | String :webauthn_id
9 | String :public_key, null: false
10 | Integer :sign_count, null: false
11 | Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
12 | primary_key [:<%= table_prefix %>_id, :webauthn_id]
13 | end
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/INSTRUCTIONS:
--------------------------------------------------------------------------------
1 | ===============================================================================
2 |
3 | * Ensure you have defined a root path in your config/routes.rb. For example:
4 |
5 | root to: "pages#home"
6 |
7 | * Ensure you're displaying flash messages in your layout template. For example:
8 |
9 | <% if notice %>
10 |
<%= notice %>
11 | <% end %>
12 | <% if alert %>
13 | <%= alert %>
14 | <% end %>
15 |
16 | * Titles for Rodauth pages are available via @page_title instance variable
17 | by default, you can use it in your layout file:
18 |
19 | <%= @page_title || "Default title" %>
20 |
21 | * You can copy Rodauth views into your app by running:
22 |
23 | rails g rodauth:views # default bootstrap views
24 |
25 | rails g rodauth:views --css=tailwind # tailwind views (requires @tailwindcss/forms plugin)
26 |
27 | * You can copy email templates and generate mailer integration by running:
28 |
29 | rails g rodauth:mailer
30 |
31 | ===============================================================================
32 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/controllers/rodauth_controller.rb.tt:
--------------------------------------------------------------------------------
1 | class RodauthController < ApplicationController
2 | # Used by Rodauth for rendering views, CSRF protection, running any
3 | # registered action callbacks and rescue handlers, instrumentation etc.
4 |
5 | # Controller callbacks and rescue handlers will run around Rodauth endpoints.
6 | # before_action :verify_captcha, only: :login, if: -> { request.post? }
7 | # rescue_from("SomeError") { |exception| ... }
8 |
9 | # Layout can be changed for all Rodauth pages or only certain pages.
10 | # layout "authentication"
11 | # layout -> do
12 | # case rodauth.current_route
13 | # when :login, :create_account, :verify_account, :verify_account_resend,
14 | # :reset_password, :reset_password_request
15 | # "authentication"
16 | # else
17 | # "application"
18 | # end
19 | # end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb.tt:
--------------------------------------------------------------------------------
1 | class RodauthMailer < ApplicationMailer
2 | default to: -> { @rodauth.email_to }, from: -> { @rodauth.email_from }
3 |
4 | <%= mailer_content -%>
5 |
6 | private
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.
10 | def rodauth(name, account_id, &block)
11 | instance = RodauthApp.rodauth(name).allocate
12 | instance.account_from_id(account_id)
13 | instance.instance_eval(&block) if block
14 | instance
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/misc/rodauth_app.rb.tt:
--------------------------------------------------------------------------------
1 | class RodauthApp < Rodauth::Rails::App
2 | # primary configuration
3 | configure RodauthMain
4 |
5 | # secondary configuration
6 | # configure RodauthAdmin, :admin
7 |
8 | route do |r|
9 | <% unless jwt? -%>
10 | rodauth.load_memory # autologin remembered users
11 |
12 | <% end -%>
13 | r.rodauth # route rodauth requests
14 |
15 | # ==> Authenticating requests
16 | # Call `rodauth.require_account` for requests that you want to
17 | # require authentication for. For example:
18 | #
19 | # # authenticate /dashboard/* and /account/* requests
20 | # if r.path.start_with?("/dashboard") || r.path.start_with?("/account")
21 | # rodauth.require_account
22 | # end
23 |
24 | # ==> Secondary configurations
25 | # r.rodauth(:admin) # route admin rodauth requests
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/models/account.rb.tt:
--------------------------------------------------------------------------------
1 | <% if activerecord? -%>
2 | class <%= table_prefix.camelize %> < ApplicationRecord
3 | include Rodauth::Rails.model
4 | <% if ActiveRecord.version >= Gem::Version.new("7.0") -%>
5 | enum :status, { unverified: 1, verified: 2, closed: 3 }
6 | <% else -%>
7 | enum status: { unverified: 1, verified: 2, closed: 3 }
8 | <% end -%>
9 | end
10 | <% else -%>
11 | class <%= table_prefix.camelize %> < Sequel::Model
12 | include Rodauth::Rails.model
13 | plugin :enum
14 | enum :status, unverified: 1, verified: 2, closed: 3
15 | end
16 | <% end -%>
17 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.email_auth_request_path, method: :post, data: { turbo: false } do |form| %>
2 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
3 |
4 |
5 | <%= form.submit rodauth.email_auth_request_button, class: "btn btn-primary" %>
6 |
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.login_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.skip_login_field_on_login? %>
3 |
4 | <%= form.label "login", rodauth.login_label, class: "form-label" %>
5 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", readonly: true, class: "form-control-plaintext" %>
6 |
7 | <% else %>
8 |
9 | <%= form.label "login", rodauth.login_label, class: "form-label" %>
10 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: rodauth.login_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.login_param)}", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
11 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "invalid-feedback", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
12 |
13 | <% end %>
14 |
15 | <% unless rodauth.skip_password_field_on_login? %>
16 |
17 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
18 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
19 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
20 |
21 | <% end %>
22 |
23 |
24 | <%= form.submit rodauth.login_button, class: "btn btn-primary" %>
25 |
26 | <% end %>
27 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb:
--------------------------------------------------------------------------------
1 | <% unless rodauth.login_form_footer_links.empty? %>
2 | <%== rodauth.login_form_footer_links_heading %>
3 |
4 |
5 | <% rodauth.login_form_footer_links.sort.each do |_, link, text| %>
6 | - <%= link_to text, link %>
7 | <% end %>
8 |
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb:
--------------------------------------------------------------------------------
1 | <%= rodauth.recovery_codes.map { |s| h(s) }.join("\n\n") %>
2 |
3 | <% if rodauth.can_add_recovery_codes? %>
4 | <%== rodauth.add_recovery_codes_heading %>
5 | <%= render template: "rodauth/recovery_codes", layout: false %>
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.change_login_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.label "login", rodauth.login_label, class: "form-label" %>
4 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.login_param)}", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "invalid-feedback", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
6 |
7 |
8 | <% if rodauth.require_login_confirmation? %>
9 |
10 | <%= form.label "login-confirm", rodauth.login_confirm_label, class: "form-label" %>
11 | <%= form.email_field rodauth.login_confirm_param, value: params[rodauth.login_confirm_param], id: "login-confirm", autocomplete: "email", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.login_confirm_param)}", aria: ({ invalid: true, describedby: "login-confirm_error_message" } if rodauth.field_error(rodauth.login_confirm_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.login_confirm_param), class: "invalid-feedback", id: "login-confirm_error_message") if rodauth.field_error(rodauth.login_confirm_param) %>
13 |
14 | <% end %>
15 |
16 | <% if rodauth.change_login_requires_password? %>
17 |
18 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
19 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
20 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
21 |
22 | <% end %>
23 |
24 |
25 | <%= form.submit rodauth.change_login_button, class: "btn btn-primary" %>
26 |
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.change_password_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.change_password_requires_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.label "new-password", rodauth.new_password_label, class: "form-label" %>
12 | <%= form.password_field rodauth.new_password_param, value: "", id: "new-password", autocomplete: "new-password", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.new_password_param)}", aria: ({ invalid: true, describedby: "new-password_error_message" } if rodauth.field_error(rodauth.new_password_param)) %>
13 | <%= content_tag(:span, rodauth.field_error(rodauth.new_password_param), class: "invalid-feedback", id: "new-password_error_message") if rodauth.field_error(rodauth.new_password_param) %>
14 |
15 |
16 | <% if rodauth.require_password_confirmation? %>
17 |
18 | <%= form.label "password-confirm", rodauth.password_confirm_label, class: "form-label" %>
19 | <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_confirm_param)}", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
20 | <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "invalid-feedback", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
21 |
22 | <% end %>
23 |
24 |
25 | <%= form.submit rodauth.change_password_button, class: "btn btn-primary" %>
26 |
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.close_account_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.close_account_requires_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.submit rodauth.close_account_button, class: "btn btn-danger" %>
12 |
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/confirm_password.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.confirm_password_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
4 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
6 |
7 |
8 |
9 | <%= form.submit rodauth.confirm_password_button, class: "btn btn-primary" %>
10 |
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/email_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.email_auth_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.submit rodauth.login_button, class: "btn btn-primary" %>
4 |
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/login.html.erb:
--------------------------------------------------------------------------------
1 | <%== rodauth.login_form_header %>
2 | <%= render "login_form" %>
3 | <%== rodauth.login_form_footer %>
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.logout_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.features.include?(:active_sessions) %>
3 |
9 | <% end %>
10 |
11 |
12 | <%= form.submit rodauth.logout_button, class: "btn btn-warning" %>
13 |
14 | <% end %>
15 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/multi_phase_login.html.erb:
--------------------------------------------------------------------------------
1 | <%== rodauth.login_form_header %>
2 | <%== rodauth.render_multi_phase_login_forms %>
3 | <%== rodauth.login_form_footer %>
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_auth_path, method: :post, data: { turbo: false } do |form| %>
2 |
11 |
12 |
13 | <%= form.submit rodauth.otp_auth_button, class: "btn btn-primary" %>
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_disable_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.submit rodauth.otp_disable_button, class: "btn btn-warning" %>
12 |
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_setup_path, method: :post, data: { turbo: false } do |form| %>
2 | <%= form.hidden_field rodauth.otp_setup_param, value: rodauth.otp_user_key, id: "otp-key" %>
3 | <%= form.hidden_field rodauth.otp_setup_raw_param, value: rodauth.otp_key, id: "otp-hmac-secret" if rodauth.otp_keys_use_hmac? %>
4 |
5 |
9 |
10 |
11 |
16 |
17 |
18 | <% if rodauth.two_factor_modifications_require_password? %>
19 |
20 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
21 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
22 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
23 |
24 | <% end %>
25 |
26 |
35 |
36 |
37 | <%= form.submit rodauth.otp_setup_button, class: "btn btn-primary" %>
38 |
39 |
40 |
41 | <% end %>
42 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/otp_unlock.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_unlock_path, method: :post, data: { turbo: false } do |form| %>
2 | <%= rodauth.otp_unlock_consecutive_successes_label %>: <%= rodauth.otp_unlock_num_successes %>
3 | <%= rodauth.otp_unlock_required_consecutive_successes_label %>: <%= rodauth.otp_unlock_auths_required %>
4 | <%= rodauth.otp_unlock_next_auth_deadline_label %>: <%= rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format) %>
5 |
6 |
15 |
16 |
17 | <%= form.submit rodauth.otp_unlock_button, class: "btn btn-primary" %>
18 |
19 | <% end %>
20 |
21 | <%== rodauth.otp_unlock_form_footer %>
22 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/otp_unlock_not_available.html.erb:
--------------------------------------------------------------------------------
1 | <%= rodauth.otp_unlock_consecutive_successes_label %>: <%= rodauth.otp_unlock_num_successes %>
2 | <%= rodauth.otp_unlock_required_consecutive_successes_label %>: <%= rodauth.otp_unlock_auths_required %>
3 | <%= rodauth.otp_unlock_next_auth_attempt_label %>: <%= rodauth.otp_unlock_next_auth_attempt_after.strftime(rodauth.strftime_format) %>
4 | <%= rodauth.otp_unlock_next_auth_attempt_refresh_label %>
5 | <%== rodauth.otp_unlock_refresh_tag %>
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.recovery_auth_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.label "recovery-code", rodauth.recovery_codes_label, class: "form-label" %>
4 | <%= form.text_field rodauth.recovery_codes_param, value: "", id: "recovery-code", autocomplete: "off", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.recovery_codes_param)}", aria: ({ invalid: true, describedby: "recovery-code_error_message" } if rodauth.field_error(rodauth.recovery_codes_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.recovery_codes_param), class: "invalid-feedback", id: "recovery-code_error_message") if rodauth.field_error(rodauth.recovery_codes_param) %>
6 |
7 |
8 |
9 | <%= form.submit rodauth.recovery_auth_button, class: "btn btn-primary" %>
10 |
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/recovery_codes.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.recovery_codes_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.submit rodauth.recovery_codes_button || rodauth.view_recovery_codes_button, name: (rodauth.add_recovery_codes_param if rodauth.recovery_codes_button), class: "btn btn-primary" %>
12 |
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.remember_path, method: :post, data: { turbo: false } do |form| %>
2 |
18 |
19 |
20 | <%= form.submit rodauth.remember_button, class: "btn btn-primary" %>
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.reset_password_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
4 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
6 |
7 |
8 | <% if rodauth.require_password_confirmation? %>
9 |
10 | <%= form.label "password-confirm", rodauth.password_confirm_label, class: "form-label" %>
11 | <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_confirm_param)}", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "invalid-feedback", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
13 |
14 | <% end %>
15 |
16 |
17 | <%= form.submit rodauth.reset_password_button, class: "btn btn-primary" %>
18 |
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.reset_password_request_path, method: :post, data: { turbo: false } do |form| %>
2 | <%== rodauth.reset_password_explanatory_text %>
3 |
4 | <% if params[rodauth.login_param] && !rodauth.field_error(rodauth.login_param) %>
5 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
6 | <% else %>
7 |
8 | <%= form.label "login", rodauth.login_label, class: "form-label" %>
9 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.login_param)}", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
10 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "invalid-feedback", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
11 |
12 | <% end %>
13 |
14 |
15 | <%= form.submit rodauth.reset_password_request_button, class: "btn btn-primary" %>
16 |
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/sms_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_auth_path, method: :post, data: { turbo: false } do |form| %>
2 |
11 |
12 |
13 | <%= form.submit rodauth.sms_auth_button, class: "btn btn-primary" %>
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/sms_confirm.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_confirm_path, method: :post, data: { turbo: false } do |form| %>
2 |
11 |
12 |
13 | <%= form.submit rodauth.sms_confirm_button, class: "btn btn-primary" %>
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_disable_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.submit rodauth.sms_disable_button, class: "btn btn-primary" %>
12 |
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_request_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.submit rodauth.sms_request_button, class: "btn btn-primary" %>
4 |
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_setup_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
19 |
20 |
21 | <%= form.submit rodauth.sms_setup_button, class: "btn btn-primary" %>
22 |
23 | <% end %>
24 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_email_auth_request_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.email_auth_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
3 |
4 | <%= form.submit rodauth.email_auth_request_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" %>
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.login_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.skip_login_field_on_login? %>
3 |
4 | <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
5 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", readonly: true, class: "mt-2 text-sm w-full py-2 px-0 bg-inherit border-transparent focus:ring-0 focus:border-transparent" %>
6 |
7 | <% else %>
8 |
9 | <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
10 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: rodauth.login_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_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: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
11 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
12 |
13 | <% end %>
14 |
15 | <% unless rodauth.skip_password_field_on_login? %>
16 |
17 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
18 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
19 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
20 |
21 | <% end %>
22 |
23 | <%= form.submit rodauth.login_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" %>
24 | <% end %>
25 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form_footer.html.erb:
--------------------------------------------------------------------------------
1 | <% unless rodauth.login_form_footer_links.empty? %>
2 |
3 | <% rodauth.login_form_footer_links.sort.each do |_, link, text| %>
4 | - <%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %>
5 | <% end %>
6 |
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/add_recovery_codes.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% if rodauth.recovery_codes.any? %>
3 |
4 | <% rodauth.recovery_codes.each do |code| %>
5 |
<%= code %>
6 | <% end %>
7 |
8 | <% end %>
9 |
10 | <% if rodauth.can_add_recovery_codes? %>
11 |
12 | <%== rodauth.add_recovery_codes_heading %>
13 |
14 | <%= render template: "rodauth/recovery_codes", layout: false %>
15 | <% end %>
16 |
17 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/close_account.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.close_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.close_account_requires_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 | <%= form.submit rodauth.close_account_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-600 dark:bg-red-400 dark:hover:bg-red-500 dark:text-gray-900 dark:focus:ring-red-400 dark:focus:ring-offset-current" %>
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/confirm_password.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.confirm_password_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 |
4 |
5 | <%= rodauth.confirm_password_page_title %>
6 |
7 |
8 |
9 | <%= form.label "password", rodauth.password_label, class: "text-sm text-gray-400" %>
10 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "w-full px-3 py-2 border rounded-md border-gray-400 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 focus:dark:border-emerald-400 focus:dark:bg-gray-700 invalid:border-red-500 invalid:text-red-600 focus:invalid:border-red-500 focus:invalid:ring-red-500 valid:border-green-500 #{"border rounded-md border-red-500" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
11 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "text-red-800 text-xs mt-1 ml-1", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
12 |
13 |
14 |
15 | <%= form.submit rodauth.confirm_password_button, class: "w-full px-8 py-2 font-semibold rounded-md w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-emerald-400 dark:text-gray-900" %>
16 |
17 |
18 |
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/email_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.email_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <%= form.submit rodauth.login_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" %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/login.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%== rodauth.login_form_header %>
3 | <%= render "login_form" %>
4 | <%== rodauth.login_form_footer %>
5 |
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/logout.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.logout_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.features.include?(:active_sessions) %>
3 |
4 | <%= form.check_box rodauth.global_logout_param, id: "global-logout", class: "rounded 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", include_hidden: false %>
5 | <%= form.label "global-logout", rodauth.global_logout_label, class: "text-sm" %>
6 |
7 | <% end %>
8 |
9 | <%= form.submit rodauth.logout_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md bg-yellow-300 hover:bg-yellow-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-300 dark:bg-violet-400 dark:hover:bg-violet-500 dark:text-gray-900 dark:focus:ring-violet-400 dark:focus:ring-offset-current" %>
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/multi_phase_login.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%== rodauth.login_form_header %>
3 | <%== rodauth.render_multi_phase_login_forms %>
4 | <%== rodauth.login_form_footer %>
5 |
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%= form.label "otp-auth-code", rodauth.otp_auth_label, class: "block text-sm font-semibold" %>
4 | <%= 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)) %>
5 | <%= 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) %>
6 |
7 |
8 | <%= form.submit rodauth.otp_auth_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" %>
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_disable.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_disable_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 | <%= form.submit rodauth.otp_disable_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md bg-yellow-300 hover:bg-yellow-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-300 dark:bg-violet-400 dark:hover:bg-violet-500 dark:text-gray-900 dark:focus:ring-violet-400 dark:focus:ring-offset-current" %>
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.otp_unlock_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | - <%= rodauth.otp_unlock_consecutive_successes_label %>:
4 | - <%= rodauth.otp_unlock_num_successes %>
5 |
6 | - <%= rodauth.otp_unlock_required_consecutive_successes_label %>:
7 | - <%= rodauth.otp_unlock_auths_required %>
8 |
9 | - <%= rodauth.otp_unlock_next_auth_deadline_label %>:
10 | - <%= rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format) %>
11 |
12 |
13 |
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 |
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 %>
23 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock_not_available.html.erb:
--------------------------------------------------------------------------------
1 |
2 | - <%= rodauth.otp_unlock_consecutive_successes_label %>:
3 | - <%= rodauth.otp_unlock_num_successes %>
4 |
5 | - <%= rodauth.otp_unlock_required_consecutive_successes_label %>:
6 | - <%= rodauth.otp_unlock_auths_required %>
7 |
8 | - <%= rodauth.otp_unlock_next_auth_attempt_label %>:
9 | - <%= rodauth.otp_unlock_deadline.strftime(rodauth.strftime_format) %>
10 |
11 |
12 | <%= rodauth.otp_unlock_next_auth_attempt_refresh_label %>
13 |
14 | <%== rodauth.otp_unlock_refresh_tag %>
15 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.recovery_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%= form.label "recovery-code", rodauth.recovery_codes_label, class: "block text-sm font-semibold" %>
4 | <%= form.text_field rodauth.recovery_codes_param, value: "", id: "recovery-code", autocomplete: "off", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.recovery_codes_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: "recovery-code_error_message" } if rodauth.field_error(rodauth.recovery_codes_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.recovery_codes_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "recovery-code_error_message") if rodauth.field_error(rodauth.recovery_codes_param) %>
6 |
7 |
8 | <%= form.submit rodauth.recovery_auth_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" %>
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_codes.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.recovery_codes_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 | <%= form.submit rodauth.recovery_codes_button || rodauth.view_recovery_codes_button, name: (rodauth.add_recovery_codes_param if rodauth.recovery_codes_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" %>
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/remember.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.remember_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
16 |
17 | <%= form.submit rodauth.remember_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" %>
18 | <% end %>
19 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/reset_password.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.reset_password_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
4 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
6 |
7 |
8 | <% if rodauth.require_password_confirmation? %>
9 |
10 | <%= form.label "password-confirm", rodauth.password_confirm_label, class: "block text-sm font-semibold" %>
11 | <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_confirm_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: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
13 |
14 | <% end %>
15 |
16 | <%= form.submit rodauth.reset_password_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" %>
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/reset_password_request.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.reset_password_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%== rodauth.reset_password_explanatory_text %>
4 |
5 |
6 | <% if params[rodauth.login_param] && !rodauth.field_error(rodauth.login_param) %>
7 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
8 | <% else %>
9 |
10 | <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
11 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_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: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
13 |
14 | <% end %>
15 |
16 | <%= form.submit rodauth.reset_password_request_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" %>
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_auth.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%= form.label "sms-code", rodauth.sms_code_label, class: "block text-sm font-semibold" %>
4 | <%= form.text_field rodauth.sms_code_param, value: "", id: "sms-code", autocomplete: "one-time-code", 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.sms_code_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: "sms-code_error_message" } if rodauth.field_error(rodauth.sms_code_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.sms_code_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "sms-code_error_message") if rodauth.field_error(rodauth.sms_code_param) %>
6 |
7 |
8 | <%= form.submit rodauth.sms_auth_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" %>
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_confirm.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_confirm_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%= form.label "sms-code", rodauth.sms_code_label, class: "block text-sm font-semibold" %>
4 | <%= form.text_field rodauth.sms_code_param, value: "", id: "sms-code", autocomplete: "one-time-code", 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.sms_code_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: "sms-code_error_message" } if rodauth.field_error(rodauth.sms_code_param)) %>
5 | <%= content_tag(:span, rodauth.field_error(rodauth.sms_code_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "sms-code_error_message") if rodauth.field_error(rodauth.sms_code_param) %>
6 |
7 |
8 | <%= form.submit rodauth.sms_confirm_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" %>
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_disable.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_disable_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 | <%= form.submit rodauth.sms_disable_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" %>
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_request.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <%= form.submit rodauth.sms_request_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" %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_setup.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.sms_setup_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.label "sms-phone", rodauth.sms_phone_label, class: "block text-sm font-semibold" %>
12 | <%= form.telephone_field rodauth.sms_phone_param, value: "", id: "sms-phone", autocomplete: "tel", 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.sms_phone_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: "sms-phone_error_message" } if rodauth.field_error(rodauth.sms_phone_param)) %>
13 | <%= content_tag(:span, rodauth.field_error(rodauth.sms_phone_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "sms-phone_error_message") if rodauth.field_error(rodauth.sms_phone_param) %>
14 |
15 |
16 | <%= form.submit rodauth.sms_setup_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" %>
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_auth.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% rodauth.two_factor_auth_links.sort.each do |_, link, text| %>
3 | - <%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %>
4 | <% end %>
5 |
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_disable.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.two_factor_disable_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 | <%= form.submit rodauth.two_factor_disable_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md bg-yellow-300 hover:bg-yellow-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-300 dark:bg-violet-400 dark:hover:bg-violet-500 dark:text-gray-900 dark:focus:ring-violet-400 dark:focus:ring-offset-current" %>
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_manage.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% if rodauth.two_factor_setup_links.any? %>
3 |
4 | <%== rodauth.two_factor_setup_heading %>
5 |
6 |
7 | <% rodauth.two_factor_setup_links.sort.each do |_, link, text| %>
8 | - <%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %>
9 | <% end %>
10 |
11 | <% end %>
12 |
13 | <% if rodauth.two_factor_remove_links.any? %>
14 |
15 | <%== rodauth.two_factor_remove_heading %>
16 |
17 |
18 | <% rodauth.two_factor_remove_links.sort.each do |_, link, text| %>
19 | - <%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %>
20 | <% end %>
21 | <% if rodauth.two_factor_remove_links.length > 1 %>
22 | - <%= link_to rodauth.two_factor_disable_link_text, rodauth.two_factor_disable_path, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %>
23 | <% end %>
24 |
25 | <% end %>
26 |
27 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/unlock_account.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.unlock_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%== rodauth.unlock_account_explanatory_text %>
4 |
5 |
6 | <% if rodauth.unlock_account_requires_password? %>
7 |
8 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
9 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
10 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
11 |
12 | <% end %>
13 |
14 | <%= form.submit rodauth.unlock_account_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" %>
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/unlock_account_request.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.unlock_account_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%== rodauth.unlock_account_request_explanatory_text %>
4 |
5 |
6 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
7 |
8 | <%= form.submit rodauth.unlock_account_request_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" %>
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.verify_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.verify_account_set_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 |
9 | <% if rodauth.require_password_confirmation? %>
10 |
11 | <%= form.label "password-confirm", rodauth.password_confirm_label, class: "block text-sm font-semibold" %>
12 | <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_confirm_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: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
13 | <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
14 |
15 | <% end %>
16 | <% end %>
17 |
18 | <%= form.submit rodauth.verify_account_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" %>
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account_resend.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.verify_account_resend_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 |
3 | <%== rodauth.verify_account_resend_explanatory_text %>
4 |
5 |
6 | <% if params[rodauth.login_param] %>
7 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
8 | <% else %>
9 |
10 | <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
11 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_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: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
13 |
14 | <% end %>
15 |
16 | <%= form.submit rodauth.verify_account_resend_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" %>
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_login_change.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.verify_login_change_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <%= form.submit rodauth.verify_login_change_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" %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_auth.html.erb:
--------------------------------------------------------------------------------
1 | <% cred = rodauth.webauthn_credential_options_for_get %>
2 |
3 | <%= form_with url: rodauth.webauthn_auth_form_path, method: :post, id: "webauthn-auth-form", data: { credential_options: cred.as_json.to_json, turbo: false }, class: "w-full max-w-sm" do |form| %>
4 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
5 | <%= form.hidden_field rodauth.webauthn_auth_challenge_param, value: cred.challenge %>
6 | <%= form.hidden_field rodauth.webauthn_auth_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
7 | <%= form.text_field rodauth.webauthn_auth_param, value: "", id: "webauthn-auth", class: "hidden", aria: { hidden: "true" } %>
8 |
9 | <%= form.submit rodauth.webauthn_auth_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" %>
10 |
11 | <% end %>
12 |
13 | <%= javascript_include_tag rodauth.webauthn_auth_js_path, extname: false %>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_autofill.html.erb:
--------------------------------------------------------------------------------
1 | <% cred = rodauth.webauthn_credential_options_for_get %>
2 |
3 | <%= form_with url: rodauth.webauthn_login_path, method: :post, id: "webauthn-login-form", data: { credential_options: cred.as_json.to_json, turbo: false } do |form| %>
4 | <%= form.hidden_field rodauth.webauthn_auth_challenge_param, value: cred.challenge %>
5 | <%= form.hidden_field rodauth.webauthn_auth_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
6 | <%= form.text_field rodauth.webauthn_auth_param, value: "", id: "webauthn-auth", class: "hidden", aria: { hidden: "true" } %>
7 | <%= form.submit rodauth.webauthn_auth_button, class: "hidden" %>
8 | <% end %>
9 |
10 | <%= javascript_include_tag rodauth.webauthn_autofill_js_path, extname: false %>
11 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.webauthn_remove_path, method: :post, id: "webauthn-remove-form", data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
20 |
21 | <%= form.submit rodauth.webauthn_remove_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" %>
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_setup.html.erb:
--------------------------------------------------------------------------------
1 | <% cred = rodauth.new_webauthn_credential %>
2 |
3 | <%= form_with url: request.path, method: :post, id: "webauthn-setup-form", data: { credential_options: cred.as_json.to_json, turbo: false }, class: "w-full max-w-sm" do |form| %>
4 | <%= form.hidden_field rodauth.webauthn_setup_challenge_param, value: cred.challenge %>
5 | <%= form.hidden_field rodauth.webauthn_setup_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
6 | <%= form.text_field rodauth.webauthn_setup_param, value: "", id: "webauthn-setup", class: "hidden", aria: { hidden: "true" } %>
7 |
8 | <% if rodauth.two_factor_modifications_require_password? %>
9 |
10 | <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
11 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_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: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
13 |
14 | <% end %>
15 |
16 |
17 | <%= form.submit rodauth.webauthn_setup_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" %>
18 |
19 | <% end %>
20 |
21 | <%= javascript_include_tag rodauth.webauthn_setup_js_path, extname: false %>
22 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% rodauth.two_factor_auth_links.sort.each do |_, link, text| %>
3 | - <%= link_to text, link %>
4 | <% end %>
5 |
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.two_factor_disable_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
11 | <%= form.submit rodauth.two_factor_disable_button, class: "btn btn-primary" %>
12 |
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb:
--------------------------------------------------------------------------------
1 | <% if rodauth.two_factor_setup_links.any? %>
2 | <%== rodauth.two_factor_setup_heading %>
3 |
4 |
5 | <% rodauth.two_factor_setup_links.sort.each do |_, link, text| %>
6 | - <%= link_to text, link %>
7 | <% end %>
8 |
9 | <% end %>
10 |
11 | <% if rodauth.two_factor_remove_links.any? %>
12 | <%== rodauth.two_factor_remove_heading %>
13 |
14 |
15 | <% rodauth.two_factor_remove_links.sort.each do |_, link, text| %>
16 | - <%= link_to text, link %>
17 | <% end %>
18 | <% if rodauth.two_factor_remove_links.length > 1 %>
19 | - <%= link_to rodauth.two_factor_disable_link_text, rodauth.two_factor_disable_path %>
20 | <% end %>
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.unlock_account_path, method: :post, data: { turbo: false } do |form| %>
2 | <%== rodauth.unlock_account_explanatory_text %>
3 |
4 | <% if rodauth.unlock_account_requires_password? %>
5 |
6 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
7 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
8 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
9 |
10 | <% end %>
11 |
12 |
13 | <%= form.submit rodauth.unlock_account_button, class: "btn btn-primary" %>
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.unlock_account_request_path, method: :post, data: { turbo: false } do |form| %>
2 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
3 |
4 | <%== rodauth.unlock_account_request_explanatory_text %>
5 |
6 |
7 | <%= form.submit rodauth.unlock_account_request_button, class: "btn btn-primary" %>
8 |
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/verify_account.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.verify_account_path, method: :post, data: { turbo: false } do |form| %>
2 | <% if rodauth.verify_account_set_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 |
9 | <% if rodauth.require_password_confirmation? %>
10 |
11 | <%= form.label "password-confirm", rodauth.password_confirm_label, class: "form-label" %>
12 | <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_confirm_param)}", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
13 | <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "invalid-feedback", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
14 |
15 | <% end %>
16 | <% end %>
17 |
18 |
19 | <%= form.submit rodauth.verify_account_button, class: "btn btn-primary" %>
20 |
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.verify_account_resend_path, method: :post, data: { turbo: false } do |form| %>
2 | <%== rodauth.verify_account_resend_explanatory_text %>
3 |
4 | <% if params[rodauth.login_param] %>
5 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
6 | <% else %>
7 |
8 | <%= form.label "login", rodauth.login_label, class: "form-label" %>
9 | <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.login_param)}", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
10 | <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "invalid-feedback", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
11 |
12 | <% end %>
13 |
14 |
15 | <%= form.submit rodauth.verify_account_resend_button, class: "btn btn-primary" %>
16 |
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/verify_login_change.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.verify_login_change_path, method: :post, data: { turbo: false } do |form| %>
2 |
3 | <%= form.submit rodauth.verify_login_change_button, class: "btn btn-primary" %>
4 |
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb:
--------------------------------------------------------------------------------
1 | <% cred = rodauth.webauthn_credential_options_for_get %>
2 |
3 | <%= form_with url: rodauth.webauthn_auth_form_path, method: :post, id: "webauthn-auth-form", data: { credential_options: cred.as_json.to_json, turbo: false } do |form| %>
4 | <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
5 | <%= form.hidden_field rodauth.webauthn_auth_challenge_param, value: cred.challenge %>
6 | <%= form.hidden_field rodauth.webauthn_auth_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
7 | <%= form.text_field rodauth.webauthn_auth_param, value: "", id: "webauthn-auth", class: "d-none", aria: { hidden: "true" } %>
8 |
13 | <% end %>
14 |
15 | <%= javascript_include_tag rodauth.webauthn_auth_js_path, extname: false %>
16 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/webauthn_autofill.html.erb:
--------------------------------------------------------------------------------
1 | <% cred = rodauth.webauthn_credential_options_for_get %>
2 |
3 | <%= form_with url: rodauth.webauthn_login_path, method: :post, id: "webauthn-login-form", data: { credential_options: cred.as_json.to_json, turbo: false } do |form| %>
4 | <%= form.hidden_field rodauth.webauthn_auth_challenge_param, value: cred.challenge %>
5 | <%= form.hidden_field rodauth.webauthn_auth_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
6 | <%= form.text_field rodauth.webauthn_auth_param, value: "", id: "webauthn-auth", class: "d-none", aria: { hidden: "true" } %>
7 | <%= form.submit rodauth.webauthn_auth_button, class: "d-none" %>
8 | <% end %>
9 |
10 | <%= javascript_include_tag rodauth.webauthn_autofill_js_path, extname: false %>
11 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: rodauth.webauthn_remove_path, method: :post, id: "webauthn-remove-form", data: { turbo: false } do |form| %>
2 | <% if rodauth.two_factor_modifications_require_password? %>
3 |
4 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
5 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7 |
8 | <% end %>
9 |
10 |
20 |
21 |
22 | <%= form.submit rodauth.webauthn_remove_button, class: "btn btn-primary" %>
23 |
24 | <% end %>
25 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb:
--------------------------------------------------------------------------------
1 | <% cred = rodauth.new_webauthn_credential %>
2 |
3 | <%= form_with url: request.path, method: :post, id: "webauthn-setup-form", data: { credential_options: cred.as_json.to_json, turbo: false } do |form| %>
4 | <%= form.hidden_field rodauth.webauthn_setup_challenge_param, value: cred.challenge %>
5 | <%= form.hidden_field rodauth.webauthn_setup_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
6 | <%= form.text_field rodauth.webauthn_setup_param, value: "", id: "webauthn-setup", class: "d-none", aria: { hidden: "true" } %>
7 |
8 | <% if rodauth.two_factor_modifications_require_password? %>
9 |
10 | <%= form.label "password", rodauth.password_label, class: "form-label" %>
11 | <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
12 | <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
13 |
14 | <% end %>
15 |
16 |
21 | <% end %>
22 |
23 | <%= javascript_include_tag rodauth.webauthn_setup_js_path, extname: false %>
24 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb:
--------------------------------------------------------------------------------
1 | Someone has requested a login link for the account with this email
2 | address. If you did not request a login link, please ignore this
3 | message. If you requested a login link, please go to
4 | <%= @rodauth.email_auth_email_link %>
5 | to login to this account.
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_disabled.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has disabled TOTP authentication for the account
2 | associated to this email address.
3 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_locked_out.text.erb:
--------------------------------------------------------------------------------
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.
10 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_setup.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has setup TOTP authentication for the account
2 | associated to this email address.
3 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlock_failed.text.erb:
--------------------------------------------------------------------------------
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.
9 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlocked.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has unlocked TOTP authentication for the account
2 | associated to this email address.
3 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/password_changed.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has changed the password for the account
2 | associated to this email address.
3 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb:
--------------------------------------------------------------------------------
1 | Someone has requested a password reset for the account with this email
2 | address. If you did not request a password reset, please ignore this
3 | message. If you requested a password reset, please go to
4 | <%= @rodauth.reset_password_email_link %>
5 | to reset the password for the account.
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password_notify.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has reset the password for the account
2 | associated to this email address.
3 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb:
--------------------------------------------------------------------------------
1 | Someone has requested that the account with this email be unlocked.
2 | If you did not request the unlocking of this account, please ignore this
3 | message. If you requested the unlocking of this account, please go to
4 | <%= @rodauth.unlock_account_email_link %>
5 | to unlock this account.
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb:
--------------------------------------------------------------------------------
1 | Someone has created an account with this email address. If you did not create
2 | this account, please ignore this message. If you created this account, please go to
3 | <%= @rodauth.verify_account_email_link %>
4 | to verify the account.
5 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb:
--------------------------------------------------------------------------------
1 | Someone with an account has requested their login be changed to this email address:
2 |
3 | Old email: <%= @account.email %>
4 |
5 | New email: <%= @new_email %>
6 |
7 | If you did not request this login change, please ignore this message. If you
8 | requested this login change, please go to
9 | <%= @rodauth.verify_login_change_email_link %>
10 | to verify the login change.
11 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_added.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has added a WebAuthn authenticator to the
2 | account associated to this email address. There are now <%= @account.webauthn_keys.count %> WebAuthn
3 | authenticator(s) with access to the account.
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_removed.text.erb:
--------------------------------------------------------------------------------
1 | Someone (hopefully you) has removed a WebAuthn authenticator from the
2 | account associated to this email address. There are now <%= @account.webauthn_keys.count %> WebAuthn
3 | authenticator(s) with access to the account.
4 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/config/initializers/rodauth.rb.tt:
--------------------------------------------------------------------------------
1 | Rodauth::Rails.configure do |config|
2 | config.app = "RodauthApp"
3 | # config.middleware = false # disable auto-insertion of Rodauth middleware
4 | # config.tilt = false # skip loading Tilt gem for rendering built-in templates
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/db/migrate/create_rodauth.rb.tt:
--------------------------------------------------------------------------------
1 | <% if defined?(::ActiveRecord::Railtie) -%>
2 | class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
3 | def change
4 | <%= migration_content -%>
5 | end
6 | end
7 | <% else -%>
8 | Sequel.migration do
9 | change do
10 | <%= migration_content -%>
11 | end
12 | end
13 | <% end -%>
14 |
--------------------------------------------------------------------------------
/lib/generators/rodauth/templates/test/fixtures/accounts.yml.tt:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 | one:
3 | email: freddie@queen.com
4 | password_hash: <%%= RodauthApp.rodauth.allocate.password_hash("password") %>
5 | status: verified
6 |
7 | two:
8 | email: brian@queen.com
9 | password_hash: <%%= RodauthApp.rodauth.allocate.password_hash("password") %>
10 | status: verified
11 |
--------------------------------------------------------------------------------
/lib/rodauth-rails.rb:
--------------------------------------------------------------------------------
1 | require "rodauth/rails"
2 |
--------------------------------------------------------------------------------
/lib/rodauth/rails.rb:
--------------------------------------------------------------------------------
1 | require "rodauth/rails/version"
2 | require "rodauth/rails/railtie"
3 | require "rodauth/model"
4 |
5 | module Rodauth
6 | module Rails
7 | class Error < StandardError
8 | end
9 |
10 | # This allows avoiding loading Rodauth at boot time.
11 | autoload :App, "rodauth/rails/app"
12 | autoload :Auth, "rodauth/rails/auth"
13 | autoload :Mailer, "rodauth/rails/mailer"
14 |
15 | @app = nil
16 | @middleware = true
17 | @tilt = true
18 |
19 | class << self
20 | def lib(**options, &block)
21 | c = Class.new(Rodauth::Rails::App)
22 | c.configure(json: false, **options) do
23 | enable :internal_request
24 | instance_exec(&block)
25 | end
26 | c.freeze
27 | c.rodauth
28 | end
29 |
30 | def rodauth(name = nil, account: nil, **options)
31 | auth_class = app.rodauth!(name)
32 |
33 | unless auth_class.features.include?(:internal_request)
34 | fail Rodauth::Rails::Error, "Rodauth::Rails.rodauth requires internal_request feature to be enabled"
35 | end
36 |
37 | if account
38 | options[:account_id] = account.id
39 | end
40 |
41 | instance = auth_class.internal_request_eval(options) do
42 | if defined?(ActiveRecord::Base) && account.is_a?(ActiveRecord::Base)
43 | @account = account.attributes_before_type_cast.symbolize_keys
44 | elsif defined?(Sequel::Model) && account.is_a?(Sequel::Model)
45 | @account = account.values
46 | end
47 | self
48 | end
49 |
50 | # clean up inspect output
51 | instance.remove_instance_variable(:@internal_request_block)
52 | instance.remove_instance_variable(:@internal_request_return_value)
53 |
54 | instance
55 | end
56 |
57 | def model(name = nil, **options)
58 | Rodauth::Model.new(app.rodauth!(name), **options)
59 | end
60 |
61 | # Routing constraint that requires authenticated account.
62 | def authenticate(name = nil, &condition)
63 | lambda do |request|
64 | rodauth = request.env.fetch ["rodauth", *name].join(".")
65 | rodauth.require_account
66 | condition.nil? || condition.call(rodauth)
67 | end
68 | end
69 |
70 | if ::Rails.gem_version >= Gem::Version.new("5.2")
71 | def secret_key_base
72 | ::Rails.application.secret_key_base
73 | end
74 | else
75 | def secret_key_base
76 | ::Rails.application.secrets.secret_key_base
77 | end
78 | end
79 |
80 | def configure
81 | yield self
82 | end
83 |
84 | attr_writer :app
85 | attr_writer :middleware
86 | attr_writer :tilt
87 |
88 | def app
89 | fail Rodauth::Rails::Error, "app was not configured" unless @app
90 |
91 | @app.constantize
92 | end
93 |
94 | def middleware?
95 | @middleware
96 | end
97 |
98 | def tilt?
99 | @tilt
100 | end
101 | end
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/auth.rb:
--------------------------------------------------------------------------------
1 | require "rodauth"
2 | require "rodauth/rails/feature"
3 |
4 | module Rodauth
5 | module Rails
6 | # Base auth class that applies some changes to the default configuration.
7 | class Auth < Rodauth::Auth
8 | configure do
9 | enable :rails
10 |
11 | # database functions are more complex to set up, so disable them by default
12 | use_database_authentication_functions? false
13 |
14 | # avoid having to set deadline values in column default values
15 | set_deadline_values? true
16 |
17 | # use HMACs for additional security
18 | hmac_secret { Rodauth::Rails.secret_key_base }
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/controller_methods.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | module Rails
3 | module ControllerMethods
4 | def self.included(controller)
5 | # ActionController::API doesn't have helper methods
6 | if controller.respond_to?(:helper_method)
7 | controller.helper_method :rodauth
8 | end
9 | end
10 |
11 | def rodauth(name = nil)
12 | request.env.fetch ["rodauth", *name].join(".")
13 | end
14 |
15 | private
16 |
17 | # Adds response status to instrumentation payload for logging,
18 | # when calling a halting rodauth method inside a controller.
19 | def append_info_to_payload(payload)
20 | super
21 | if request.env["rodauth.rails.status"]
22 | payload[:status] = request.env.delete("rodauth.rails.status")
23 | end
24 | end
25 |
26 | def rodauth_response
27 | res = catch(:halt) { return yield }
28 |
29 | self.status = res[0]
30 | self.headers.merge! res[1]
31 | self.response_body = res[2]
32 |
33 | res
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | Feature.define(:rails) do
3 | # Assign feature and feature configuration to constants for introspection.
4 | Rodauth::Rails::Feature = self
5 | Rodauth::Rails::FeatureConfiguration = self.configuration
6 |
7 | require "rodauth/rails/feature/base"
8 | require "rodauth/rails/feature/callbacks"
9 | require "rodauth/rails/feature/csrf"
10 | require "rodauth/rails/feature/render"
11 | require "rodauth/rails/feature/email" if defined?(ActionMailer)
12 | require "rodauth/rails/feature/instrumentation"
13 | require "rodauth/rails/feature/internal_request"
14 |
15 | include Rodauth::Rails::Feature::Base
16 | include Rodauth::Rails::Feature::Callbacks
17 | include Rodauth::Rails::Feature::Csrf
18 | include Rodauth::Rails::Feature::Render
19 | include Rodauth::Rails::Feature::Email if defined?(ActionMailer)
20 | include Rodauth::Rails::Feature::Instrumentation
21 | include Rodauth::Rails::Feature::InternalRequest
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature/base.rb:
--------------------------------------------------------------------------------
1 | require "active_support/concern"
2 |
3 | module Rodauth
4 | module Rails
5 | module Feature
6 | module Base
7 | extend ActiveSupport::Concern
8 |
9 | included do
10 | auth_methods :rails_controller
11 | auth_value_methods :rails_account_model
12 | auth_cached_method :rails_controller_instance
13 | end
14 |
15 | def rails_account
16 | @rails_account = nil if account.nil? || @rails_account&.id != account_id
17 | @rails_account ||= instantiate_rails_account if account!
18 | end
19 |
20 | # Reset Rails session to protect from session fixation attacks.
21 | def clear_session
22 | rails_controller_instance.reset_session
23 | end
24 |
25 | # Default the flash error key to Rails' default :alert.
26 | def flash_error_key
27 | :alert
28 | end
29 |
30 | # Evaluates the block in context of a Rodauth controller instance.
31 | def rails_controller_eval(&block)
32 | rails_controller_instance.instance_exec(&block)
33 | end
34 |
35 | def rails_controller
36 | if only_json? && ::Rails.configuration.api_only
37 | ActionController::API
38 | else
39 | ActionController::Base
40 | end
41 | end
42 |
43 | def rails_account_model
44 | table = accounts_table
45 | table = table.column if table.is_a?(Sequel::SQL::QualifiedIdentifier) # schema is specified
46 | table.to_s.classify.constantize
47 | rescue NameError
48 | raise Error, "cannot infer account model, please set `rails_account_model` in your rodauth configuration"
49 | end
50 |
51 | delegate :rails_routes, :rails_cookies, :rails_request, to: :scope
52 |
53 | def session
54 | super
55 | rescue Roda::RodaError
56 | fail Rodauth::Rails::Error, "There is no session middleware configured, see instructions on how to add it: https://guides.rubyonrails.org/api_app.html#using-session-middlewares"
57 | end
58 |
59 | private
60 |
61 | def instantiate_rails_account
62 | if defined?(ActiveRecord::Base) && rails_account_model < ActiveRecord::Base
63 | if account_id
64 | rails_account_model.instantiate(account.stringify_keys)
65 | else
66 | rails_account_model.new(account)
67 | end
68 | elsif defined?(Sequel::Model) && rails_account_model < Sequel::Model
69 | rails_account_model.load(account)
70 | else
71 | fail Error, "unsupported model type: #{rails_account_model}"
72 | end
73 | end
74 |
75 | # Instance of the configured controller with current request's env hash.
76 | def _rails_controller_instance
77 | controller = rails_controller.new
78 | controller.set_request! rails_request
79 | controller.set_response! rails_controller.make_response!(controller.request)
80 | controller
81 | end
82 | end
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature/callbacks.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | module Rails
3 | module Feature
4 | module Callbacks
5 | extend ActiveSupport::Concern
6 |
7 | private
8 |
9 | def _around_rodauth
10 | rails_controller_instance.instance_variable_set(:@_action_name, current_route.to_s)
11 |
12 | rails_controller_around { super }
13 | end
14 |
15 | # Runs controller callbacks and rescue handlers around Rodauth actions.
16 | def rails_controller_around
17 | result = nil
18 |
19 | rails_controller_rescue do
20 | rails_controller_callbacks do
21 | result = catch(:halt) { yield }
22 | end
23 | end
24 |
25 | result = handle_rails_controller_response(result)
26 |
27 | throw :halt, result if result
28 | end
29 |
30 | # Runs any #(before|around|after)_action controller callbacks.
31 | def rails_controller_callbacks(&block)
32 | rails_controller_instance.run_callbacks(:process_action, &block)
33 | end
34 |
35 | # Runs any registered #rescue_from controller handlers.
36 | def rails_controller_rescue
37 | yield
38 | rescue Exception => exception
39 | rails_controller_instance.rescue_with_handler(exception) || raise
40 |
41 | unless rails_controller_instance.performed?
42 | raise Rodauth::Rails::Error, "rescue_from handler didn't write any response"
43 | end
44 | end
45 |
46 | # Handles controller rendering a response or setting response headers.
47 | def handle_rails_controller_response(result)
48 | if rails_controller_instance.performed?
49 | rails_controller_response
50 | elsif result
51 | result[1].merge!(rails_controller_instance.response.headers)
52 | result
53 | end
54 | end
55 |
56 | # Returns Roda response from controller response if set.
57 | def rails_controller_response
58 | controller_response = rails_controller_instance.response
59 |
60 | response.status = controller_response.status
61 | response.headers.merge! controller_response.headers
62 | response.write controller_response.body
63 |
64 | response.finish
65 | end
66 | end
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature/csrf.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | module Rails
3 | module Feature
4 | module Csrf
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | auth_methods(
9 | :rails_csrf_tag,
10 | :rails_csrf_param,
11 | :rails_csrf_token,
12 | :rails_check_csrf!,
13 | )
14 | end
15 |
16 | # Render Rails CSRF tags in Rodauth templates.
17 | def csrf_tag(*)
18 | rails_csrf_tag if rails_controller_csrf?
19 | end
20 |
21 | # Verify Rails' authenticity token.
22 | def check_csrf
23 | rails_check_csrf! if rails_controller_csrf?
24 | end
25 |
26 | # Have Rodauth call #check_csrf automatically.
27 | def check_csrf?
28 | rails_check_csrf? if rails_controller_csrf?
29 | end
30 |
31 | private
32 |
33 | def rails_controller_callbacks
34 | return super unless rails_controller_csrf?
35 |
36 | # don't verify CSRF token as part of callbacks, Rodauth will do that
37 | rails_controller_instance.allow_forgery_protection = false
38 | super do
39 | # turn the setting back to default so that form tags generate CSRF tags
40 | rails_controller_instance.allow_forgery_protection = rails_controller.allow_forgery_protection
41 | yield
42 | end
43 | end
44 |
45 | # Checks whether ActionController::RequestForgeryProtection is included
46 | # and that protect_from_forgery was called.
47 | def rails_check_csrf?
48 | !!rails_controller_instance.forgery_protection_strategy
49 | end
50 |
51 | # Calls the controller to verify the authenticity token.
52 | def rails_check_csrf!
53 | rails_controller_instance.send(:verify_authenticity_token)
54 | end
55 |
56 | # Hidden tag with Rails CSRF token inserted into Rodauth templates.
57 | def rails_csrf_tag
58 | %()
59 | end
60 |
61 | # The request parameter under which to send the Rails CSRF token.
62 | def rails_csrf_param
63 | rails_controller.request_forgery_protection_token
64 | end
65 |
66 | # The Rails CSRF token value inserted into Rodauth templates.
67 | def rails_csrf_token
68 | rails_controller_instance.send(:form_authenticity_token)
69 | end
70 |
71 | # Checks whether ActionController::RequestForgeryProtection is included.
72 | def rails_controller_csrf?
73 | rails_controller.respond_to?(:protect_from_forgery)
74 | end
75 | end
76 | end
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature/email.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | module Rails
3 | module Feature
4 | module Email
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | depends :email_base
9 | end
10 |
11 | private
12 |
13 | # Create emails with ActionMailer which uses configured delivery method.
14 | def create_email_to(to, subject, body)
15 | Rodauth::Rails::Mailer.create_email(
16 | to: to,
17 | from: email_from,
18 | subject: "#{email_subject_prefix}#{subject}",
19 | body: body
20 | )
21 | end
22 |
23 | # Delivers the given email.
24 | def send_email(email)
25 | email.deliver_now
26 | end
27 |
28 | # for backwards compatibility
29 | Mailer = Rodauth::Rails::Mailer
30 | deprecate_constant :Mailer
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature/internal_request.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | module Rails
3 | module Feature
4 | module InternalRequest
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | auth_private_methods :rails_url_options
9 | end
10 |
11 | def domain
12 | return super unless missing_host? && rails_url_options!
13 |
14 | rails_url_options.fetch(:host)
15 | end
16 |
17 | def base_url
18 | return super unless missing_host? && domain && rails_url_options!
19 |
20 | scheme = rails_url_options[:protocol] || "http"
21 | port = rails_url_options[:port]
22 |
23 | url = "#{scheme}://#{domain}"
24 | url << ":#{port}" if port
25 | url
26 | end
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 |
33 | private
34 |
35 | def rails_controller_around
36 | return yield if internal_request?
37 | super
38 | end
39 |
40 | def rails_instrument_request
41 | return yield if internal_request?
42 | super
43 | end
44 |
45 | def rails_instrument_redirection
46 | return yield if internal_request?
47 | super
48 | end
49 |
50 | # Checks whether we're in an internal request and host was not set,
51 | # or the request doesn't exist such as with path_class_methods feature.
52 | def missing_host?
53 | internal_request? && (request.host.nil? || request.host == INVALID_DOMAIN) || scope.nil?
54 | end
55 |
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
59 |
60 | def _rails_url_options
61 | defined?(ActionMailer) ? ActionMailer::Base.default_url_options.dup : {}
62 | end
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/rodauth/rails/feature/render.rb:
--------------------------------------------------------------------------------
1 | module Rodauth
2 | module Rails
3 | module Feature
4 | module Render
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | auth_methods :rails_render
9 | end
10 |
11 | # Renders templates with layout. First tries to render a user-defined
12 | # template, otherwise falls back to Rodauth's template.
13 | def view(page, title)
14 | set_title(title)
15 | rails_render(action: page.tr("-", "_"), layout: true) ||
16 | rails_render(html: super.html_safe, layout: true, formats: :html)
17 | end
18 |
19 | # Renders templates without layout. First tries to render a user-defined
20 | # template or partial, otherwise falls back to Rodauth's template.
21 | def render(page)
22 | rails_render(partial: page.tr("-", "_"), layout: false) ||
23 | rails_render(action: page.tr("-", "_"), layout: false) ||
24 | super.html_safe
25 | end
26 |
27 | def button(*)
28 | super.html_safe
29 | end
30 |
31 | if defined?(::Turbo)
32 | def turbo_stream
33 | rails_controller_instance.send(:turbo_stream)
34 | end
35 | end
36 |
37 | private
38 |
39 | # Calls the Rails renderer, returning nil if a template is missing.
40 | def rails_render(*args)
41 | return if rails_controller <= ActionController::API
42 |
43 | rails_controller_instance.render_to_string(*args)
44 | rescue ActionView::MissingTemplate
45 | nil
46 | end
47 |
48 | # Only look up template formats that the current request is accepting.
49 | def before_rodauth
50 | super
51 | rails_controller_instance.formats = rails_request.formats.map(&:ref).compact
52 | end
53 |
54 | # Not all Rodauth actions are Turbo-compatible (some form submissions
55 | # render 200 HTML responses), so we disable Turbo on all Rodauth forms.
56 | def _view(meth, *)
57 | html = super
58 | html = html.gsub(/