rodauth-tools 0.3.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +93 -0
  3. data/.gitlint +9 -0
  4. data/.markdownlint-cli2.jsonc +26 -0
  5. data/.pre-commit-config.yaml +46 -0
  6. data/.rubocop.yml +18 -0
  7. data/.rubocop_todo.yml +243 -0
  8. data/CHANGELOG.md +81 -0
  9. data/CLAUDE.md +262 -0
  10. data/CODE_OF_CONDUCT.md +132 -0
  11. data/CONTRIBUTING.md +111 -0
  12. data/Gemfile +35 -0
  13. data/Gemfile.lock +356 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +339 -0
  16. data/Rakefile +8 -0
  17. data/lib/rodauth/features/external_identity.rb +946 -0
  18. data/lib/rodauth/features/hmac_secret_guard.rb +119 -0
  19. data/lib/rodauth/features/jwt_secret_guard.rb +120 -0
  20. data/lib/rodauth/features/table_guard.rb +937 -0
  21. data/lib/rodauth/sequel_generator.rb +531 -0
  22. data/lib/rodauth/table_inspector.rb +124 -0
  23. data/lib/rodauth/template_inspector.rb +134 -0
  24. data/lib/rodauth/tools/console_helpers.rb +158 -0
  25. data/lib/rodauth/tools/migration/sequel/account_expiration.erb +9 -0
  26. data/lib/rodauth/tools/migration/sequel/active_sessions.erb +10 -0
  27. data/lib/rodauth/tools/migration/sequel/audit_logging.erb +12 -0
  28. data/lib/rodauth/tools/migration/sequel/base.erb +41 -0
  29. data/lib/rodauth/tools/migration/sequel/disallow_password_reuse.erb +8 -0
  30. data/lib/rodauth/tools/migration/sequel/email_auth.erb +17 -0
  31. data/lib/rodauth/tools/migration/sequel/jwt_refresh.erb +18 -0
  32. data/lib/rodauth/tools/migration/sequel/lockout.erb +21 -0
  33. data/lib/rodauth/tools/migration/sequel/otp.erb +9 -0
  34. data/lib/rodauth/tools/migration/sequel/otp_unlock.erb +8 -0
  35. data/lib/rodauth/tools/migration/sequel/password_expiration.erb +7 -0
  36. data/lib/rodauth/tools/migration/sequel/recovery_codes.erb +8 -0
  37. data/lib/rodauth/tools/migration/sequel/remember.erb +16 -0
  38. data/lib/rodauth/tools/migration/sequel/reset_password.erb +17 -0
  39. data/lib/rodauth/tools/migration/sequel/single_session.erb +7 -0
  40. data/lib/rodauth/tools/migration/sequel/sms_codes.erb +10 -0
  41. data/lib/rodauth/tools/migration/sequel/verify_account.erb +9 -0
  42. data/lib/rodauth/tools/migration/sequel/verify_login_change.erb +17 -0
  43. data/lib/rodauth/tools/migration/sequel/webauthn.erb +15 -0
  44. data/lib/rodauth/tools/migration.rb +188 -0
  45. data/lib/rodauth/tools/version.rb +9 -0
  46. data/lib/rodauth/tools.rb +29 -0
  47. data/package-lock.json +500 -0
  48. data/package.json +11 -0
  49. data/rodauth-tools.gemspec +40 -0
  50. metadata +136 -0
data/Gemfile.lock ADDED
@@ -0,0 +1,356 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rodauth-tools (0.3.0)
5
+ dry-inflector (~> 1.1)
6
+ rodauth (~> 2.41)
7
+ sequel (~> 5.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ action_text-trix (2.1.15)
13
+ railties
14
+ actioncable (8.1.1)
15
+ actionpack (= 8.1.1)
16
+ activesupport (= 8.1.1)
17
+ nio4r (~> 2.0)
18
+ websocket-driver (>= 0.6.1)
19
+ zeitwerk (~> 2.6)
20
+ actionmailbox (8.1.1)
21
+ actionpack (= 8.1.1)
22
+ activejob (= 8.1.1)
23
+ activerecord (= 8.1.1)
24
+ activestorage (= 8.1.1)
25
+ activesupport (= 8.1.1)
26
+ mail (>= 2.8.0)
27
+ actionmailer (8.1.1)
28
+ actionpack (= 8.1.1)
29
+ actionview (= 8.1.1)
30
+ activejob (= 8.1.1)
31
+ activesupport (= 8.1.1)
32
+ mail (>= 2.8.0)
33
+ rails-dom-testing (~> 2.2)
34
+ actionpack (8.1.1)
35
+ actionview (= 8.1.1)
36
+ activesupport (= 8.1.1)
37
+ nokogiri (>= 1.8.5)
38
+ rack (>= 2.2.4)
39
+ rack-session (>= 1.0.1)
40
+ rack-test (>= 0.6.3)
41
+ rails-dom-testing (~> 2.2)
42
+ rails-html-sanitizer (~> 1.6)
43
+ useragent (~> 0.16)
44
+ actiontext (8.1.1)
45
+ action_text-trix (~> 2.1.15)
46
+ actionpack (= 8.1.1)
47
+ activerecord (= 8.1.1)
48
+ activestorage (= 8.1.1)
49
+ activesupport (= 8.1.1)
50
+ globalid (>= 0.6.0)
51
+ nokogiri (>= 1.8.5)
52
+ actionview (8.1.1)
53
+ activesupport (= 8.1.1)
54
+ builder (~> 3.1)
55
+ erubi (~> 1.11)
56
+ rails-dom-testing (~> 2.2)
57
+ rails-html-sanitizer (~> 1.6)
58
+ activejob (8.1.1)
59
+ activesupport (= 8.1.1)
60
+ globalid (>= 0.3.6)
61
+ activemodel (8.1.1)
62
+ activesupport (= 8.1.1)
63
+ activerecord (8.1.1)
64
+ activemodel (= 8.1.1)
65
+ activesupport (= 8.1.1)
66
+ timeout (>= 0.4.0)
67
+ activestorage (8.1.1)
68
+ actionpack (= 8.1.1)
69
+ activejob (= 8.1.1)
70
+ activerecord (= 8.1.1)
71
+ activesupport (= 8.1.1)
72
+ marcel (~> 1.0)
73
+ activesupport (8.1.1)
74
+ base64
75
+ bigdecimal
76
+ concurrent-ruby (~> 1.0, >= 1.3.1)
77
+ connection_pool (>= 2.2.5)
78
+ drb
79
+ i18n (>= 1.6, < 2)
80
+ json
81
+ logger (>= 1.4.2)
82
+ minitest (>= 5.1)
83
+ securerandom (>= 0.3)
84
+ tzinfo (~> 2.0, >= 2.0.5)
85
+ uri (>= 0.13.1)
86
+ addressable (2.8.7)
87
+ public_suffix (>= 2.0.2, < 7.0)
88
+ android_key_attestation (0.3.0)
89
+ ast (2.4.3)
90
+ base64 (0.3.0)
91
+ bcrypt (3.1.20)
92
+ bigdecimal (3.3.1)
93
+ bindata (2.5.1)
94
+ builder (3.3.0)
95
+ bundler-audit (0.9.2)
96
+ bundler (>= 1.2.0, < 3)
97
+ thor (~> 1.0)
98
+ capybara (3.40.0)
99
+ addressable
100
+ matrix
101
+ mini_mime (>= 0.1.3)
102
+ nokogiri (~> 1.11)
103
+ rack (>= 1.6.0)
104
+ rack-test (>= 0.6.3)
105
+ regexp_parser (>= 1.5, < 3.0)
106
+ xpath (~> 3.2)
107
+ cbor (0.5.10.1)
108
+ chunky_png (1.4.0)
109
+ concurrent-ruby (1.3.5)
110
+ connection_pool (2.5.4)
111
+ cose (1.3.1)
112
+ cbor (~> 0.5.9)
113
+ openssl-signature_algorithm (~> 1.0)
114
+ crass (1.0.6)
115
+ date (3.5.0)
116
+ diff-lcs (1.6.2)
117
+ drb (2.2.3)
118
+ dry-inflector (1.2.0)
119
+ erb (5.1.3)
120
+ erubi (1.13.1)
121
+ globalid (1.3.0)
122
+ activesupport (>= 6.1)
123
+ i18n (1.14.7)
124
+ concurrent-ruby (~> 1.0)
125
+ io-console (0.8.1)
126
+ irb (1.15.3)
127
+ pp (>= 0.6.0)
128
+ rdoc (>= 4.0.0)
129
+ reline (>= 0.4.2)
130
+ json (2.16.0)
131
+ jwt (3.1.2)
132
+ base64
133
+ language_server-protocol (3.17.0.5)
134
+ lint_roller (1.1.0)
135
+ logger (1.7.0)
136
+ loofah (2.24.1)
137
+ crass (~> 1.0.2)
138
+ nokogiri (>= 1.12.0)
139
+ mail (2.9.0)
140
+ logger
141
+ mini_mime (>= 0.1.1)
142
+ net-imap
143
+ net-pop
144
+ net-smtp
145
+ marcel (1.1.0)
146
+ matrix (0.4.3)
147
+ mini_mime (1.1.5)
148
+ minitest (5.26.0)
149
+ net-imap (0.5.12)
150
+ date
151
+ net-protocol
152
+ net-pop (0.1.2)
153
+ net-protocol
154
+ net-protocol (0.2.2)
155
+ timeout
156
+ net-smtp (0.5.1)
157
+ net-protocol
158
+ nio4r (2.7.5)
159
+ nokogiri (1.18.10-arm64-darwin)
160
+ racc (~> 1.4)
161
+ nokogiri (1.18.10-x86_64-linux-gnu)
162
+ racc (~> 1.4)
163
+ openssl (3.3.1)
164
+ openssl-signature_algorithm (1.3.0)
165
+ openssl (> 2.0)
166
+ parallel (1.27.0)
167
+ parser (3.3.10.0)
168
+ ast (~> 2.4.1)
169
+ racc
170
+ pastel (0.8.0)
171
+ tty-color (~> 0.5)
172
+ pp (0.6.3)
173
+ prettyprint
174
+ prettyprint (0.2.0)
175
+ prism (1.6.0)
176
+ psych (5.2.6)
177
+ date
178
+ stringio
179
+ public_suffix (6.0.2)
180
+ racc (1.8.1)
181
+ rack (3.2.4)
182
+ rack-session (2.1.1)
183
+ base64 (>= 0.1.0)
184
+ rack (>= 3.0.0)
185
+ rack-test (2.2.0)
186
+ rack (>= 1.3)
187
+ rackup (2.2.1)
188
+ rack (>= 3)
189
+ rails (8.1.1)
190
+ actioncable (= 8.1.1)
191
+ actionmailbox (= 8.1.1)
192
+ actionmailer (= 8.1.1)
193
+ actionpack (= 8.1.1)
194
+ actiontext (= 8.1.1)
195
+ actionview (= 8.1.1)
196
+ activejob (= 8.1.1)
197
+ activemodel (= 8.1.1)
198
+ activerecord (= 8.1.1)
199
+ activestorage (= 8.1.1)
200
+ activesupport (= 8.1.1)
201
+ bundler (>= 1.15.0)
202
+ railties (= 8.1.1)
203
+ rails-dom-testing (2.3.0)
204
+ activesupport (>= 5.0.0)
205
+ minitest
206
+ nokogiri (>= 1.6)
207
+ rails-html-sanitizer (1.6.2)
208
+ loofah (~> 2.21)
209
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
210
+ railties (8.1.1)
211
+ actionpack (= 8.1.1)
212
+ activesupport (= 8.1.1)
213
+ irb (~> 1.13)
214
+ rackup (>= 1.0.0)
215
+ rake (>= 12.2)
216
+ thor (~> 1.0, >= 1.2.2)
217
+ tsort (>= 0.2)
218
+ zeitwerk (~> 2.6)
219
+ rainbow (3.1.1)
220
+ rake (13.3.1)
221
+ rdoc (6.15.1)
222
+ erb
223
+ psych (>= 4.0.0)
224
+ tsort
225
+ regexp_parser (2.11.3)
226
+ reline (0.6.2)
227
+ io-console (~> 0.5)
228
+ roda (3.98.0)
229
+ rack
230
+ rodauth (2.41.0)
231
+ roda (>= 2.6.0)
232
+ sequel (>= 4)
233
+ rotp (6.3.0)
234
+ rqrcode (3.1.0)
235
+ chunky_png (~> 1.0)
236
+ rqrcode_core (~> 2.0)
237
+ rqrcode_core (2.0.0)
238
+ rspec (3.13.2)
239
+ rspec-core (~> 3.13.0)
240
+ rspec-expectations (~> 3.13.0)
241
+ rspec-mocks (~> 3.13.0)
242
+ rspec-core (3.13.6)
243
+ rspec-support (~> 3.13.0)
244
+ rspec-expectations (3.13.5)
245
+ diff-lcs (>= 1.2.0, < 2.0)
246
+ rspec-support (~> 3.13.0)
247
+ rspec-mocks (3.13.6)
248
+ diff-lcs (>= 1.2.0, < 2.0)
249
+ rspec-support (~> 3.13.0)
250
+ rspec-support (3.13.6)
251
+ rubocop (1.81.7)
252
+ json (~> 2.3)
253
+ language_server-protocol (~> 3.17.0.2)
254
+ lint_roller (~> 1.1.0)
255
+ parallel (~> 1.10)
256
+ parser (>= 3.3.0.2)
257
+ rainbow (>= 2.2.2, < 4.0)
258
+ regexp_parser (>= 2.9.3, < 3.0)
259
+ rubocop-ast (>= 1.47.1, < 2.0)
260
+ ruby-progressbar (~> 1.7)
261
+ unicode-display_width (>= 2.4.0, < 4.0)
262
+ rubocop-ast (1.48.0)
263
+ parser (>= 3.3.7.2)
264
+ prism (~> 1.4)
265
+ rubocop-rake (0.7.1)
266
+ lint_roller (~> 1.1)
267
+ rubocop (>= 1.72.1)
268
+ rubocop-rspec (3.8.0)
269
+ lint_roller (~> 1.1)
270
+ rubocop (~> 1.81)
271
+ ruby-progressbar (1.13.0)
272
+ safety_net_attestation (0.5.0)
273
+ jwt (>= 2.0, < 4.0)
274
+ securerandom (0.4.1)
275
+ sequel (5.98.0)
276
+ bigdecimal
277
+ sequel-activerecord_connection (2.0.1)
278
+ activerecord (>= 5.1)
279
+ sequel (~> 5.38)
280
+ sqlite3 (2.8.0-arm64-darwin)
281
+ sqlite3 (2.8.0-x86_64-linux-gnu)
282
+ stringio (3.1.7)
283
+ thor (1.4.0)
284
+ tilt (2.6.1)
285
+ timeout (0.4.4)
286
+ tpm-key_attestation (0.14.1)
287
+ bindata (~> 2.4)
288
+ openssl (> 2.0)
289
+ openssl-signature_algorithm (~> 1.0)
290
+ tryouts (3.7.1)
291
+ concurrent-ruby (~> 1.0, < 2)
292
+ irb
293
+ minitest (~> 5.0)
294
+ pastel (~> 0.8)
295
+ prism (~> 1.0)
296
+ rspec (>= 3.0, < 5.0)
297
+ tty-cursor (~> 0.7)
298
+ tty-screen (~> 0.8)
299
+ tsort (0.2.0)
300
+ tty-color (0.6.0)
301
+ tty-cursor (0.7.1)
302
+ tty-screen (0.8.2)
303
+ tzinfo (2.0.6)
304
+ concurrent-ruby (~> 1.0)
305
+ unicode-display_width (3.2.0)
306
+ unicode-emoji (~> 4.1)
307
+ unicode-emoji (4.1.0)
308
+ uri (1.1.0)
309
+ useragent (0.16.11)
310
+ warning (1.5.0)
311
+ webauthn (3.4.3)
312
+ android_key_attestation (~> 0.3.0)
313
+ bindata (~> 2.4)
314
+ cbor (~> 0.5.9)
315
+ cose (~> 1.1)
316
+ openssl (>= 2.2)
317
+ safety_net_attestation (~> 0.5.0)
318
+ tpm-key_attestation (~> 0.14.0)
319
+ websocket-driver (0.8.0)
320
+ base64
321
+ websocket-extensions (>= 0.1.0)
322
+ websocket-extensions (0.1.5)
323
+ xpath (3.2.0)
324
+ nokogiri (~> 1.8)
325
+ zeitwerk (2.7.3)
326
+
327
+ PLATFORMS
328
+ arm64-darwin-25
329
+ x86_64-linux
330
+
331
+ DEPENDENCIES
332
+ bcrypt (~> 3.1)
333
+ bundler-audit
334
+ capybara
335
+ dry-inflector
336
+ irb
337
+ jwt (~> 3.1)
338
+ rack-test (~> 2.1)
339
+ rails (>= 6.0)
340
+ rake (~> 13.3)
341
+ rodauth-tools!
342
+ rotp
343
+ rqrcode
344
+ rspec (~> 3.0)
345
+ rubocop (~> 1.81)
346
+ rubocop-rake
347
+ rubocop-rspec
348
+ sequel-activerecord_connection (~> 2.0)
349
+ sqlite3 (~> 2.8)
350
+ tilt (~> 2.4)
351
+ tryouts (~> 3.0)
352
+ warning
353
+ webauthn
354
+
355
+ BUNDLED WITH
356
+ 2.7.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 delano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,339 @@
1
+ # Rodauth::Tools
2
+
3
+ Framework-agnostic utilities for [Rodauth](http://rodauth.jeremyevans.net) authentication. Provides external Rodauth features and Sequel migration generators.
4
+
5
+ ## Quick Start
6
+
7
+ ```ruby
8
+ # 1. Add table_guard to catch missing tables at startup
9
+ class RodauthApp < Roda
10
+ plugin :rodauth do
11
+ enable :login, :logout
12
+ enable :table_guard # ← Validates tables exist
13
+
14
+ # Production: fail fast if tables missing
15
+ table_guard_mode :raise
16
+ end
17
+ end
18
+
19
+ # 2. Run your app - catches problems immediately!
20
+ # => Rodauth::ConfigurationError: Missing required database tables!
21
+ # - accounts (feature: base)
22
+ # - account_password_hashes (feature: base)
23
+ ```
24
+
25
+ ## Overview
26
+
27
+ Rodauth::Tools provides utilities that work with any Rodauth setup, regardless of framework:
28
+
29
+ 1. **External Rodauth Features** - Like `table_guard` for validating database table setup, `external_identity` for tracking external IDs, and secret guards.
30
+ 2. **Sequel Migration Generator** - Generate Rodauth database migrations for 19 features.
31
+
32
+ This is NOT a framework adapter. It is a collection of tools extracted from framework integrations to be used in any Rodauth environment. For specific framework integration, use:
33
+
34
+ - Rails: [rodauth-rails](https://github.com/janko/rodauth-rails)
35
+ - Others: Integrate Rodauth directly - [see integration guide](docs/rodauth-integration.md)
36
+
37
+ ## Installation
38
+
39
+ Add this line to your application's Gemfile:
40
+
41
+ ```ruby
42
+ gem 'rodauth-tools'
43
+ ```
44
+
45
+ And then execute:
46
+
47
+ ```bash
48
+ bundle install
49
+ ```
50
+
51
+ Or install it yourself as:
52
+
53
+ ```bash
54
+ gem install rodauth-tools
55
+ ```
56
+
57
+ ## Features
58
+
59
+ ### 1. Table Guard Feature
60
+
61
+ Validates that required database tables exist for enabled Rodauth features.
62
+
63
+ ```ruby
64
+ class RodauthApp < Roda
65
+ plugin :rodauth do
66
+ enable :login, :logout, :otp
67
+ enable :table_guard # ← Add this
68
+
69
+ table_guard_mode :raise # or :warn, :error, :halt, :silent
70
+ end
71
+ end
72
+ ```
73
+
74
+ **Modes:**
75
+
76
+ - `:silent` / `:skip` / `nil` - Disable validation (debug log only)
77
+ - `:warn` - Log warning message and continue execution
78
+ - `:error` - Print distinctive message to error log but continue execution
79
+ - `:raise` - Log error and raise `Rodauth::ConfigurationError` (recommended for production)
80
+ - `:halt` / `:exit` - Log error and exit the process immediately
81
+ - Block - Custom handler (see docs)
82
+
83
+ **Introspection:**
84
+
85
+ ```ruby
86
+ rodauth = MyApp.rodauth
87
+
88
+ # List all required tables
89
+ rodauth.list_all_required_tables
90
+ # => [:accounts, :account_password_hashes, :account_otp_keys, ...]
91
+
92
+ # Check status of each table
93
+ rodauth.table_status
94
+ # => [{method: :accounts_table, table: :accounts, exists: true}, ...]
95
+
96
+ # Get missing tables
97
+ rodauth.missing_tables
98
+ # => [{method: :otp_keys_table, table: :account_otp_keys}, ...]
99
+ ```
100
+
101
+ **Documentation:** [docs/features/table-guard.md](docs/features/table-guard.md)
102
+
103
+ ### 2. External Identity Feature
104
+
105
+ Store external service identifiers in your accounts table with automatic helper methods.
106
+
107
+ ```ruby
108
+ class RodauthApp < Roda
109
+ plugin :rodauth do
110
+ enable :external_identity
111
+
112
+ # Declare columns (names used as-is)
113
+ external_identity_column :stripe_customer_id
114
+ external_identity_column :redis_uuid
115
+ external_identity_column :elasticsearch_doc_id
116
+
117
+ # Configuration
118
+ external_identity_check_columns :autocreate # Generate migration code
119
+ end
120
+ end
121
+
122
+ # Usage - helper methods match column names
123
+ rodauth.stripe_customer_id # => "cus_abc123"
124
+ rodauth.redis_uuid # => "550e8400-e29b-41d4-..."
125
+ rodauth.elasticsearch_doc_id # => "doc_789xyz"
126
+ ```
127
+
128
+ **Key Features:**
129
+
130
+ - Declarative column configuration
131
+ - Automatic helper method generation
132
+ - Auto-inclusion in `account_select`
133
+ - Column existence validation with migration code generation
134
+ - Introspection API for debugging
135
+
136
+ **Common Use Cases:**
137
+
138
+ - Payment integration (Stripe customer IDs)
139
+ - Session management (Redis keys)
140
+ - Search indexing (Elasticsearch document IDs)
141
+ - Federated authentication (Auth0 user IDs)
142
+
143
+ **Documentation:** [docs/features/external-identity.md](docs/features/external-identity.md)
144
+
145
+ ### 3. HMAC Secret Guard Feature
146
+
147
+ Automatically configure and validate HMAC secrets at application startup to prevent deployment errors.
148
+
149
+ ```ruby
150
+ class RodauthApp < Roda
151
+ plugin :rodauth do
152
+ enable :hmac_secret_guard
153
+
154
+ # Production: Raises error if HMAC_SECRET missing
155
+ # Development: Uses fallback secret with warning
156
+ end
157
+ end
158
+ ```
159
+
160
+ **Key Features:**
161
+
162
+ - Automatic loading from `HMAC_SECRET` environment variable
163
+ - Production mode: Raises `ConfigurationError` if secret missing
164
+ - Development mode: Logs warning and uses fallback
165
+ - Deletes secret from ENV after loading (security)
166
+ - Configurable production detection and error messages
167
+
168
+ **Documentation:** [docs/features/hmac-secret-guard.md](docs/features/hmac-secret-guard.md)
169
+
170
+ ### 4. JWT Secret Guard Feature
171
+
172
+ Automatically configure and validate JWT secrets at application startup to prevent deployment errors.
173
+
174
+ ```ruby
175
+ class RodauthApp < Roda
176
+ plugin :rodauth do
177
+ enable :jwt_secret_guard
178
+
179
+ # Production: Raises error if JWT_SECRET missing
180
+ # Development: Uses fallback secret with warning
181
+ end
182
+ end
183
+ ```
184
+
185
+ **Key Features:**
186
+
187
+ - Automatic loading from `JWT_SECRET` environment variable
188
+ - Production mode: Raises `ConfigurationError` if secret missing
189
+ - Development mode: Logs warning and uses fallback
190
+ - Deletes secret from ENV after loading (security)
191
+ - Defines `jwt_secret` method for standalone use (no JWT feature required)
192
+
193
+ **Documentation:** [docs/features/jwt-secret-guard.md](docs/features/jwt-secret-guard.md)
194
+
195
+ ### 5. Sequel Migration Generator
196
+
197
+ Generate database migrations for Rodauth features.
198
+
199
+ ```ruby
200
+ require "rodauth/tools"
201
+
202
+ generator = Rodauth::Tools::Migration.new(
203
+ features: [:base, :verify_account, :otp],
204
+ prefix: "account" # table prefix (default: "account")
205
+ )
206
+
207
+ # Get migration content
208
+ puts generator.generate
209
+
210
+ # Get Rodauth configuration
211
+ puts generator.configuration
212
+ # => {
213
+ # accounts_table: :accounts,
214
+ # verify_account_table: :account_verification_keys,
215
+ # otp_keys_table: :account_otp_keys
216
+ # }
217
+ ```
218
+
219
+ **Supported Features** (19 total):
220
+
221
+ - `base` - Core accounts table
222
+ - `remember` - Remember me functionality
223
+ - `verify_account` - Account verification
224
+ - `verify_login_change` - Login change verification
225
+ - `reset_password` - Password reset
226
+ - `email_auth` - Passwordless email authentication
227
+ - `otp` - TOTP multifactor authentication
228
+ - `otp_unlock` - OTP unlock
229
+ - `sms_codes` - SMS codes
230
+ - `recovery_codes` - Backup recovery codes
231
+ - `webauthn` - WebAuthn keys
232
+ - `lockout` - Account lockouts
233
+ - `active_sessions` - Session management
234
+ - `account_expiration` - Account expiration
235
+ - `password_expiration` - Password expiration
236
+ - `single_session` - Single session per account
237
+ - `audit_logging` - Authentication audit logs
238
+ - `disallow_password_reuse` - Password history
239
+ - `jwt_refresh` - JWT refresh tokens
240
+
241
+ ## Console
242
+
243
+ Interactive console for testing:
244
+
245
+ ```bash
246
+ bin/console
247
+ ```
248
+
249
+ Example session:
250
+
251
+ ```ruby
252
+ db = setup_test_db
253
+ app = create_app(db, features: [:login, :otp])
254
+ rodauth = app.rodauth
255
+
256
+ rodauth.list_all_required_tables
257
+ rodauth.table_status
258
+ rodauth.missing_tables
259
+ ```
260
+
261
+ ## Development
262
+
263
+ ```bash
264
+ # Run tests
265
+ bundle exec rspec
266
+
267
+ # Run console
268
+ bin/console
269
+ ```
270
+
271
+ ## Architecture
272
+
273
+ ```plain
274
+ ┌─────────────────────────┐
275
+ │ Rodauth │ ← the real work
276
+ │ (jeremyevans/rodauth) │
277
+ └───────────┬─────────────┘
278
+
279
+ ┌───────────────────────┼───────────────────────┐
280
+ │ │ │
281
+ ▼ ▼ ▼
282
+ rodauth-rails rodauth-tools your own
283
+ (janko) (this gem) features
284
+ │ │
285
+ │ ┌───────┴───────┐
286
+ │ │ │
287
+ │ features migrations
288
+ │ │ │
289
+ │ table_guard sequel templates (based on rodauth-rails)
290
+ │ external_identity
291
+ │ *_secret_guard
292
+
293
+ └───────────────┬───────────────┘
294
+
295
+
296
+ Your Rack app
297
+ ```
298
+
299
+ **Rodauth Feature Architecture:**
300
+
301
+ Rodauth Features are modules that mix into `Rodauth::Auth` instances at runtime. They use lifecycle hooks like `post_configure` for initialization and `auth_value_method` for user-overridable settings. See [docs/rodauth-features-api.md](docs/rodauth-features-api.md) for a DSL reference.
302
+
303
+ ## Documentation
304
+
305
+ - **[Table Guard Feature](docs/features/table-guard.md)** - Validate required database tables
306
+ - **[External Identity Feature](docs/features/external-identity.md)** - Track external service identifiers
307
+ - **[HMAC Secret Guard Feature](docs/features/hmac-secret-guard.md)** - Validate HMAC secrets at startup
308
+ - **[JWT Secret Guard Feature](docs/features/jwt-secret-guard.md)** - Validate JWT secrets at startup
309
+ - **[Sequel Migrations](docs/sequel-migrations.md)** - Integrating table_guard with Sequel migrations
310
+ - **[Rodauth Feature API](docs/rodauth-features-api.md)** - Complete DSL reference for feature development
311
+ - **[Rodauth Integration](docs/rodauth-integration.md)** - Framework integration patterns
312
+ - **[Mail Configuration](docs/rodauth-mail.md)** - Email and SMTP setup
313
+
314
+ ## Related Projects
315
+
316
+ - [rodauth](https://github.com/jeremyevans/rodauth) - The authentication framework
317
+ - [rodauth-rails](https://github.com/janko/rodauth-rails) - Rails integration
318
+ - [roda](https://github.com/jeremyevans/roda) - Routing tree web toolkit
319
+
320
+ ## AI Development Assistance
321
+
322
+ Version 0.3.0's features were developed with AI assistance:
323
+
324
+ - **Zed Agent (Claude Sonnet 4)** - Core feature implementation: Secret Guards, Table Guard, and Migrations
325
+ - **Gemini 3 Pro** - Release readiness assessment
326
+ - **GitHub Copilot** - Code completion
327
+
328
+ The maintainer remains responsible for all security decisions and implementation. We believe in transparency about development tools, especially for security-focused software.
329
+
330
+ ## Acknowledgments
331
+
332
+ This project originated as an effort to bring some of the excellent developer experience of [rodauth-rails](https://github.com/janko/rodauth-rails) to non-Rails environments. It started as a rack adapter (`rodauth-rack`) but was eventually distilled into this collection of framework-agnostic tools.
333
+
334
+ - **Migration Templates**: Heavily borrowed from [rodauth-rails](https://github.com/janko/rodauth-rails) by Janko Marohnić.
335
+ - **Inspiration**: The `rodauth-rails` project demonstrated how much easier Rodauth configuration could be with the right tooling.
336
+
337
+ ## License
338
+
339
+ MIT License