rodauth-rails 1.7.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/README.md +50 -10
  4. data/lib/generators/rodauth/install_generator.rb +35 -34
  5. data/lib/generators/rodauth/migration/active_record/account_expiration.erb +2 -2
  6. data/lib/generators/rodauth/migration/active_record/active_sessions.erb +2 -2
  7. data/lib/generators/rodauth/migration/active_record/audit_logging.erb +3 -3
  8. data/lib/generators/rodauth/migration/active_record/base.erb +1 -1
  9. data/lib/generators/rodauth/migration/active_record/disallow_password_reuse.erb +2 -2
  10. data/lib/generators/rodauth/migration/active_record/email_auth.erb +2 -1
  11. data/lib/generators/rodauth/migration/active_record/jwt_refresh.erb +3 -3
  12. data/lib/generators/rodauth/migration/active_record/lockout.erb +4 -4
  13. data/lib/generators/rodauth/migration/active_record/otp.erb +2 -2
  14. data/lib/generators/rodauth/migration/active_record/password_expiration.erb +2 -2
  15. data/lib/generators/rodauth/migration/active_record/recovery_codes.erb +2 -2
  16. data/lib/generators/rodauth/migration/active_record/remember.erb +2 -2
  17. data/lib/generators/rodauth/migration/active_record/reset_password.erb +2 -2
  18. data/lib/generators/rodauth/migration/active_record/single_session.erb +2 -2
  19. data/lib/generators/rodauth/migration/active_record/sms_codes.erb +2 -2
  20. data/lib/generators/rodauth/migration/active_record/verify_account.erb +2 -2
  21. data/lib/generators/rodauth/migration/active_record/verify_login_change.erb +2 -2
  22. data/lib/generators/rodauth/migration/active_record/webauthn.erb +4 -4
  23. data/lib/generators/rodauth/migration/sequel/account_expiration.erb +2 -2
  24. data/lib/generators/rodauth/migration/sequel/active_sessions.erb +3 -3
  25. data/lib/generators/rodauth/migration/sequel/audit_logging.erb +3 -3
  26. data/lib/generators/rodauth/migration/sequel/base.erb +1 -1
  27. data/lib/generators/rodauth/migration/sequel/disallow_password_reuse.erb +2 -2
  28. data/lib/generators/rodauth/migration/sequel/email_auth.erb +2 -2
  29. data/lib/generators/rodauth/migration/sequel/jwt_refresh.erb +3 -3
  30. data/lib/generators/rodauth/migration/sequel/lockout.erb +4 -4
  31. data/lib/generators/rodauth/migration/sequel/otp.erb +2 -2
  32. data/lib/generators/rodauth/migration/sequel/password_expiration.erb +2 -2
  33. data/lib/generators/rodauth/migration/sequel/recovery_codes.erb +2 -2
  34. data/lib/generators/rodauth/migration/sequel/remember.erb +2 -2
  35. data/lib/generators/rodauth/migration/sequel/reset_password.erb +2 -2
  36. data/lib/generators/rodauth/migration/sequel/single_session.erb +2 -2
  37. data/lib/generators/rodauth/migration/sequel/sms_codes.erb +2 -2
  38. data/lib/generators/rodauth/migration/sequel/verify_account.erb +2 -2
  39. data/lib/generators/rodauth/migration/sequel/verify_login_change.erb +2 -2
  40. data/lib/generators/rodauth/migration/sequel/webauthn.erb +5 -5
  41. data/lib/generators/rodauth/migration_generator.rb +43 -1
  42. data/lib/generators/rodauth/templates/app/mailers/{rodauth_mailer.rb → rodauth_mailer.rb.tt} +8 -6
  43. data/lib/generators/rodauth/templates/app/misc/{rodauth_main.rb → rodauth_main.rb.tt} +69 -10
  44. data/lib/generators/rodauth/templates/app/models/{account.rb → account.rb.tt} +2 -2
  45. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb +2 -2
  46. data/lib/generators/rodauth/templates/test/fixtures/{accounts.yml → accounts.yml.tt} +2 -2
  47. data/lib/rodauth/rails/app.rb +19 -0
  48. data/lib/rodauth/rails/feature/base.rb +0 -10
  49. data/lib/rodauth/rails/version.rb +1 -1
  50. data/rodauth-rails.gemspec +1 -1
  51. metadata +13 -14
  52. data/lib/generators/rodauth/templates/config/initializers/sequel.rb +0 -4
  53. /data/lib/generators/rodauth/templates/app/controllers/{rodauth_controller.rb → rodauth_controller.rb.tt} +0 -0
  54. /data/lib/generators/rodauth/templates/app/misc/{rodauth_app.rb → rodauth_app.rb.tt} +0 -0
  55. /data/lib/generators/rodauth/templates/config/initializers/{rodauth.rb → rodauth.rb.tt} +0 -0
  56. /data/lib/generators/rodauth/templates/db/migrate/{create_rodauth.rb → create_rodauth.rb.tt} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a0e7a34f68cd9914ad148799dcc5b83c5f8509d6d3278f57a26ebd497a8460c
4
- data.tar.gz: bfacb6b1ee374c5c0c1178d1a3b22a08bdb78626c1c91f3997a3c8572a8ede85
3
+ metadata.gz: df597e01d85bea28254330ac00e288d569fe709744e11ade3ada370784034b6a
4
+ data.tar.gz: 2934d9ea6177fa55e383cfbcbfdca6b4b27a36a1afdb7c9971e918fbca76e604
5
5
  SHA512:
6
- metadata.gz: eb5ff0efa4e19d89f864eea1e5d2e7541b2a198cff6af02ec281904e660bac5124d59842f602527e988ac958913724c54225fceb02ca834f1ee03cbf6a975f24
7
- data.tar.gz: 13350871f426602595dfa738b1a2016372f589788dfdd5d06ad5e915d351d2d5a4f3cec18c45871337153106c1f524ab27bae7f4c7f30c7e9fb2dbe02025c77d
6
+ metadata.gz: bf30eabb2fb372e96caf39ceb0e01696dcc45003db00703fd4eaa127bba8378a0e76c4194b0f2161851df82a066f0147ed72cedea25429657a287734f76d4e49
7
+ data.tar.gz: 40f773856e54971d573995e7f6c11b9a67c42c881ce95e458739fc5a12151a954233b3e80339beff622b82e79bc5485fc6497f7dd90c09cfd42ed2ca1abe3912
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 1.8.0 (2023-02-25)
2
+
3
+ * Add table argument to `rodauth:install` generator (@janko)
4
+
5
+ * Add `--prefix` option to `rodauth:migration` generator for choosing table prefix (@janko)
6
+
7
+ * Add `--argon2` option to `rodauth:install` generator for configuring password hashing using Argon2 (@janko)
8
+
9
+ * Move generated Sequel setup from initializer into the Rodauth configuration for faster boot time (@janko)
10
+
11
+ * Use `email_to` and `email_from` Rodauth methods in generated mailer (@janko)
12
+
13
+ * Add missing foreign key constraint in `email_auth` Active Record migration (@janko)
14
+
15
+ * Correctly retrieve JSON request body when using Falcon (@janko)
16
+
17
+ * Render validation error correctly in generated `webauthn_remove` Tailwind template (@janko)
18
+
1
19
  ## 1.7.1 (2023-01-25)
2
20
 
3
21
  * Make internal_request integration work on Rack 3.x (@janko)
data/README.md CHANGED
@@ -70,7 +70,13 @@ Next, run the install generator:
70
70
  $ rails generate rodauth:install
71
71
  ```
72
72
 
73
- Or if you want Rodauth endpoints to be exposed via [JSON API]:
73
+ This will use the `accounts` table. If you want a different table name:
74
+
75
+ ```sh
76
+ $ rails generate rodauth:install users
77
+ ```
78
+
79
+ If you want Rodauth endpoints to be exposed via [JSON API]:
74
80
 
75
81
  ```sh
76
82
  $ rails generate rodauth:install --json # regular authentication using the Rails session
@@ -79,6 +85,13 @@ $ rails generate rodauth:install --jwt # token authentication via the "Authoriza
79
85
  $ bundle add jwt
80
86
  ```
81
87
 
88
+ To use Argon2 instead of bcrypt for password hashing:
89
+
90
+ ```sh
91
+ $ rails generate rodauth:install --argon2
92
+ $ bundle add argon2
93
+ ```
94
+
82
95
  This generator will create a Rodauth app and configuration with common
83
96
  authentication features enabled, a database migration with tables required by
84
97
  those features, a mailer with default templates, and a few other files.
@@ -180,12 +193,9 @@ session will be reset.
180
193
 
181
194
  #### Custom account model
182
195
 
183
- The `#rails_account` method will try to infer the account model class from
184
- the configured table name. For example, if the `accounts_table` is set to
185
- `:users`, it will automatically assume the model class of `User`.
186
-
187
- However, if the model class cannot be inferred from the table name, you can
188
- configure it manually:
196
+ The `#rails_account` method will try to infer the account model class from the
197
+ configured accounts table name. However, if the model class cannot be inferred
198
+ from the table name, you can configure it manually:
189
199
 
190
200
  ```rb
191
201
  # app/misc/rodauth_main.rb
@@ -499,7 +509,7 @@ page][custom mailer worker] on how to set it up.
499
509
 
500
510
  The install generator will create a migration for tables used by the Rodauth
501
511
  features enabled by default. For any additional features, you can use the
502
- migration generator to create the corresponding tables:
512
+ migration generator for creating the required tables:
503
513
 
504
514
  ```sh
505
515
  $ rails generate rodauth:migration otp sms_codes recovery_codes
@@ -515,6 +525,30 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
515
525
  end
516
526
  ```
517
527
 
528
+ #### Table prefix
529
+
530
+ If you're storing account records in a table other than `accounts`, you'll want
531
+ to specify the correct table prefix when generating new migrations:
532
+
533
+ ```sh
534
+ $ rails generate rodauth:migration base active_sessions --prefix user
535
+
536
+ # Add the following to your Rodauth configuration:
537
+ #
538
+ # accounts_table :users
539
+ # active_sessions_table :user_active_session_keys
540
+ # active_sessions_account_id_column :user_id
541
+ ```
542
+ ```rb
543
+ # db/migration/*_create_rodauth_user_base_active_sessions.rb
544
+ class CreateRodauthUserBaseActiveSessions < ActiveRecord::Migration
545
+ def change
546
+ create_table :users do |t| ... end
547
+ create_table :user_active_session_keys do |t| ... end
548
+ end
549
+ end
550
+ ```
551
+
518
552
  #### Custom migration name
519
553
 
520
554
  You can change the default migration name:
@@ -1103,11 +1137,17 @@ require "rodauth/migrations"
1103
1137
 
1104
1138
  class CreateRodauthDatabaseFunctions < ActiveRecord::Migration
1105
1139
  def up
1106
- Rodauth.create_database_authentication_functions(DB)
1140
+ Rodauth.create_database_authentication_functions(db)
1107
1141
  end
1108
1142
 
1109
1143
  def down
1110
- Rodauth.drop_database_authentication_functions(DB)
1144
+ Rodauth.drop_database_authentication_functions(db)
1145
+ end
1146
+
1147
+ private
1148
+
1149
+ def db
1150
+ RodauthMain.allocate.db
1111
1151
  end
1112
1152
  end
1113
1153
  ```
@@ -5,19 +5,13 @@ module Rodauth
5
5
  module Rails
6
6
  module Generators
7
7
  class InstallGenerator < ::Rails::Generators::Base
8
- if RUBY_ENGINE == "jruby"
9
- SEQUEL_ADAPTERS = {
10
- "sqlite3" => "sqlite",
11
- "oracle_enhanced" => "oracle", # https://github.com/rsim/oracle-enhanced
12
- "sqlserver" => "mssql",
13
- }
14
- else
15
- SEQUEL_ADAPTERS = {
16
- "sqlite3" => "sqlite",
17
- "oracle_enhanced" => "oracle", # https://github.com/rsim/oracle-enhanced
18
- "sqlserver" => "tinytds", # https://github.com/rails-sqlserver/activerecord-sqlserver-adapter
19
- }
20
- end
8
+ SEQUEL_ADAPTERS = {
9
+ "postgresql" => RUBY_ENGINE == "jruby" ? "postgresql" : "postgres",
10
+ "mysql2" => RUBY_ENGINE == "jruby" ? "mysql" : "mysql2",
11
+ "sqlite3" => "sqlite",
12
+ "oracle_enhanced" => "oracle",
13
+ "sqlserver" => RUBY_ENGINE == "jruby" ? "mssql" : "tinytds",
14
+ }
21
15
 
22
16
  MAILER_VIEWS = %w[
23
17
  email_auth
@@ -31,24 +25,23 @@ module Rodauth
31
25
  source_root "#{__dir__}/templates"
32
26
  namespace "rodauth:install"
33
27
 
28
+ argument :table, optional: true, type: :string, desc: "Name of the accounts table"
29
+
30
+ class_option :prefix, type: :string, desc: "Change name for account tables"
31
+ class_option :argon2, type: :boolean, desc: "Use Argon2 for password hashing"
34
32
  class_option :json, type: :boolean, desc: "Configure JSON support"
35
33
  class_option :jwt, type: :boolean, desc: "Configure JWT support"
36
34
 
37
- def create_rodauth_migration
38
- invoke "rodauth:migration", migration_features, name: "create_rodauth"
35
+ def generate_rodauth_migration
36
+ invoke "rodauth:migration", migration_features,
37
+ name: "create_rodauth",
38
+ prefix: table_prefix
39
39
  end
40
40
 
41
41
  def create_rodauth_initializer
42
42
  template "config/initializers/rodauth.rb"
43
43
  end
44
44
 
45
- def create_sequel_initializer
46
- return unless defined?(ActiveRecord::Railtie)
47
- return if defined?(Sequel) && !Sequel::DATABASES.empty?
48
-
49
- template "config/initializers/sequel.rb"
50
- end
51
-
52
45
  def create_rodauth_app
53
46
  template "app/misc/rodauth_app.rb"
54
47
  template "app/misc/rodauth_main.rb"
@@ -59,7 +52,7 @@ module Rodauth
59
52
  end
60
53
 
61
54
  def create_account_model
62
- template "app/models/account.rb"
55
+ template "app/models/account.rb", "app/models/#{table_prefix}.rb"
63
56
  end
64
57
 
65
58
  def create_mailer
@@ -73,13 +66,10 @@ module Rodauth
73
66
  end
74
67
 
75
68
  def create_fixtures
76
- test_unit_options = ::Rails.application.config.generators.options[:test_unit]
77
- if test_unit_options[:fixture] && test_unit_options[:fixture_replacement].nil?
78
- if ::Rails.application.config.generators.options[:rails][:test_framework] == :rspec
79
- template "test/fixtures/accounts.yml", "spec/fixtures/accounts.yml"
80
- else
81
- template "test/fixtures/accounts.yml", "test/fixtures/accounts.yml"
82
- end
69
+ generator_options = ::Rails.application.config.generators.options
70
+ if generator_options[:test_unit][:fixture] && generator_options[:test_unit][:fixture_replacement].nil?
71
+ test_dir = generator_options[:rails][:test_framework] == :rspec ? "spec" : "test"
72
+ template "test/fixtures/accounts.yml", "#{test_dir}/fixtures/#{table_prefix.pluralize}.yml"
83
73
  end
84
74
  end
85
75
 
@@ -95,6 +85,10 @@ module Rodauth
95
85
  features
96
86
  end
97
87
 
88
+ def table_prefix
89
+ table&.underscore&.singularize || "account"
90
+ end
91
+
98
92
  def json?
99
93
  options[:json] || api_only? && session_store? && !options[:jwt]
100
94
  end
@@ -103,6 +97,15 @@ module Rodauth
103
97
  options[:jwt] || api_only? && !session_store? && !options[:json]
104
98
  end
105
99
 
100
+ def argon2?
101
+ options[:argon2]
102
+ end
103
+
104
+ def sequel_activerecord_integration?
105
+ defined?(ActiveRecord::Railtie) &&
106
+ (!defined?(Sequel) || Sequel::DATABASES.empty?)
107
+ end
108
+
106
109
  def session_store?
107
110
  !!::Rails.application.config.session_store
108
111
  end
@@ -111,10 +114,8 @@ module Rodauth
111
114
  Rodauth::Rails.api_only?
112
115
  end
113
116
 
114
- def sequel_uri_scheme
115
- scheme = SEQUEL_ADAPTERS[activerecord_adapter] || activerecord_adapter
116
- scheme = "jdbc:#{scheme}" if RUBY_ENGINE == "jruby"
117
- scheme
117
+ def sequel_adapter
118
+ SEQUEL_ADAPTERS[activerecord_adapter] || activerecord_adapter
118
119
  end
119
120
 
120
121
  def activerecord_adapter
@@ -1,7 +1,7 @@
1
1
  # Used by the account expiration feature
2
- create_table :account_activity_times, id: false do |t|
2
+ create_table :<%= table_prefix %>_activity_times, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.datetime :last_activity_at, null: false
6
6
  t.datetime :last_login_at, null: false
7
7
  t.datetime :expired_at
@@ -1,6 +1,6 @@
1
1
  # Used by the active sessions feature
2
- create_table :account_active_session_keys, primary_key: [:account_id, :session_id] do |t|
3
- t.references :account, foreign_key: true<%= primary_key_type(:type) %>
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
4
  t.string :session_id
5
5
  t.datetime :created_at, null: false, default: <%= current_timestamp %>
6
6
  t.datetime :last_use, null: false, default: <%= current_timestamp %>
@@ -1,6 +1,6 @@
1
1
  # Used by the audit logging feature
2
- create_table :account_authentication_audit_logs<%= primary_key_type %> do |t|
3
- t.references :account, foreign_key: true, null: false<%= primary_key_type(:type) %>
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
4
  t.datetime :at, null: false, default: <%= current_timestamp %>
5
5
  t.text :message, null: false
6
6
  <% case activerecord_adapter -%>
@@ -11,6 +11,6 @@ create_table :account_authentication_audit_logs<%= primary_key_type %> do |t|
11
11
  <% else -%>
12
12
  t.string :metadata
13
13
  <% end -%>
14
- t.index [:account_id, :at], name: "audit_account_at_idx"
14
+ t.index [:<%= table_prefix %>_id, :at], name: "audit_<%= table_prefix %>_at_idx"
15
15
  t.index :at, name: "audit_at_idx"
16
16
  end
@@ -2,7 +2,7 @@
2
2
  enable_extension "citext"
3
3
 
4
4
  <% end -%>
5
- create_table :accounts<%= primary_key_type %> do |t|
5
+ create_table :<%= table_prefix.pluralize %><%= primary_key_type %> do |t|
6
6
  t.integer :status, null: false, default: 1
7
7
  <% case activerecord_adapter -%>
8
8
  <% when "postgresql" -%>
@@ -1,5 +1,5 @@
1
1
  # Used by the disallow password reuse feature
2
- create_table :account_previous_password_hashes do |t|
3
- t.references :account, foreign_key: true<%= primary_key_type(:type) %>
2
+ create_table :<%= table_prefix %>_previous_password_hashes do |t|
3
+ t.references :<%= table_prefix %>, foreign_key: true<%= primary_key_type(:type) %>
4
4
  t.string :password_hash, null: false
5
5
  end
@@ -1,6 +1,7 @@
1
1
  # Used by the email auth feature
2
- create_table :account_email_auth_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_email_auth_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
4
5
  t.string :key, null: false
5
6
  t.datetime :deadline, null: false
6
7
  t.datetime :email_last_sent, null: false, default: <%= current_timestamp %>
@@ -1,7 +1,7 @@
1
1
  # Used by the jwt refresh feature
2
- create_table :account_jwt_refresh_keys<%= primary_key_type %> do |t|
3
- t.references :account, foreign_key: true, null: false<%= primary_key_type(:type) %>
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
4
  t.string :key, null: false
5
5
  t.datetime :deadline, null: false
6
- t.index :account_id, name: "account_jwt_rk_account_id_idx"
6
+ t.index :<%= table_prefix %>_id, name: "<%= table_prefix %>_jwt_rk_<%= table_prefix %>_id_idx"
7
7
  end
@@ -1,12 +1,12 @@
1
1
  # Used by the lockout feature
2
- create_table :account_login_failures, id: false do |t|
2
+ create_table :<%= table_prefix %>_login_failures, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.integer :number, null: false, default: 1
6
6
  end
7
- create_table :account_lockouts, id: false do |t|
7
+ create_table :<%= table_prefix %>_lockouts, id: false do |t|
8
8
  t.<%= primary_key_type(nil) %> :id, primary_key: true
9
- t.foreign_key :accounts, column: :id
9
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
10
10
  t.string :key, null: false
11
11
  t.datetime :deadline, null: false
12
12
  t.datetime :email_last_sent
@@ -1,7 +1,7 @@
1
1
  # Used by the otp feature
2
- create_table :account_otp_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_otp_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :key, null: false
6
6
  t.integer :num_failures, null: false, default: 0
7
7
  t.datetime :last_use, null: false, default: <%= current_timestamp %>
@@ -1,6 +1,6 @@
1
1
  # Used by the password expiration feature
2
- create_table :account_password_change_times, id: false do |t|
2
+ create_table :<%= table_prefix %>_password_change_times, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.datetime :changed_at, null: false, default: <%= current_timestamp %>
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # Used by the recovery codes feature
2
- create_table :account_recovery_codes, primary_key: [:id, :code] do |t|
2
+ create_table :<%= table_prefix %>_recovery_codes, primary_key: [:id, :code] do |t|
3
3
  t.<%= primary_key_type(nil) %> :id
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :code
6
6
  end
@@ -1,7 +1,7 @@
1
1
  # Used by the remember me feature
2
- create_table :account_remember_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_remember_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :key, null: false
6
6
  t.datetime :deadline, null: false
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # Used by the password reset feature
2
- create_table :account_password_reset_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_password_reset_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :key, null: false
6
6
  t.datetime :deadline, null: false
7
7
  t.datetime :email_last_sent, null: false, default: <%= current_timestamp %>
@@ -1,6 +1,6 @@
1
1
  # Used by the single session feature
2
- create_table :account_session_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_session_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :key, null: false
6
6
  end
@@ -1,7 +1,7 @@
1
1
  # Used by the sms codes feature
2
- create_table :account_sms_codes, id: false do |t|
2
+ create_table :<%= table_prefix %>_sms_codes, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :phone_number, null: false
6
6
  t.integer :num_failures
7
7
  t.string :code
@@ -1,7 +1,7 @@
1
1
  # Used by the account verification feature
2
- create_table :account_verification_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_verification_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :key, null: false
6
6
  t.datetime :requested_at, null: false, default: <%= current_timestamp %>
7
7
  t.datetime :email_last_sent, null: false, default: <%= current_timestamp %>
@@ -1,7 +1,7 @@
1
1
  # Used by the verify login change feature
2
- create_table :account_login_change_keys, id: false do |t|
2
+ create_table :<%= table_prefix %>_login_change_keys, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :key, null: false
6
6
  t.string :login, null: false
7
7
  t.datetime :deadline, null: false
@@ -1,11 +1,11 @@
1
1
  # Used by the webauthn feature
2
- create_table :account_webauthn_user_ids, id: false do |t|
2
+ create_table :<%= table_prefix %>_webauthn_user_ids, id: false do |t|
3
3
  t.<%= primary_key_type(nil) %> :id, primary_key: true
4
- t.foreign_key :accounts, column: :id
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
5
  t.string :webauthn_id, null: false
6
6
  end
7
- create_table :account_webauthn_keys, primary_key: [:account_id, :webauthn_id] do |t|
8
- t.references :account, foreign_key: true<%= primary_key_type(:type) %>
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
9
  t.string :webauthn_id
10
10
  t.string :public_key, null: false
11
11
  t.integer :sign_count, null: false
@@ -1,6 +1,6 @@
1
1
  # Used by the account expiration feature
2
- create_table :account_activity_times do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_activity_times do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  DateTime :last_activity_at, null: false
5
5
  DateTime :last_login_at, null: false
6
6
  DateTime :expired_at
@@ -1,8 +1,8 @@
1
1
  # Used by the active sessions feature
2
- create_table :account_active_session_keys do
3
- foreign_key :account_id, :accounts, type: :Bignum
2
+ create_table :<%= table_prefix %>_active_session_keys do
3
+ foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, type: :Bignum
4
4
  String :session_id
5
5
  Time :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
6
6
  Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
7
- primary_key [:account_id, :session_id]
7
+ primary_key [:<%= table_prefix %>_id, :session_id]
8
8
  end
@@ -1,7 +1,7 @@
1
1
  # Used by the audit logging feature
2
- create_table :account_authentication_audit_logs do
2
+ create_table :<%= table_prefix %>_authentication_audit_logs do
3
3
  primary_key :id, type: :Bignum
4
- foreign_key :account_id, :accounts, null: false, type: :Bignum
4
+ foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, null: false, type: :Bignum
5
5
  DateTime :at, null: false, default: Sequel::CURRENT_TIMESTAMP
6
6
  String :message, null: false
7
7
  <% case db.database_type -%>
@@ -12,6 +12,6 @@ create_table :account_authentication_audit_logs do
12
12
  <% else -%>
13
13
  String :metadata
14
14
  <% end -%>
15
- index [:account_id, :at], name: :audit_account_at_idx
15
+ index [:<%= table_prefix %>_id, :at], name: :audit_<%= table_prefix %>_at_idx
16
16
  index :at, name: :audit_at_idx
17
17
  end
@@ -5,7 +5,7 @@ rescue NoMethodError # migration is being reverted
5
5
  end
6
6
 
7
7
  <% end -%>
8
- create_table :accounts do
8
+ create_table :<%= table_prefix.pluralize %> do
9
9
  primary_key :id, type: :Bignum
10
10
  <% if db.database_type == :postgres -%>
11
11
  citext :email, null: false
@@ -1,6 +1,6 @@
1
1
  # Used by the disallow password reuse feature
2
- create_table :account_previous_password_hashes do
2
+ create_table :<%= table_prefix %>_previous_password_hashes do
3
3
  primary_key :id, type: :Bignum
4
- foreign_key :account_id, :accounts, type: :Bignum
4
+ foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, type: :Bignum
5
5
  String :password_hash, null: false
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # Used by the email auth feature
2
- create_table :account_email_auth_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_email_auth_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  DateTime :deadline, null: false
6
6
  DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
@@ -1,8 +1,8 @@
1
1
  # Used by the jwt refresh feature
2
- create_table :account_jwt_refresh_keys do
2
+ create_table :<%= table_prefix %>_jwt_refresh_keys do
3
3
  primary_key :id, type: :Bignum
4
- foreign_key :account_id, :accounts, null: false, type: :Bignum
4
+ foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, null: false, type: :Bignum
5
5
  String :key, null: false
6
6
  DateTime :deadline, null: false
7
- index :account_id, name: :account_jwt_rk_account_id_idx
7
+ index :<%= table_prefix %>_id, name: :<%= table_prefix %>_jwt_rk_<%= table_prefix %>_id_idx
8
8
  end
@@ -1,10 +1,10 @@
1
1
  # Used by the lockout feature
2
- create_table :account_login_failures do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_login_failures do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  Integer :number, null: false, default: 1
5
5
  end
6
- create_table :account_lockouts do
7
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
6
+ create_table :<%= table_prefix %>_lockouts do
7
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
8
8
  String :key, null: false
9
9
  DateTime :deadline, null: false
10
10
  DateTime :email_last_sent
@@ -1,6 +1,6 @@
1
1
  # Used by the otp feature
2
- create_table :account_otp_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_otp_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  Integer :num_failures, null: false, default: 0
6
6
  Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
@@ -1,5 +1,5 @@
1
1
  # Used by the password expiration feature
2
- create_table :account_password_change_times do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_password_change_times do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  DateTime :changed_at, null: false, default: Sequel::CURRENT_TIMESTAMP
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # Used by the recovery codes feature
2
- create_table :account_recovery_codes do
3
- foreign_key :id, :accounts, type: :Bignum
2
+ create_table :<%= table_prefix %>_recovery_codes do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, type: :Bignum
4
4
  String :code
5
5
  primary_key [:id, :code]
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # Used by the remember me feature
2
- create_table :account_remember_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_remember_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  DateTime :deadline, null: false
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # Used by the password reset feature
2
- create_table :account_password_reset_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_password_reset_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  DateTime :deadline, null: false
6
6
  DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
@@ -1,5 +1,5 @@
1
1
  # Used by the single session feature
2
- create_table :account_session_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_session_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # Used by the sms codes feature
2
- create_table :account_sms_codes do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_sms_codes do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :phone_number, null: false
5
5
  Integer :num_failures
6
6
  String :code
@@ -1,6 +1,6 @@
1
1
  # Used by the account verification feature
2
- create_table :account_verification_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_verification_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  DateTime :requested_at, null: false, default: Sequel::CURRENT_TIMESTAMP
6
6
  DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
@@ -1,6 +1,6 @@
1
1
  # Used by the verify login change feature
2
- create_table :account_login_change_keys do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_login_change_keys do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :key, null: false
5
5
  String :login, null: false
6
6
  DateTime :deadline, null: false
@@ -1,13 +1,13 @@
1
1
  # Used by the webauthn feature
2
- create_table :account_webauthn_user_ids do
3
- foreign_key :id, :accounts, primary_key: true, type: :Bignum
2
+ create_table :<%= table_prefix %>_webauthn_user_ids do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
4
  String :webauthn_id, null: false
5
5
  end
6
- create_table :account_webauthn_keys do
7
- foreign_key :account_id, :accounts, type: :Bignum
6
+ create_table :<%= table_prefix %>_webauthn_keys do
7
+ foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, type: :Bignum
8
8
  String :webauthn_id
9
9
  String :public_key, null: false
10
10
  Integer :sign_count, null: false
11
11
  Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
12
- primary_key [:account_id, :webauthn_id]
12
+ primary_key [:<%= table_prefix %>_id, :webauthn_id]
13
13
  end
@@ -13,6 +13,9 @@ module Rodauth
13
13
  desc: "Rodauth features to create tables for (otp, sms_codes, single_session, account_expiration etc.)",
14
14
  default: %w[]
15
15
 
16
+ class_option :prefix, optional: true, type: :string,
17
+ desc: "Change prefix for generated tables (default: account)"
18
+
16
19
  class_option :name, optional: true, type: :string,
17
20
  desc: "Name of the generated migration file"
18
21
 
@@ -22,10 +25,24 @@ module Rodauth
22
25
  migration_template "db/migrate/create_rodauth.rb", File.join(db_migrate_path, "#{migration_name}.rb")
23
26
  end
24
27
 
28
+ def show_instructions
29
+ # skip if called from install generator, it already adds configuration
30
+ return if current_command_chain.include?(:generate_rodauth_migration)
31
+ return unless options[:prefix] && behavior == :invoke
32
+
33
+ configuration = CONFIGURATION.values_at(*features.map(&:to_sym))
34
+ .flat_map(&:to_a)
35
+ .map { |config, format| "#{config} :#{format % { plural: table_prefix.pluralize, singular: table_prefix }}" }
36
+ .join("\n")
37
+ .indent(2)
38
+
39
+ say "\nAdd the following to your Rodauth configuration:\n\n#{configuration}"
40
+ end
41
+
25
42
  private
26
43
 
27
44
  def migration_name
28
- options[:name] || "create_rodauth_#{features.join("_")}"
45
+ options[:name] || ["create_rodauth", *options[:prefix], *features].join("_")
29
46
  end
30
47
 
31
48
  def migration_content
@@ -64,6 +81,31 @@ module Rodauth
64
81
  Dir["#{MIGRATION_DIR}/*.erb"].map { |filename| File.basename(filename, ".erb") }
65
82
  end
66
83
 
84
+ def table_prefix
85
+ options[:prefix]&.singularize || "account"
86
+ end
87
+
88
+ CONFIGURATION = {
89
+ base: { accounts_table: "%{plural}" },
90
+ remember: { remember_table: "%{singular}_remember_keys" },
91
+ verify_account: { verify_account_table: "%{singular}_verification_keys" },
92
+ verify_login_change: { verify_login_change_table: "%{singular}_login_change_keys" },
93
+ reset_password: { reset_password_table: "%{singular}_password_reset_keys" },
94
+ email_auth: { email_auth_table: "%{singular}_email_auth_keys" },
95
+ otp: { otp_keys_table: "%{singular}_otp_keys" },
96
+ sms_codes: { sms_codes_table: "%{singular}_sms_codes" },
97
+ recovery_codes: { recovery_codes_table: "%{singular}_recovery_codes" },
98
+ webauthn: { webauthn_keys_table: "%{singular}_webauthn_keys", webauthn_user_ids_table: "%{singular}_webauthn_user_ids", webauthn_keys_account_id_column: "%{singular}_id" },
99
+ lockout: { account_login_failures_table: "%{singular}_login_failures", account_lockouts_table: "%{singular}_lockouts" },
100
+ active_sessions: { active_sessions_table: "%{singular}_active_session_keys", active_sessions_account_id_column: "%{singular}_id" },
101
+ account_expiration: { account_activity_table: "%{singular}_activity_times" },
102
+ password_expiration: { password_expiration_table: "%{singular}_password_change_times" },
103
+ single_session: { single_session_table: "%{singular}_session_keys" },
104
+ audit_logging: { audit_logging_table: "%{singular}_authentication_audit_logs", audit_logging_account_id_column: "%{singular}_id" },
105
+ disallow_password_reuse: { previous_password_hash_table: "%{singular}_previous_password_hashes", previous_password_account_id_column: "%{singular}_id" },
106
+ jwt_refresh: { jwt_refresh_token_table: "%{singular}_jwt_refresh_keys", jwt_refresh_token_account_id_column: "%{singular}_id" },
107
+ }
108
+
67
109
  if defined?(::ActiveRecord::Railtie) # Active Record
68
110
  include ::ActiveRecord::Generators::Migration
69
111
 
@@ -1,16 +1,18 @@
1
1
  class RodauthMailer < ApplicationMailer
2
+ default to: -> { @rodauth.email_to }, from: -> { @rodauth.email_from }
3
+
2
4
  def verify_account(name, account_id, key)
3
5
  @rodauth = rodauth(name, account_id) { @verify_account_key_value = key }
4
6
  @account = @rodauth.rails_account
5
7
 
6
- mail to: @account.email, subject: @rodauth.verify_account_email_subject
8
+ mail subject: @rodauth.verify_account_email_subject
7
9
  end
8
10
 
9
11
  def reset_password(name, account_id, key)
10
12
  @rodauth = rodauth(name, account_id) { @reset_password_key_value = key }
11
13
  @account = @rodauth.rails_account
12
14
 
13
- mail to: @account.email, subject: @rodauth.reset_password_email_subject
15
+ mail subject: @rodauth.reset_password_email_subject
14
16
  end
15
17
 
16
18
  def verify_login_change(name, account_id, key)
@@ -25,28 +27,28 @@ class RodauthMailer < ApplicationMailer
25
27
  @rodauth = rodauth(name, account_id)
26
28
  @account = @rodauth.rails_account
27
29
 
28
- mail to: @account.email, subject: @rodauth.password_changed_email_subject
30
+ mail subject: @rodauth.password_changed_email_subject
29
31
  end
30
32
 
31
33
  # def reset_password_notify(name, account_id)
32
34
  # @rodauth = rodauth(name, account_id)
33
35
  # @account = @rodauth.rails_account
34
36
 
35
- # mail to: @account.email, subject: @rodauth.reset_password_notify_email_subject
37
+ # mail subject: @rodauth.reset_password_notify_email_subject
36
38
  # end
37
39
 
38
40
  # def email_auth(name, account_id, key)
39
41
  # @rodauth = rodauth(name, account_id) { @email_auth_key_value = key }
40
42
  # @account = @rodauth.rails_account
41
43
 
42
- # mail to: @account.email, subject: @rodauth.email_auth_email_subject
44
+ # mail subject: @rodauth.email_auth_email_subject
43
45
  # end
44
46
 
45
47
  # def unlock_account(name, account_id, key)
46
48
  # @rodauth = rodauth(name, account_id) { @unlock_account_key_value = key }
47
49
  # @account = @rodauth.rails_account
48
50
 
49
- # mail to: @account.email, subject: @rodauth.unlock_account_email_subject
51
+ # mail subject: @rodauth.unlock_account_email_subject
50
52
  # end
51
53
 
52
54
  private
@@ -1,18 +1,56 @@
1
+ require "sequel/core"
2
+
1
3
  class RodauthMain < Rodauth::Rails::Auth
2
4
  configure do
3
5
  # List of authentication features that are loaded.
4
6
  enable :create_account, :verify_account, :verify_account_grace_period,
5
7
  :login, :logout<%= ", :remember" unless jwt? %><%= ", :json" if json? %><%= ", :jwt" if jwt? %>,
6
8
  :reset_password, :change_password, :change_password_notify,
7
- :change_login, :verify_login_change, :close_account
9
+ :change_login, :verify_login_change, :close_account<%= ", :argon2" if argon2? %>
8
10
 
9
11
  # See the Rodauth documentation for the list of available config options:
10
12
  # http://rodauth.jeremyevans.net/documentation.html
11
13
 
12
14
  # ==> General
15
+ <% if sequel_activerecord_integration? -%>
16
+ # Initialize Sequel and have it reuse Active Record's database connection.
17
+ <% if RUBY_ENGINE == "jruby" -%>
18
+ db Sequel.connect("jdbc:<%= sequel_adapter %>://", extensions: :activerecord_connection, keep_reference: false)
19
+ <% else -%>
20
+ db Sequel.<%= sequel_adapter %>(extensions: :activerecord_connection, keep_reference: false)
21
+ <% end -%>
22
+
23
+ <% end -%>
24
+ # Change prefix of table and foreign key column names from default "account"
25
+ <% if table -%>
26
+ accounts_table :<%= table_prefix.pluralize %>
27
+ verify_account_table :<%= table_prefix %>_verification_keys
28
+ verify_login_change_table :<%= table_prefix %>_login_change_keys
29
+ reset_password_table :<%= table_prefix %>_password_reset_keys
30
+ <% unless jwt? -%>
31
+ remember_table :<%= table_prefix %>_remember_keys
32
+ <% end -%>
33
+ <% else -%>
34
+ # accounts_table :users
35
+ # verify_account_table :user_verification_keys
36
+ # verify_login_change_table :user_login_change_keys
37
+ # reset_password_table :user_password_reset_keys
38
+ <% unless jwt? -%>
39
+ # remember_table :user_remember_keys
40
+ <% end -%>
41
+ <% end -%>
42
+
13
43
  # The secret key used for hashing public-facing tokens for various features.
14
44
  # Defaults to Rails `secret_key_base`, but you can use your own secret key.
15
45
  # hmac_secret "<%= SecureRandom.hex(64) %>"
46
+ <% if argon2? -%>
47
+
48
+ # Use a rotatable password pepper when hashing passwords with Argon2.
49
+ # argon2_secret "<SECRET_KEY>"
50
+
51
+ # Since we're using argon2, prevent loading the bcrypt gem to save memory.
52
+ require_bcrypt? false
53
+ <% end -%>
16
54
  <% if jwt? -%>
17
55
 
18
56
  # Set JWT secret, which is used to cryptographically protect the token.
@@ -28,10 +66,13 @@ class RodauthMain < Rodauth::Rails::Auth
28
66
  # require_login_confirmation? false
29
67
  <% end -%>
30
68
 
31
- # Specify the controller used for view rendering and CSRF verification.
69
+ # Use path prefix for all routes.
70
+ # prefix "/auth"
71
+
72
+ # Specify the controller used for view rendering, CSRF, and callbacks.
32
73
  rails_controller { RodauthController }
33
74
 
34
- # Set on Rodauth controller with the title of the current page.
75
+ # Set in Rodauth controller instance with the title of the current page.
35
76
  title_instance_variable :@page_title
36
77
 
37
78
  # Store account status in an integer column without foreign key constraint.
@@ -40,14 +81,13 @@ class RodauthMain < Rodauth::Rails::Auth
40
81
  # Store password hash in a column instead of a separate table.
41
82
  account_password_hash_column :password_hash
42
83
 
43
- # Passwords shorter than 8 characters are considered weak according to OWASP.
44
- password_minimum_length 8
45
- # bcrypt has a maximum input length of 72 bytes, truncating any extra bytes.
46
- password_maximum_bytes 72
47
-
48
84
  # Set password when creating account instead of when verifying.
49
85
  verify_account_set_password? false
50
86
 
87
+ # Change some default param keys.
88
+ # login_param "email"
89
+ # password_confirm_param "confirm_password"
90
+
51
91
  # Redirect back to originally requested location after authentication.
52
92
  # login_return_to_requested_location? true
53
93
  # two_factor_auth_return_to_requested_location? true # if using MFA
@@ -110,8 +150,27 @@ class RodauthMain < Rodauth::Rails::Auth
110
150
  # password_too_short_message { "needs to have at least #{password_minimum_length} characters" }
111
151
  # login_does_not_meet_requirements_message { "invalid email#{", #{login_requirement_message}" if login_requirement_message}" }
112
152
 
113
- # Change minimum number of password characters required when creating an account.
114
- # password_minimum_length 8
153
+ # Passwords shorter than 8 characters are considered weak according to OWASP.
154
+ password_minimum_length 8
155
+ <% if argon2? -%>
156
+ # Having a maximum password length set prevents long password DoS attacks.
157
+ password_maximum_length 64
158
+ <% else -%>
159
+ # bcrypt has a maximum input length of 72 bytes, truncating any extra bytes.
160
+ password_maximum_bytes 72
161
+ <% end -%>
162
+
163
+ # Custom password complexity requirements (alternative to password_complexity feature).
164
+ # password_meets_requirements? do |password|
165
+ # super(password) && password_complex_enough?(password)
166
+ # end
167
+ # auth_class_eval do
168
+ # def password_complex_enough?(password)
169
+ # return true if password.match?(/\d/) && password.match?(/[^a-zA-Z\d]/)
170
+ # set_password_requirement_error_message(:password_simple, "requires one number and one special character")
171
+ # false
172
+ # end
173
+ # end
115
174
  <% unless jwt? -%>
116
175
 
117
176
  # ==> Remember Feature
@@ -1,5 +1,5 @@
1
1
  <% if defined?(ActiveRecord::Railtie) -%>
2
- class Account < ApplicationRecord
2
+ class <%= table_prefix.camelize %> < ApplicationRecord
3
3
  include Rodauth::Rails.model
4
4
  <% if ActiveRecord.version >= Gem::Version.new("7.0") -%>
5
5
  enum :status, unverified: 1, verified: 2, closed: 3
@@ -8,7 +8,7 @@ class Account < ApplicationRecord
8
8
  <% end -%>
9
9
  end
10
10
  <% else -%>
11
- class Account < Sequel::Model
11
+ class <%= table_prefix.camelize %> < Sequel::Model
12
12
  include Rodauth::Rails.model
13
13
  plugin :enum
14
14
  enum :status, unverified: 1, verified: 2, closed: 3
@@ -8,13 +8,13 @@
8
8
  <% end %>
9
9
 
10
10
  <fieldset class="mb-6">
11
- <% (usage = rodauth.account_webauthn_usage).each do |id, last_use| %>
11
+ <% rodauth.account_webauthn_usage.each do |id, last_use| %>
12
12
  <div class="flex items-center space-x-2">
13
13
  <%= form.radio_button rodauth.webauthn_remove_param, id, id: "webauthn-remove-#{id}", class: "dark:bg-gray-900 dark:border-gray-600 dark:checked:bg-current dark:checked:border-current dark:checked:text-emerald-400 dark:focus:ring-emerald-400 dark:focus:ring-offset-gray-900" %>
14
14
  <%= form.label "webauthn-remove-#{id}", "Last use: #{last_use}", class: "text-sm" %>
15
- <%= content_tag(:span, rodauth.field_error(rodauth.webauthn_remove_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "webauthn_remove_error_message") if rodauth.field_error(rodauth.webauthn_remove_param) && id == usage.keys.last %>
16
15
  </div>
17
16
  <% end %>
17
+ <%= content_tag(:span, rodauth.field_error(rodauth.webauthn_remove_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "webauthn_remove_error_message") if rodauth.field_error(rodauth.webauthn_remove_param) %>
18
18
  </fieldset>
19
19
 
20
20
  <%= 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" %>
@@ -1,10 +1,10 @@
1
1
  # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
2
  one:
3
3
  email: freddie@queen.com
4
- password_hash: <%%= BCrypt::Password.create("password", cost: BCrypt::Engine::MIN_COST) %>
4
+ password_hash: <%%= RodauthMain.allocate.password_hash("password") %>
5
5
  status: verified
6
6
 
7
7
  two:
8
8
  email: brian@queen.com
9
- password_hash: <%%= BCrypt::Password.create("password", cost: BCrypt::Engine::MIN_COST) %>
9
+ password_hash: <%%= RodauthMain.allocate.password_hash("password") %>
10
10
  status: verified
@@ -82,6 +82,25 @@ module Rodauth
82
82
  super
83
83
  end
84
84
  end
85
+
86
+ # The Rack input might not be rewindable, so ensure we parse the JSON
87
+ # request body in Rails, and avoid parsing it again in Roda.
88
+ def POST
89
+ if content_type =~ /json/
90
+ env["roda.json_params"] = scope.rails_request.POST.to_hash
91
+ end
92
+ super
93
+ end
94
+
95
+ unless ActionPack.version < Gem::Version.new("5.0")
96
+ # When calling a Rodauth method that redirects inside the Rails
97
+ # router, Roda's after hook that commits the flash would never get
98
+ # called, so we make sure to commit the flash beforehand.
99
+ def redirect(*)
100
+ scope.rails_request.commit_flash
101
+ super
102
+ end
103
+ end
85
104
  end
86
105
  end
87
106
  end
@@ -60,16 +60,6 @@ module Rodauth
60
60
 
61
61
  private
62
62
 
63
- unless ActionPack.version < Gem::Version.new("5.0")
64
- # When calling a Rodauth method that redirects inside the Rails
65
- # router, Roda's after hook that commits the flash would never get
66
- # called, so we make sure to commit the flash beforehand.
67
- def redirect(*)
68
- rails_request.commit_flash
69
- super
70
- end
71
- end
72
-
73
63
  def instantiate_rails_account
74
64
  if defined?(ActiveRecord::Base) && rails_account_model < ActiveRecord::Base
75
65
  rails_account_model.instantiate(account.stringify_keys)
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "1.7.1"
3
+ VERSION = "1.8.0"
4
4
  end
5
5
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.require_paths = ["lib"]
18
18
 
19
19
  spec.add_dependency "railties", ">= 4.2", "< 8"
20
- spec.add_dependency "rodauth", "~> 2.27"
20
+ spec.add_dependency "rodauth", "~> 2.28"
21
21
  spec.add_dependency "roda", "~> 3.55"
22
22
  spec.add_dependency "sequel-activerecord_connection", "~> 1.1"
23
23
  spec.add_dependency "rodauth-model", "~> 0.2"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.1
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-25 00:00:00.000000000 Z
11
+ date: 2023-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.27'
39
+ version: '2.28'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.27'
46
+ version: '2.28'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: roda
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -219,11 +219,11 @@ files:
219
219
  - lib/generators/rodauth/migration/sequel/webauthn.erb
220
220
  - lib/generators/rodauth/migration_generator.rb
221
221
  - lib/generators/rodauth/templates/INSTRUCTIONS
222
- - lib/generators/rodauth/templates/app/controllers/rodauth_controller.rb
223
- - lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb
224
- - lib/generators/rodauth/templates/app/misc/rodauth_app.rb
225
- - lib/generators/rodauth/templates/app/misc/rodauth_main.rb
226
- - lib/generators/rodauth/templates/app/models/account.rb
222
+ - lib/generators/rodauth/templates/app/controllers/rodauth_controller.rb.tt
223
+ - lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb.tt
224
+ - lib/generators/rodauth/templates/app/misc/rodauth_app.rb.tt
225
+ - lib/generators/rodauth/templates/app/misc/rodauth_main.rb.tt
226
+ - lib/generators/rodauth/templates/app/models/account.rb.tt
227
227
  - lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb
228
228
  - lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb
229
229
  - lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb
@@ -307,10 +307,9 @@ files:
307
307
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb
308
308
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb
309
309
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb
310
- - lib/generators/rodauth/templates/config/initializers/rodauth.rb
311
- - lib/generators/rodauth/templates/config/initializers/sequel.rb
312
- - lib/generators/rodauth/templates/db/migrate/create_rodauth.rb
313
- - lib/generators/rodauth/templates/test/fixtures/accounts.yml
310
+ - lib/generators/rodauth/templates/config/initializers/rodauth.rb.tt
311
+ - lib/generators/rodauth/templates/db/migrate/create_rodauth.rb.tt
312
+ - lib/generators/rodauth/templates/test/fixtures/accounts.yml.tt
314
313
  - lib/generators/rodauth/views_generator.rb
315
314
  - lib/rodauth-rails.rb
316
315
  - lib/rodauth/rails.rb
@@ -352,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
352
351
  - !ruby/object:Gem::Version
353
352
  version: '0'
354
353
  requirements: []
355
- rubygems_version: 3.4.4
354
+ rubygems_version: 3.4.6
356
355
  signing_key:
357
356
  specification_version: 4
358
357
  summary: Provides Rails integration for Rodauth.
@@ -1,4 +0,0 @@
1
- require "sequel/core"
2
-
3
- # initialize Sequel and have it reuse Active Record's database connection
4
- DB = Sequel.connect("<%= sequel_uri_scheme %>://", extensions: :activerecord_connection)