rodauth-rails 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/LICENSE.txt +1 -1
- data/README.md +50 -10
- data/lib/generators/rodauth/install_generator.rb +35 -34
- data/lib/generators/rodauth/migration/active_record/account_expiration.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/active_sessions.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/audit_logging.erb +3 -3
- data/lib/generators/rodauth/migration/active_record/base.erb +1 -1
- data/lib/generators/rodauth/migration/active_record/disallow_password_reuse.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/email_auth.erb +2 -1
- data/lib/generators/rodauth/migration/active_record/jwt_refresh.erb +3 -3
- data/lib/generators/rodauth/migration/active_record/lockout.erb +4 -4
- data/lib/generators/rodauth/migration/active_record/otp.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/password_expiration.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/recovery_codes.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/remember.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/reset_password.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/single_session.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/sms_codes.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/verify_account.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/verify_login_change.erb +2 -2
- data/lib/generators/rodauth/migration/active_record/webauthn.erb +4 -4
- data/lib/generators/rodauth/migration/sequel/account_expiration.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/active_sessions.erb +3 -3
- data/lib/generators/rodauth/migration/sequel/audit_logging.erb +3 -3
- data/lib/generators/rodauth/migration/sequel/base.erb +1 -1
- data/lib/generators/rodauth/migration/sequel/disallow_password_reuse.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/email_auth.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/jwt_refresh.erb +3 -3
- data/lib/generators/rodauth/migration/sequel/lockout.erb +4 -4
- data/lib/generators/rodauth/migration/sequel/otp.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/password_expiration.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/recovery_codes.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/remember.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/reset_password.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/single_session.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/sms_codes.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/verify_account.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/verify_login_change.erb +2 -2
- data/lib/generators/rodauth/migration/sequel/webauthn.erb +5 -5
- data/lib/generators/rodauth/migration_generator.rb +43 -1
- data/lib/generators/rodauth/templates/app/mailers/{rodauth_mailer.rb → rodauth_mailer.rb.tt} +8 -6
- data/lib/generators/rodauth/templates/app/misc/{rodauth_main.rb → rodauth_main.rb.tt} +69 -10
- data/lib/generators/rodauth/templates/app/models/{account.rb → account.rb.tt} +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_auth.html.erb +13 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb +21 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_setup.html.erb +21 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +4 -4
- data/lib/generators/rodauth/templates/test/fixtures/{accounts.yml → accounts.yml.tt} +2 -2
- data/lib/rodauth/rails/app.rb +19 -0
- data/lib/rodauth/rails/feature/base.rb +0 -10
- data/lib/rodauth/rails/feature/email.rb +2 -2
- data/lib/rodauth/rails/feature/internal_request.rb +1 -1
- data/lib/rodauth/rails/version.rb +1 -1
- data/rodauth-rails.gemspec +1 -1
- metadata +16 -14
- data/lib/generators/rodauth/templates/config/initializers/sequel.rb +0 -4
- /data/lib/generators/rodauth/templates/app/controllers/{rodauth_controller.rb → rodauth_controller.rb.tt} +0 -0
- /data/lib/generators/rodauth/templates/app/misc/{rodauth_app.rb → rodauth_app.rb.tt} +0 -0
- /data/lib/generators/rodauth/templates/config/initializers/{rodauth.rb → rodauth.rb.tt} +0 -0
- /data/lib/generators/rodauth/templates/db/migrate/{create_rodauth.rb → create_rodauth.rb.tt} +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
# Used by the remember me feature
|
2
|
-
create_table
|
3
|
-
foreign_key :id,
|
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
|
3
|
-
foreign_key :id,
|
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
|
3
|
-
foreign_key :id,
|
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
|
3
|
-
foreign_key :id,
|
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
|
3
|
-
foreign_key :id,
|
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
|
3
|
-
foreign_key :id,
|
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
|
3
|
-
foreign_key :id,
|
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
|
7
|
-
foreign_key
|
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 [
|
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] || "
|
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
|
|
data/lib/generators/rodauth/templates/app/mailers/{rodauth_mailer.rb → rodauth_mailer.rb.tt}
RENAMED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
#
|
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
|
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
|
-
#
|
114
|
-
|
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
|
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
|
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
|
@@ -0,0 +1,13 @@
|
|
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
|
+
<div id="webauthn-auth-button">
|
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
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<%= javascript_include_tag rodauth.webauthn_auth_js_path, extname: false %>
|
@@ -0,0 +1,21 @@
|
|
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
|
+
<div class="mb-6">
|
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
|
+
</div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<fieldset class="mb-6">
|
11
|
+
<% rodauth.account_webauthn_usage.each do |id, last_use| %>
|
12
|
+
<div class="flex items-center space-x-2">
|
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
|
+
<%= form.label "webauthn-remove-#{id}", "Last use: #{last_use}", class: "text-sm" %>
|
15
|
+
</div>
|
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
|
+
</fieldset>
|
19
|
+
|
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" %>
|
21
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
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
|
+
<div class="mb-6">
|
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
|
+
</div>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<div id="webauthn-setup-button">
|
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
|
+
</div>
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<%= javascript_include_tag rodauth.webauthn_setup_js_path, extname: false %>
|
@@ -1,10 +1,10 @@
|
|
1
|
-
<% cred = rodauth.
|
1
|
+
<% cred = rodauth.webauthn_credential_options_for_get %>
|
2
2
|
|
3
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
4
|
<%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
|
5
5
|
<%= form.hidden_field rodauth.webauthn_auth_challenge_param, value: cred.challenge %>
|
6
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", aria: { hidden: "true" } %>
|
7
|
+
<%= form.text_field rodauth.webauthn_auth_param, value: "", id: "webauthn-auth", class: "d-none", aria: { hidden: "true" } %>
|
8
8
|
<div id="webauthn-auth-button">
|
9
9
|
<div class="form-group mb-3">
|
10
10
|
<%= form.submit rodauth.webauthn_auth_button, class: "btn btn-primary" %>
|
@@ -12,4 +12,4 @@
|
|
12
12
|
</div>
|
13
13
|
<% end %>
|
14
14
|
|
15
|
-
<%= javascript_include_tag rodauth.webauthn_auth_js_path %>
|
15
|
+
<%= javascript_include_tag rodauth.webauthn_auth_js_path, extname: false %>
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<% cred = rodauth.new_webauthn_credential %>
|
2
2
|
|
3
|
-
<%= form_with url:
|
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
4
|
<%= form.hidden_field rodauth.webauthn_setup_challenge_param, value: cred.challenge %>
|
5
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", aria: { hidden: "true" } %>
|
6
|
+
<%= form.text_field rodauth.webauthn_setup_param, value: "", id: "webauthn-setup", class: "d-none", aria: { hidden: "true" } %>
|
7
7
|
|
8
8
|
<% if rodauth.two_factor_modifications_require_password? %>
|
9
9
|
<div class="form-group mb-3">
|
@@ -13,11 +13,11 @@
|
|
13
13
|
</div>
|
14
14
|
<% end %>
|
15
15
|
|
16
|
-
<div id="webauthn-setup-button">
|
16
|
+
<div id="webauthn-setup-button">
|
17
17
|
<div class="form-group mb-3">
|
18
18
|
<%= form.submit rodauth.webauthn_setup_button, class: "btn btn-primary" %>
|
19
19
|
</div>
|
20
20
|
</div>
|
21
21
|
<% end %>
|
22
22
|
|
23
|
-
<%= javascript_include_tag rodauth.webauthn_setup_js_path %>
|
23
|
+
<%= javascript_include_tag rodauth.webauthn_setup_js_path, extname: false %>
|
@@ -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: <%%=
|
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: <%%=
|
9
|
+
password_hash: <%%= RodauthMain.allocate.password_hash("password") %>
|
10
10
|
status: verified
|
data/lib/rodauth/rails/app.rb
CHANGED
@@ -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)
|
@@ -41,7 +41,7 @@ module Rodauth
|
|
41
41
|
# Checks whether we're in an internal request and host was not set,
|
42
42
|
# or the request doesn't exist such as with path_class_methods feature.
|
43
43
|
def missing_host?
|
44
|
-
internal_request? && request.host == INVALID_DOMAIN || scope.nil?
|
44
|
+
internal_request? && (request.host.nil? || request.host == INVALID_DOMAIN) || scope.nil?
|
45
45
|
end
|
46
46
|
|
47
47
|
def rails_url_options
|
data/rodauth-rails.gemspec
CHANGED
@@ -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.
|
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.
|
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:
|
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.
|
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.
|
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
|
@@ -286,6 +286,9 @@ files:
|
|
286
286
|
- lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account.html.erb
|
287
287
|
- lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account_resend.html.erb
|
288
288
|
- lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_login_change.html.erb
|
289
|
+
- lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_auth.html.erb
|
290
|
+
- lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb
|
291
|
+
- lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_setup.html.erb
|
289
292
|
- lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb
|
290
293
|
- lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb
|
291
294
|
- lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb
|
@@ -304,10 +307,9 @@ files:
|
|
304
307
|
- lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb
|
305
308
|
- lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb
|
306
309
|
- lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb
|
307
|
-
- lib/generators/rodauth/templates/config/initializers/rodauth.rb
|
308
|
-
- lib/generators/rodauth/templates/
|
309
|
-
- lib/generators/rodauth/templates/
|
310
|
-
- 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
|
311
313
|
- lib/generators/rodauth/views_generator.rb
|
312
314
|
- lib/rodauth-rails.rb
|
313
315
|
- lib/rodauth/rails.rb
|
@@ -349,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
349
351
|
- !ruby/object:Gem::Version
|
350
352
|
version: '0'
|
351
353
|
requirements: []
|
352
|
-
rubygems_version: 3.
|
354
|
+
rubygems_version: 3.4.6
|
353
355
|
signing_key:
|
354
356
|
specification_version: 4
|
355
357
|
summary: Provides Rails integration for Rodauth.
|
File without changes
|