rodauth-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ require "rails/generators/base"
2
+ require "rodauth/version"
3
+
4
+ module Rodauth
5
+ module Rails
6
+ module Generators
7
+ class MailerGenerator < ::Rails::Generators::Base
8
+ source_root "#{__dir__}/templates"
9
+ namespace "rodauth:mailer"
10
+
11
+ VIEWS = %w[
12
+ email_auth
13
+ password_changed
14
+ reset_password
15
+ unlock_account
16
+ verify_account
17
+ verify_login_change
18
+ ]
19
+
20
+ class_option :name,
21
+ desc: "The name for the mailer and the views directory",
22
+ default: "rodauth"
23
+
24
+ def copy_mailer
25
+ template "app/mailers/rodauth_mailer.rb",
26
+ "app/mailers/#{options[:name].underscore}_mailer.rb"
27
+ end
28
+
29
+ def copy_mailer_views
30
+ VIEWS.each do |view|
31
+ template "app/views/rodauth_mailer/#{view}.text.erb",
32
+ "app/views/#{options[:name].underscore}_mailer/#{view}.text.erb"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ class RodauthController < ApplicationController
2
+ # used by Rodauth for rendering views and CSRF protection
3
+ end
@@ -0,0 +1,37 @@
1
+ class <%= options[:name].camelize %>Mailer < ApplicationMailer
2
+ def verify_account(recipient, email_link)
3
+ @email_link = email_link
4
+
5
+ mail to: recipient
6
+ end
7
+
8
+ def reset_password(recipient, email_link)
9
+ @email_link = email_link
10
+
11
+ mail to: recipient
12
+ end
13
+
14
+ def verify_login_change(recipient, old_login, new_login, email_link)
15
+ @old_login = old_login
16
+ @new_login = new_login
17
+ @email_link = email_link
18
+
19
+ mail to: recipient
20
+ end
21
+
22
+ def password_changed(recipient)
23
+ mail to: recipient
24
+ end
25
+
26
+ # def email_auth(recipient, email_link)
27
+ # @email_link = email_link
28
+
29
+ # mail to: recipient
30
+ # end
31
+
32
+ # def unlock_account(recipient, email_link)
33
+ # @email_link = email_link
34
+
35
+ # mail to: recipient
36
+ # end
37
+ end
@@ -0,0 +1,2 @@
1
+ class Account < ApplicationRecord
2
+ end
@@ -0,0 +1,3 @@
1
+ Rodauth::Rails.configure do |config|
2
+ config.app = "RodauthApp"
3
+ end
@@ -0,0 +1,13 @@
1
+ require "sequel/core"
2
+
3
+ # initialize the appropriate Sequel adapter without creating a connection
4
+ <% case adapter -%>
5
+ <% when "postgresql" -%>
6
+ DB = Sequel.postgres(test: false)
7
+ <% when "mysql2" -%>
8
+ DB = Sequel.mysql2(test: false)
9
+ <% when "sqlite3" -%>
10
+ DB = Sequel.sqlite(test: false)
11
+ <% end -%>
12
+ # have Sequel use ActiveRecord's connection for database interaction
13
+ DB.extension :activerecord_connection
@@ -0,0 +1,170 @@
1
+ class CreateRodauth < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ <% if adapter == "postgresql" -%>
4
+ enable_extension "citext"
5
+
6
+ <% end -%>
7
+ create_table :accounts do |t|
8
+ <% case adapter -%>
9
+ <% when "postgresql" -%>
10
+ t.citext :email, null: false, index: { unique: true, where: "status IN ('verified', 'unverified')" }
11
+ <% else -%>
12
+ t.string :email, null: false, index: { unique: true }
13
+ <% end -%>
14
+ t.string :status, null: false, default: "verified"
15
+ end
16
+
17
+ # Used if storing password hashes in a separate table (default)
18
+ create_table :account_password_hashes do |t|
19
+ t.foreign_key :accounts, column: :id
20
+ t.string :password_hash, null: false
21
+ end
22
+
23
+ # Used by the password reset feature
24
+ create_table :account_password_reset_keys do |t|
25
+ t.foreign_key :accounts, column: :id
26
+ t.string :key, null: false
27
+ t.datetime :deadline, null: false
28
+ t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
29
+ end
30
+
31
+ # Used by the account verification feature
32
+ create_table :account_verification_keys do |t|
33
+ t.foreign_key :accounts, column: :id
34
+ t.string :key, null: false
35
+ t.datetime :requested_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
36
+ t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
37
+ end
38
+
39
+ # Used by the verify login change feature
40
+ create_table :account_login_change_keys do |t|
41
+ t.foreign_key :accounts, column: :id
42
+ t.string :key, null: false
43
+ t.string :login, null: false
44
+ t.datetime :deadline, null: false
45
+ end
46
+
47
+ # Used by the remember me feature
48
+ create_table :account_remember_keys do |t|
49
+ t.foreign_key :accounts, column: :id
50
+ t.string :key, null: false
51
+ t.datetime :deadline, null: false
52
+ end
53
+
54
+ # # Used by the audit logging feature
55
+ # create_table :account_authentication_audit_logs do |t|
56
+ # t.references :account, null: false
57
+ # t.datetime :at, null: false, default: -> { "CURRENT_TIMESTAMP" }
58
+ # t.text :message, null: false
59
+ <% case adapter -%>
60
+ <% when "postgresql" -%>
61
+ # t.jsonb :metadata
62
+ <% when "sqlite3", "mysql2" -%>
63
+ # t.json :metadata
64
+ <% else -%>
65
+ # t.string :metadata
66
+ <% end -%>
67
+ # t.index [:account_id, :at], name: "audit_account_at_idx"
68
+ # t.index :at, name: "audit_at_idx"
69
+ # end
70
+
71
+ # # Used by the jwt refresh feature
72
+ # create_table :account_jwt_refresh_keys do |t|
73
+ # t.references :account, null: false
74
+ # t.string :key, null: false
75
+ # t.datetime :deadline, null: false
76
+ # t.index :account_id, name: "account_jwt_rk_account_id_idx"
77
+ # end
78
+
79
+ # # Used by the disallow_password_reuse feature
80
+ # create_table :account_previous_password_hashes do |t|
81
+ # t.references :account
82
+ # t.string :password_hash, null: false
83
+ # end
84
+
85
+ # # Used by the lockout feature
86
+ # create_table :account_login_failures do |t|
87
+ # t.foreign_key :accounts, column: :id
88
+ # t.integer :number, null: false, default: 1
89
+ # end
90
+ # create_table :account_lockouts do |t|
91
+ # t.foreign_key :accounts, column: :id
92
+ # t.string :key, null: false
93
+ # t.datetime :deadline, null: false
94
+ # t.datetime :email_last_sent
95
+ # end
96
+
97
+ # # Used by the email auth feature
98
+ # create_table :account_email_auth_keys do |t|
99
+ # t.foreign_key :accounts, column: :id
100
+ # t.string :key, null: false
101
+ # t.datetime :deadline, null: false
102
+ # t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
103
+ # end
104
+
105
+ # # Used by the password expiration feature
106
+ # create_table :account_password_change_times do |t|
107
+ # t.foreign_key :accounts, column: :id
108
+ # t.datetime :changed_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
109
+ # end
110
+
111
+ # # Used by the account expiration feature
112
+ # create_table :account_activity_times do |t|
113
+ # t.foreign_key :accounts, column: :id
114
+ # t.datetime :last_activity_at, null: false
115
+ # t.datetime :last_login_at, null: false
116
+ # t.datetime :expired_at
117
+ # end
118
+
119
+ # # Used by the single session feature
120
+ # create_table :account_session_keys do |t|
121
+ # t.foreign_key :accounts, column: :id
122
+ # t.string :key, null: false
123
+ # end
124
+
125
+ # # Used by the active sessions feature
126
+ # create_table :account_active_session_keys, primary_key: [:account_id, :session_id] do |t|
127
+ # t.references :account
128
+ # t.string :session_id
129
+ # t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
130
+ # t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
131
+ # end
132
+
133
+ # # Used by the webauthn feature
134
+ # create_table :account_webauthn_user_ids do |t|
135
+ # t.foreign_key :accounts, column: :id
136
+ # t.string :webauthn_id, null: false
137
+ # end
138
+ # create_table :account_webauthn_keys, primary_key: [:account_id, :webauthn_id] do |t|
139
+ # t.references :account
140
+ # t.string :webauthn_id
141
+ # t.string :public_key, null: false
142
+ # t.integer :sign_count, null: false
143
+ # t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
144
+ # end
145
+
146
+ # # Used by the otp feature
147
+ # create_table :account_otp_keys do |t|
148
+ # t.foreign_key :accounts, column: :id
149
+ # t.string :key, null: false
150
+ # t.integer :num_failures, null: false, default: 0
151
+ # t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
152
+ # end
153
+
154
+ # # Used by the recovery codes feature
155
+ # create_table :account_recovery_codes, primary_key: [:id, :code] do |t|
156
+ # t.integer :id
157
+ # t.foreign_key :accounts, column: :id
158
+ # t.string :code
159
+ # end
160
+
161
+ # # Used by the sms codes feature
162
+ # create_table :account_sms_codes do |t|
163
+ # t.foreign_key :accounts, column: :id
164
+ # t.string :phone_number, null: false
165
+ # t.integer :num_failures
166
+ # t.string :code
167
+ # t.datetime :code_issued_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
168
+ # end
169
+ end
170
+ end
@@ -0,0 +1,186 @@
1
+ class RodauthApp < Rodauth::Rails::App
2
+ configure do
3
+ # List of authentication features that are loaded.
4
+ enable :create_account, :verify_account, :verify_account_grace_period,
5
+ :login, :remember, :logout,
6
+ :reset_password, :change_password, :change_password_notify,
7
+ :change_login, :verify_login_change,
8
+ :close_account
9
+
10
+ # See the Rodauth documentation for the list of available config options:
11
+ # http://rodauth.jeremyevans.net/documentation.html
12
+
13
+ # ==> General
14
+ # Specify the controller used for view rendering and CSRF verification.
15
+ rails_controller { RodauthController }
16
+
17
+ # Store account status in a text column.
18
+ account_status_column :status
19
+ account_unverified_status_value "unverified"
20
+ account_open_status_value "verified"
21
+ account_closed_status_value "closed"
22
+
23
+ # Store password hash in a column instead of a separate table.
24
+ # account_password_hash_column :password_digest
25
+
26
+ # Set password when creating account instead of when verifying.
27
+ verify_account_set_password? false
28
+
29
+ # Redirect back to originally requested location after authentication.
30
+ # login_return_to_requested_location? true
31
+ # two_factor_auth_return_to_requested_location? true # if using MFA
32
+
33
+ # Autologin the user after they have reset their password.
34
+ # reset_password_autologin? true
35
+
36
+ # Delete the account record when the user has closed their account.
37
+ # delete_account_on_close? true
38
+
39
+ # Redirect to the app from login and registration pages if already logged in.
40
+ # already_logged_in { redirect login_redirect }
41
+
42
+ # ==> Emails
43
+ # Uncomment the lines below once you've imported mailer views.
44
+ # send_reset_password_email do
45
+ # RodauthMailer.reset_password(email_to, password_reset_email_link).deliver_now
46
+ # end
47
+ # send_verify_account_email do
48
+ # RodauthMailer.verify_account(email_to, verify_account_email_link).deliver_now
49
+ # end
50
+ # send_verify_login_change_email do |login|
51
+ # RodauthMailer.verify_login_change(login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link).deliver_now
52
+ # end
53
+ # send_password_changed_email do
54
+ # RodauthMailer.password_changed(email_to).deliver_now
55
+ # end
56
+ # # send_email_auth_email do
57
+ # # RodauthMailer.email_auth(email_to, email_auth_email_link).deliver_now
58
+ # # end
59
+ # # send_unlock_account_email do
60
+ <% if Rodauth::MAJOR == 1 -%>
61
+ # # @unlock_account_key_value = get_unlock_account_key
62
+ <% end -%>
63
+ # # RodauthMailer.unlock_account(email_to, unlock_account_email_link).deliver_now
64
+ # # end
65
+
66
+ # In the meantime you can tweak settings for emails created by Rodauth
67
+ # email_subject_prefix "[MyApp] "
68
+ # email_from "noreply@myapp.com"
69
+ # send_email(&:deliver_later)
70
+ # reset_password_email_body { "Click here to reset your password: #{reset_password_email_link}" }
71
+
72
+ # ==> Flash
73
+ # Match flash keys with ones already used in the Rails app.
74
+ # flash_notice_key :success # default is :notice
75
+ # flash_error_key :error # default is :alert
76
+
77
+ # Override default flash messages.
78
+ # create_account_notice_flash "Your account has been created. Please verify your account by visiting the confirmation link sent to your email address."
79
+ # login_error_flash "Login is required for accessing this page"
80
+ # login_notice_flash nil
81
+
82
+ # ==> Validation
83
+ # Override default validation error messages.
84
+ # no_matching_login_message "user with this email address doesn't exist"
85
+ # already_an_account_with_this_login_message "user with this email address already exists"
86
+ # password_too_short_message { "needs to have at least #{password_minimum_length} characters" }
87
+ # login_does_not_meet_requirements_message { "invalid email#{", #{login_requirement_message}" if login_requirement_message}" }
88
+
89
+ # Change minimum number of password characters required when creating an account.
90
+ # password_minimum_length 8
91
+
92
+ # ==> Remember Feature
93
+ # Remember all logged in users.
94
+ after_login { remember_login }
95
+
96
+ # Or only remember users that have ticked a "Remember Me" checkbox on login.
97
+ # after_login { remember_login if param_or_nil("remember") }
98
+
99
+ # Extend user's remember period when remembered via a cookie
100
+ extend_remember_deadline? true
101
+
102
+ # Consider remembered users to be multifactor-authenticated (if using MFA).
103
+ # after_load_memory { two_factor_update_session("totp") if two_factor_authentication_setup? }
104
+
105
+ # ==> Hooks
106
+ # Validate custom fields in the create account form.
107
+ # before_create_account do
108
+ # throw_error_status(422, "name", "must be present") if param("name").empty?
109
+ # end
110
+
111
+ # Perform additional actions after the account is created.
112
+ # after_create_account do
113
+ # Profile.create!(account_id: account[:id], name: param("name"))
114
+ # end
115
+
116
+ # Do additional cleanup after the account is closed.
117
+ # after_close_account do
118
+ # Profile.find_by!(account_id: account[:id]).destroy
119
+ # end
120
+
121
+ # ==> Redirects
122
+ # Redirect to home page after logout.
123
+ logout_redirect "/"
124
+
125
+ # Redirect to wherever login redirects to after account verification.
126
+ verify_account_redirect { login_redirect }
127
+
128
+ # Redirect to login page after password reset.
129
+ reset_password_redirect { login_path }
130
+
131
+ # ==> Deadlines
132
+ # Change default deadlines for some actions.
133
+ # verify_account_grace_period 3.days
134
+ # reset_password_deadline_interval Hash[hours: 6]
135
+ # verify_login_change_deadline_interval Hash[days: 2]
136
+ # remember_deadline_interval Hash[days: 30]
137
+
138
+ # ==> Extending
139
+ # Define any additional methods you want for the Rodauth object.
140
+ # auth_class_eval do
141
+ # def my_send_email(name, *args)
142
+ # AuthenticationMailer.public_send(name, *args).deliver_later
143
+ # end
144
+ # end
145
+ #
146
+ # Then use the new custom method in configuration blocks.
147
+ # send_password_reset_email do
148
+ # my_send_email(:password_reset, email_to, password_reset_email_link)
149
+ # end
150
+ end
151
+
152
+ # ==> Multiple configurations
153
+ # configure(:admin) do
154
+ # enable :http_basic_auth
155
+ #
156
+ # prefix "/admin"
157
+ # session_key :admin_id
158
+ # end
159
+
160
+ route do |r|
161
+ rodauth.load_memory # autologin remembered users
162
+
163
+ r.rodauth # route rodauth requests
164
+
165
+ # ==> Authenticating Requests
166
+ # Call `rodauth.require_authentication` for requests that you want to
167
+ # require authentication for. Some examples:
168
+ #
169
+ # next if r.path.start_with?("/docs") # skip authentication for documentation pages
170
+ # next if session[:admin] # skip authentication for admins
171
+ #
172
+ # # authenticate /dashboard/* and /account/* requests
173
+ # if r.path.start_with?("/dashboard") || r.path.start_with?("/account")
174
+ # rodauth.require_authentication
175
+ # end
176
+
177
+ # ==> Multiple configurations
178
+ # r.on "admin" do
179
+ # r.rodauth(:admin)
180
+ #
181
+ # unless rodauth(:admin).logged_in?
182
+ # rodauth(:admin).require_http_basic_auth
183
+ # end
184
+ # end
185
+ end
186
+ end
@@ -0,0 +1,123 @@
1
+ require "rails/generators/base"
2
+ require "rodauth/version"
3
+
4
+ module Rodauth
5
+ module Rails
6
+ module Generators
7
+ class ViewsGenerator < ::Rails::Generators::Base
8
+ source_root "#{__dir__}/templates"
9
+ namespace "rodauth:views"
10
+
11
+ VIEWS = {
12
+ login: %w[
13
+ _field _field_error _login_field _login_display _password_field
14
+ _submit _login_form _login_form_footer _login_form_header login
15
+ multi_phase_login
16
+ ],
17
+ create_account: %w[
18
+ _field _field_error _login_field _login_confirm_field
19
+ _password_field _password_confirm_field _submit create_account
20
+ ],
21
+ logout: %w[
22
+ _submit logout
23
+ ],
24
+ reset_password: %w[
25
+ _field _field_error _login_field _login_hidden_field
26
+ _password_field _password_confirm_field _submit
27
+ reset_password_request reset_password
28
+ ],
29
+ remember: %w[
30
+ _submit remember
31
+ ],
32
+ change_login: %w[
33
+ _field _field_error _login_field _login_confirm_field
34
+ _password_field _submit change_login
35
+ ],
36
+ change_password: %w[
37
+ _field _field_error _password_field _new_password_field
38
+ _password_confirm_field _submit change_password
39
+ ],
40
+ close_account: %w[
41
+ _field _field_error _password_field _submit close_account
42
+ ],
43
+ email_auth: %w[
44
+ _login_hidden_field _submit _email_auth_request_form email_auth
45
+ ],
46
+ verify_account: %w[
47
+ _field _field_error _login_hidden_field _login_field _submit
48
+ verify_account_resend verify_account
49
+ ],
50
+ lockout: %w[
51
+ _login_hidden_field _submit unlock_account_request unlock_account
52
+ ],
53
+ active_sessions: %w[
54
+ _global_logout_field
55
+ ],
56
+ two_factor_base: %w[
57
+ _field _field_error _password_field _submit
58
+ two_factor_manage two_factor_auth two_factor_disable
59
+ ],
60
+ otp: %w[
61
+ _field _field_error _otp_auth_code_field _password_field _submit
62
+ otp_setup otp_auth otp_disable
63
+ ],
64
+ sms_codes: %w[
65
+ _field _field_error _sms_code_field _sms_phone_field
66
+ _password_field _submit
67
+ sms_setup sms_confirm sms_auth sms_request sms_disable
68
+ ],
69
+ recovery_codes: %w[
70
+ _field _field_error _recovery_code_field _recovery_codes_form
71
+ recovery_codes add_recovery_codes recovery_auth
72
+ ],
73
+ webauthn: %w[
74
+ _field _field_error _login_hidden_field _password_field _submit
75
+ webauthn_setup webauthn_auth webauthn_remove
76
+ ]
77
+ }
78
+
79
+ DEPENDENCIES = {
80
+ active_sessions: :logout,
81
+ otp: :two_factor_base,
82
+ sms_codes: :two_factor_base,
83
+ recovery_codes: :two_factor_base,
84
+ webauthn: :two_factor_base,
85
+ }
86
+
87
+ class_option :features, type: :array,
88
+ desc: "Rodauth features to generate views for (login, create_account, reset_password, verify_account etc.)",
89
+ default: %w[login logout create_account verify_account reset_password change_password change_login verify_login_change close_account]
90
+
91
+ class_option :all, aliases: "-a", type: :boolean,
92
+ desc: "Generates views for all Rodauth features",
93
+ default: false
94
+
95
+ class_option :directory, aliases: "-d", type: :string,
96
+ desc: "The directory under app/views/* into which to create views",
97
+ default: "rodauth"
98
+
99
+ def create_views
100
+ features = options[:all] ? VIEWS.keys : options[:features].map(&:to_sym)
101
+
102
+ views = features.inject([]) do |list, feature|
103
+ list |= VIEWS[feature] || []
104
+ list |= VIEWS[DEPENDENCIES[feature]] || []
105
+ end
106
+
107
+ if Rodauth::MAJOR == 1
108
+ views -= %w[
109
+ multi_phase_login _global_logout_field
110
+ two_factor_manage two_factor_auth two_factor_disable
111
+ webauthn_setup webauthn_auth webauthn_remove
112
+ ]
113
+ end
114
+
115
+ views.each do |view|
116
+ template "app/views/rodauth/#{view}.html.erb",
117
+ "app/views/#{options[:directory].underscore}/#{view}.html.erb"
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1 @@
1
+ require "rodauth/rails/feature"
@@ -0,0 +1,50 @@
1
+ module Rodauth
2
+ module Rails
3
+ class App
4
+ # Sets up Rails' flash integration.
5
+ module Flash
6
+ def self.load_dependencies(app)
7
+ app.plugin :hooks
8
+ end
9
+
10
+ def self.configure(app)
11
+ app.before { request.flash } # load flash
12
+ app.after { request.commit_flash } # save flash
13
+ end
14
+
15
+ module InstanceMethods
16
+ def flash
17
+ request.flash
18
+ end
19
+ end
20
+
21
+ module RequestMethods
22
+ # If the redirect would bubble up outside of the Roda app, the after
23
+ # hook would never get called, so we make sure to commit the flash.
24
+ def redirect(*)
25
+ commit_flash
26
+ super
27
+ end
28
+
29
+ def flash
30
+ rails_request.flash
31
+ end
32
+
33
+ def commit_flash
34
+ if ActionPack.version >= Gem::Version.new("5.0.0")
35
+ rails_request.commit_flash
36
+ else
37
+ # ActionPack 4.2 automatically commits flash
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def rails_request
44
+ ActionDispatch::Request.new(env)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ require "roda"
2
+
3
+ module Rodauth
4
+ module Rails
5
+ # The superclass for creating a Rodauth middleware.
6
+ class App < Roda
7
+ require "rodauth/rails/app/flash"
8
+
9
+ plugin :middleware
10
+ plugin :hooks
11
+ plugin :render, layout: false
12
+
13
+ plugin Flash
14
+
15
+ def self.configure(name = nil, **options, &block)
16
+ plugin :rodauth, name: name, csrf: false, flash: false, **options do
17
+ # load the Rails integration
18
+ enable :rails
19
+
20
+ # database functions are more complex to set up, so disable them by default
21
+ use_database_authentication_functions? false
22
+
23
+ # avoid having to set deadline values in column default values
24
+ set_deadline_values? true
25
+
26
+ # use HMACs for additional security
27
+ hmac_secret { ::Rails.application.secrets.secret_key_base }
28
+
29
+ # evaluate user configuration
30
+ instance_exec(&block)
31
+ end
32
+ end
33
+
34
+ before do
35
+ (opts[:rodauths] || {}).each do |name, _|
36
+ if name
37
+ env["rodauth.#{name}"] = rodauth(name)
38
+ else
39
+ env["rodauth"] = rodauth
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end