otto 2.0.0.pre1 → 2.0.0.pre2

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -1
  3. data/.github/workflows/claude-code-review.yml +1 -1
  4. data/.github/workflows/claude.yml +1 -1
  5. data/.rubocop.yml +4 -1
  6. data/CHANGELOG.rst +54 -6
  7. data/Gemfile +1 -1
  8. data/Gemfile.lock +19 -18
  9. data/docs/.gitignore +1 -0
  10. data/docs/migrating/v2.0.0-pre2.md +345 -0
  11. data/lib/otto/core/configuration.rb +2 -2
  12. data/lib/otto/core/middleware_stack.rb +80 -0
  13. data/lib/otto/core/router.rb +7 -6
  14. data/lib/otto/env_keys.rb +114 -0
  15. data/lib/otto/helpers/base.rb +2 -21
  16. data/lib/otto/helpers/response.rb +22 -0
  17. data/lib/otto/mcp/{validation.rb → schema_validation.rb} +3 -2
  18. data/lib/otto/mcp/server.rb +26 -13
  19. data/lib/otto/response_handlers/json.rb +6 -0
  20. data/lib/otto/route.rb +44 -48
  21. data/lib/otto/route_handlers/factory.rb +22 -9
  22. data/lib/otto/security/authentication/authentication_middleware.rb +29 -12
  23. data/lib/otto/security/authentication/failure_result.rb +15 -7
  24. data/lib/otto/security/authentication/route_auth_wrapper.rb +149 -0
  25. data/lib/otto/security/authentication/strategies/{public_strategy.rb → noauth_strategy.rb} +1 -1
  26. data/lib/otto/security/authentication/strategy_result.rb +129 -15
  27. data/lib/otto/security/authentication.rb +2 -2
  28. data/lib/otto/security/config.rb +0 -11
  29. data/lib/otto/security/configurator.rb +2 -2
  30. data/lib/otto/security/middleware/rate_limit_middleware.rb +19 -3
  31. data/lib/otto/version.rb +1 -1
  32. data/lib/otto.rb +2 -3
  33. data/otto.gemspec +2 -0
  34. metadata +26 -6
  35. data/changelog.d/20250911_235619_delano_next.rst +0 -28
  36. data/changelog.d/20250912_123055_delano_remove_ostruct.rst +0 -21
  37. data/changelog.d/20250912_175625_claude_delano_remove_ostruct.rst +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3de0a572bb6389f8e3f6b64d3295630c1d2f9872734d5840c5c2e5dc89f1fd4
4
- data.tar.gz: 41caaa99c08d5646d576b5c15d4392757e541ae00218dd7a3831d4532ee7a30c
3
+ metadata.gz: b6215d43a8ce52d332bc046b6d51e8474c243e804bb146381fcd617fa8aebd1f
4
+ data.tar.gz: 7d73de1613bdd0f2450c2b3e16c8ef0b7020ce417f38b506c70c9b05d414d561
5
5
  SHA512:
6
- metadata.gz: bbef30e2f11091b5b74c7c20aeead0b118b22bd5a52b77cc23c8901f8dcc58f9465748b1d3c46ae1c2cf0a75b7736d07248d9150a6b9fe28ddc02ef23543ca83
7
- data.tar.gz: 765a1cc021e278ca5ff484dd55e49695176c8aac998a3374677026857c27643c237088347fd3b04bbae69f4d6eae2ffec089bffbb7019f339c43c85f6e035668
6
+ metadata.gz: 4f04484aadab6fd972ecc261ed7da8291996e1623ae5df198b08550f82afa86e7d651beb7448d75b690b7505ae5d4d8443dd24c2a98bec7bb4621bcbb8b23950
7
+ data.tar.gz: bdfe655d0f597f92bfacaf89342aa0d026ecf2e89d1975ebc1835b2521a38f3bec8ba57cce94992ab0ac2eae4f01ce0080f849f82d08bb15e3473c8794fa9667
@@ -41,9 +41,10 @@ jobs:
41
41
  - uses: actions/checkout@v5
42
42
  - name: Set up Ruby
43
43
  uses: ruby/setup-ruby@v1
44
+ continue-on-error: ${{ matrix.experimental }}
44
45
  with:
45
46
  ruby-version: ${{ matrix.ruby }}
46
- bundler-cache: true
47
+ bundler-cache: ${{ !matrix.experimental }}
47
48
 
48
49
  - name: Setup tmate session
49
50
  uses: mxschmitt/action-tmate@7b6a61a73bbb9793cb80ad69b8dd8ac19261834c # v3
@@ -27,7 +27,7 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout repository
30
- uses: actions/checkout@v4
30
+ uses: actions/checkout@v5
31
31
  with:
32
32
  fetch-depth: 1
33
33
 
@@ -26,7 +26,7 @@ jobs:
26
26
  actions: read # Required for Claude to read CI results on PRs
27
27
  steps:
28
28
  - name: Checkout repository
29
- uses: actions/checkout@v4
29
+ uses: actions/checkout@v5
30
30
  with:
31
31
  fetch-depth: 1
32
32
 
data/.rubocop.yml CHANGED
@@ -48,7 +48,10 @@ Gemspec/DevelopmentDependencies:
48
48
  Enabled: true
49
49
 
50
50
  Layout/HashAlignment:
51
- Enabled: false
51
+ Enabled: true
52
+ EnforcedHashRocketStyle: key
53
+ EnforcedColonStyle: separator
54
+ EnforcedLastArgumentHashStyle: always_ignore # Changed from ignore_implicit
52
55
 
53
56
  Lint/Void:
54
57
  Enabled: false
data/CHANGELOG.rst CHANGED
@@ -1,17 +1,65 @@
1
1
  CHANGELOG.rst
2
2
  =============
3
3
 
4
- All notable changes to Otto are documented here.
5
-
6
- The format is based on `Keep a
7
- Changelog <https://keepachangelog.com/en/1.1.0/>`__, and this project
8
- adheres to `Semantic
9
- Versioning <https://semver.org/spec/v2.0.0.html>`__.
4
+ The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.1.0/>`__, and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`__.
10
5
 
11
6
  .. raw:: html
12
7
 
13
8
  <!--scriv-insert-here-->
14
9
 
10
+ .. _changelog-2.0.0.pre2:
11
+
12
+ 2.0.0.pre2 — 2025-10-11
13
+ =======================
14
+
15
+ Added
16
+ -----
17
+
18
+ - Added `StrategyResult` class with improved user model compatibility and cleaner API
19
+ - Helper methods ``authenticated?``, ``has_role?``, ``has_permission?``, ``user_name``, ``session_id`` for cleaner Logic class implementation
20
+ - Added JSON request body parsing support in Logic class handlers
21
+ - Added new modular directory structure under ``lib/otto/security/``
22
+ - Added backward compatibility aliases to maintain existing API compatibility
23
+ - Added proper namespacing for authentication components and middleware classes
24
+
25
+ Changed
26
+ -------
27
+
28
+ - **BREAKING**: Logic class constructor signature changed from ``initialize(session, user, params, locale)`` to ``initialize(context, params, locale)``
29
+ - Logic classes now receive an immutable context object instead of separate session/user parameters
30
+ - LogicClassHandler simplified to single arity pattern, removing backward compatibility code
31
+ - Authentication middleware now creates `StrategyResult` instances for all requests
32
+ - Replaced `RequestContext` with `StrategyResult` class for better authentication handling
33
+ - Simplified authentication strategy API to return `StrategyResult` or `nil` for success/failure
34
+ - Enhanced route handlers to support JSON request body parsing
35
+ - Updated authentication middleware to use `StrategyResult` throughout
36
+ - Reorganized Otto security module structure for better maintainability and separation of concerns
37
+ - Moved authentication strategies to ``Otto::Security::Authentication::Strategies`` namespace
38
+ - Moved security middleware to ``Otto::Security::Middleware`` namespace
39
+ - Moved ``StrategyResult`` and ``FailureResult`` to ``Otto::Security::Authentication`` namespace
40
+
41
+ Removed
42
+ -------
43
+
44
+ - Removed `RequestContext` class (which was introduced and then replaced by `StrategyResult` during this development cycle)
45
+ - Removed `AuthResult` class from authentication system
46
+ - Removed `ConcurrentCacheStore` example class for an ActiveSupport::Cache::MemoryStore-compatible interface with Rack::Attack
47
+ - Removed OpenStruct dependency across the framework
48
+
49
+ Documentation
50
+ -------------
51
+
52
+ - Updated migration guide with comprehensive examples for the new context object and step-by-step conversion instructions
53
+ - Updated Logic class examples in advanced_routes and authentication_strategies to demonstrate new pattern
54
+ - Enhanced documentation with API reference and helper method examples for the new context object
55
+
56
+ AI Assistance
57
+ -------------
58
+
59
+ - AI-assisted architectural design for RequestContext Data class and security module reorganization
60
+ - Comprehensive migration of Logic classes and documentation with AI guidance for consistency
61
+ - Automated test validation and intelligent file organization following Ruby conventions
62
+
15
63
  .. _changelog-2.0.0-pre1:
16
64
 
17
65
  2.0.0-pre1 — 2025-09-10
data/Gemfile CHANGED
@@ -25,7 +25,7 @@ end
25
25
 
26
26
  group :development do
27
27
  gem 'debug'
28
- gem 'rubocop', require: false
28
+ gem 'rubocop', '~> 1.81.1', require: false
29
29
  gem 'rubocop-performance', require: false
30
30
  gem 'rubocop-rspec', require: false
31
31
  gem 'rubocop-thread_safety', require: false
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- otto (2.0.0.pre.pre1)
4
+ otto (2.0.0.pre2)
5
5
  facets (~> 3.1)
6
+ logger (~> 1, < 2.0)
6
7
  loofah (~> 2.20)
7
8
  rack (~> 3.1, < 4.0)
8
9
  rack-parser (~> 0.7)
@@ -28,7 +29,7 @@ GEM
28
29
  pp (>= 0.6.0)
29
30
  rdoc (>= 4.0.0)
30
31
  reline (>= 0.4.2)
31
- json (2.13.2)
32
+ json (2.15.1)
32
33
  json_schemer (2.4.0)
33
34
  bigdecimal
34
35
  hana (~> 1.3)
@@ -41,21 +42,21 @@ GEM
41
42
  crass (~> 1.0.2)
42
43
  nokogiri (>= 1.12.0)
43
44
  minitest (5.25.5)
44
- nokogiri (1.18.9-aarch64-linux-gnu)
45
+ nokogiri (1.18.10-aarch64-linux-gnu)
45
46
  racc (~> 1.4)
46
- nokogiri (1.18.9-aarch64-linux-musl)
47
+ nokogiri (1.18.10-aarch64-linux-musl)
47
48
  racc (~> 1.4)
48
- nokogiri (1.18.9-arm-linux-gnu)
49
+ nokogiri (1.18.10-arm-linux-gnu)
49
50
  racc (~> 1.4)
50
- nokogiri (1.18.9-arm-linux-musl)
51
+ nokogiri (1.18.10-arm-linux-musl)
51
52
  racc (~> 1.4)
52
- nokogiri (1.18.9-arm64-darwin)
53
+ nokogiri (1.18.10-arm64-darwin)
53
54
  racc (~> 1.4)
54
- nokogiri (1.18.9-x86_64-darwin)
55
+ nokogiri (1.18.10-x86_64-darwin)
55
56
  racc (~> 1.4)
56
- nokogiri (1.18.9-x86_64-linux-gnu)
57
+ nokogiri (1.18.10-x86_64-linux-gnu)
57
58
  racc (~> 1.4)
58
- nokogiri (1.18.9-x86_64-linux-musl)
59
+ nokogiri (1.18.10-x86_64-linux-musl)
59
60
  racc (~> 1.4)
60
61
  parallel (1.27.0)
61
62
  parser (3.3.9.0)
@@ -67,12 +68,12 @@ GEM
67
68
  prettyprint
68
69
  prettier_print (1.2.1)
69
70
  prettyprint (0.2.0)
70
- prism (1.4.0)
71
+ prism (1.5.2)
71
72
  psych (5.2.6)
72
73
  date
73
74
  stringio
74
75
  racc (1.8.1)
75
- rack (3.2.1)
76
+ rack (3.2.2)
76
77
  rack-attack (6.7.0)
77
78
  rack (>= 1.0, < 4)
78
79
  rack-parser (0.7.0)
@@ -87,10 +88,10 @@ GEM
87
88
  rdoc (6.14.2)
88
89
  erb
89
90
  psych (>= 4.0.0)
90
- regexp_parser (2.11.2)
91
+ regexp_parser (2.11.3)
91
92
  reline (0.6.2)
92
93
  io-console (~> 0.5)
93
- rexml (3.4.3)
94
+ rexml (3.4.4)
94
95
  rspec (3.13.1)
95
96
  rspec-core (~> 3.13.0)
96
97
  rspec-expectations (~> 3.13.0)
@@ -104,7 +105,7 @@ GEM
104
105
  diff-lcs (>= 1.2.0, < 2.0)
105
106
  rspec-support (~> 3.13.0)
106
107
  rspec-support (3.13.5)
107
- rubocop (1.80.2)
108
+ rubocop (1.81.1)
108
109
  json (~> 2.3)
109
110
  language_server-protocol (~> 3.17.0.2)
110
111
  lint_roller (~> 1.1.0)
@@ -112,10 +113,10 @@ GEM
112
113
  parser (>= 3.3.0.2)
113
114
  rainbow (>= 2.2.2, < 4.0)
114
115
  regexp_parser (>= 2.9.3, < 3.0)
115
- rubocop-ast (>= 1.46.0, < 2.0)
116
+ rubocop-ast (>= 1.47.1, < 2.0)
116
117
  ruby-progressbar (~> 1.7)
117
118
  unicode-display_width (>= 2.4.0, < 4.0)
118
- rubocop-ast (1.46.0)
119
+ rubocop-ast (1.47.1)
119
120
  parser (>= 3.3.7.2)
120
121
  prism (~> 1.4)
121
122
  rubocop-performance (1.26.0)
@@ -173,7 +174,7 @@ DEPENDENCIES
173
174
  rack-test
174
175
  rackup
175
176
  rspec (~> 3.13)
176
- rubocop
177
+ rubocop (~> 1.81.1)
177
178
  rubocop-performance
178
179
  rubocop-rspec
179
180
  rubocop-thread_safety
data/docs/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  *
2
2
  !.gitignore
3
3
  !migrating/
4
+ !migrating/*.md
@@ -0,0 +1,345 @@
1
+ # Otto v2.0.0-pre2 Migration Guide
2
+
3
+ ## Overview
4
+
5
+ This release resolves critical architectural issues with `StrategyResult` semantics and removes deprecated methods. The changes clarify the distinction between "request state" (user in session) and "authentication outcomes" (auth attempt just succeeded).
6
+
7
+ ## Breaking Changes
8
+
9
+ ### 1. StrategyResult Methods Removed
10
+
11
+ **Removed Methods:**
12
+ - `StrategyResult#success?` - Always returned `true`, meaningless
13
+ - `StrategyResult#failure?` - Always returned `false`, meaningless
14
+ - `FailureResult#success?` - Always returned `false`
15
+ - `FailureResult#failure?` - Always returned `true`
16
+
17
+ **Migration:**
18
+
19
+ ```ruby
20
+ # Before - checking success/failure
21
+ if strategy_result&.success?
22
+ # handle success
23
+ end
24
+
25
+ # After - type checking
26
+ if strategy_result.is_a?(Otto::Security::Authentication::StrategyResult)
27
+ # handle success
28
+ end
29
+
30
+ # Or for middleware - FailureResult indicates failure
31
+ if strategy_result.is_a?(Otto::Security::Authentication::FailureResult)
32
+ # handle failure
33
+ else
34
+ # handle success
35
+ end
36
+ ```
37
+
38
+ ### 2. New Semantic Distinction
39
+
40
+ **New Method:**
41
+ - `StrategyResult#auth_attempt_succeeded?` - Returns `true` only when auth strategy just executed successfully
42
+
43
+ **Key Semantic Difference:**
44
+
45
+ | Method | Meaning | Use Case |
46
+ |--------|---------|----------|
47
+ | `authenticated?` | User in session (request state) | Check if session has user |
48
+ | `auth_attempt_succeeded?` | Auth strategy just succeeded (auth outcome) | Post-login redirects, analytics |
49
+
50
+ **Migration Examples:**
51
+
52
+ #### Registration Flow (IMPORTANT)
53
+
54
+ ```ruby
55
+ # Before - BROKEN - blocks legitimate registration
56
+ class CreateAccount < Logic::Base
57
+ def raise_concerns
58
+ # This was always true if user in session, blocking registration
59
+ raise OT::FormError, "Already signed up" if @strategy_result.success?
60
+ end
61
+ end
62
+
63
+ # After - CORRECT
64
+ class CreateAccount < Logic::Base
65
+ def raise_concerns
66
+ # Check if user already in session
67
+ raise OT::FormError, "Already signed up" if @strategy_result.authenticated?
68
+ end
69
+ end
70
+ ```
71
+
72
+ #### Post-Login Redirect
73
+
74
+ ```ruby
75
+ # Before - unreliable
76
+ class AuthController
77
+ def authenticate
78
+ if @strategy_result.success? # Always true, not helpful
79
+ redirect_to dashboard_path
80
+ end
81
+ end
82
+ end
83
+
84
+ # After - correct semantic
85
+ class AuthController
86
+ def authenticate
87
+ if @strategy_result.auth_attempt_succeeded?
88
+ # Only redirect when auth route just succeeded
89
+ redirect_to dashboard_path
90
+ end
91
+ end
92
+ end
93
+ ```
94
+
95
+ ## Non-Breaking Enhancements
96
+
97
+ ### 1. Comprehensive Documentation
98
+
99
+ `StrategyResult` now includes extensive inline documentation:
100
+ - Usage patterns and creation guidelines
101
+ - Session contract for multi-app architectures
102
+ - Examples for common scenarios
103
+ - Clear distinction between request state and auth outcomes
104
+
105
+ ### 2. Session Contract (Multi-App Architectures)
106
+
107
+ For shared session architectures (Auth app + Core app):
108
+
109
+ **Required session keys for authenticated state:**
110
+ ```ruby
111
+ session['authenticated'] # Boolean flag
112
+ session['identity_id'] # User/customer ID
113
+ session['authenticated_at'] # Timestamp
114
+ ```
115
+
116
+ **Optional session keys:**
117
+ ```ruby
118
+ session['email'] # User email
119
+ session['ip_address'] # Client IP
120
+ session['user_agent'] # Client UA
121
+ session['locale'] # User locale
122
+ ```
123
+
124
+ **Advanced mode adds:**
125
+ ```ruby
126
+ session['account_external_id'] # Rodauth external_id
127
+ session['advanced_account_id'] # Rodauth account ID
128
+ ```
129
+
130
+ ## Application Code Updates Required
131
+
132
+ ### 1. Remove Manual StrategyResult Creation
133
+
134
+ **Anti-pattern identified:**
135
+ ```ruby
136
+ # BAD - Bypasses Otto's auth_method tracking
137
+ class Controller::Base
138
+ def _strategy_result
139
+ Otto::Security::Authentication::StrategyResult.new(
140
+ session: session,
141
+ user: cust,
142
+ auth_method: 'session', # Hardcoded - loses semantic meaning
143
+ metadata: { ip: req.client_ipaddress }
144
+ )
145
+ end
146
+ end
147
+ ```
148
+
149
+ **Correct approach:**
150
+ ```ruby
151
+ # GOOD - Use middleware-provided result
152
+ class Controller::Base
153
+ def strategy_result
154
+ req.env['otto.strategy_result'] # Created by AuthenticationMiddleware
155
+ end
156
+ end
157
+
158
+ # Or for non-auth checks, use session directly
159
+ class Controller::Base
160
+ def current_user
161
+ return nil unless session['authenticated']
162
+ Customer.find(session['identity_id'])
163
+ end
164
+ end
165
+ ```
166
+
167
+ ### 2. Update Logic Classes
168
+
169
+ **Pattern to check for:**
170
+ ```ruby
171
+ # Search your codebase for these patterns:
172
+ grep -r "@strategy_result.success?" apps/
173
+ grep -r "@context.success?" apps/
174
+ grep -r "strategy_result&.success?" apps/
175
+ ```
176
+
177
+ **Update to:**
178
+ - Use `authenticated?` for "user in session" checks (registration, profile access, etc.)
179
+ - Use `auth_attempt_succeeded?` for "just logged in" checks (redirects, welcome messages, etc.)
180
+
181
+ ### 3. Test Updates
182
+
183
+ **RSpec matchers:**
184
+ ```ruby
185
+ # Before
186
+ expect(result).to be_success
187
+ expect(result).to be_failure
188
+
189
+ # After
190
+ expect(result).to be_a(Otto::Security::Authentication::StrategyResult)
191
+ expect(result).to be_a(Otto::Security::Authentication::FailureResult)
192
+ ```
193
+
194
+ ## Architecture Clarifications
195
+
196
+ ### When StrategyResult is Created
197
+
198
+ 1. **Routes WITH `auth=...` requirement:**
199
+ - Strategy executes
200
+ - Returns `StrategyResult` (success) or `FailureResult` (failure)
201
+ - Middleware converts `FailureResult` to anonymous `StrategyResult` + 401 response
202
+
203
+ 2. **Routes WITHOUT `auth=...` requirement:**
204
+ - Middleware creates anonymous `StrategyResult`
205
+ - Sets `auth_method: 'anonymous'`
206
+
207
+ 3. **Auth app (Roda) routes:**
208
+ - Manually creates `StrategyResult` for Logic class compatibility
209
+ - Same interface as Otto controllers
210
+
211
+ ### Integration Boundaries
212
+
213
+ **Multi-app setup (Auth + Core + API):**
214
+ - **Shared:** Session middleware, Redis session, Logic classes, Customer model
215
+ - **Auth app:** Creates StrategyResult manually, uses Roda routing
216
+ - **Core/API apps:** StrategyResult from AuthenticationMiddleware
217
+ - **Integration:** Pure session-based, no direct code calls between apps
218
+
219
+ ## Testing Your Migration
220
+
221
+ ### 1. Registration Flow Test
222
+
223
+ ```ruby
224
+ describe "CreateAccount" do
225
+ it "blocks registration when user already authenticated" do
226
+ strategy_result = Otto::Security::Authentication::StrategyResult.new(
227
+ session: { user_id: 123 },
228
+ user: { id: 123 },
229
+ auth_method: 'anonymous', # No auth route, but user in session
230
+ metadata: {}
231
+ )
232
+
233
+ logic = CreateAccount.new(strategy_result, params, 'en')
234
+
235
+ expect { logic.raise_concerns }.to raise_error(OT::FormError, /Already signed up/)
236
+ end
237
+ end
238
+ ```
239
+
240
+ ### 2. Auth Attempt Test
241
+
242
+ ```ruby
243
+ describe "LoginHandler" do
244
+ it "redirects after successful authentication" do
245
+ strategy_result = Otto::Security::Authentication::StrategyResult.new(
246
+ session: { user_id: 123 },
247
+ user: { id: 123 },
248
+ auth_method: 'session', # Auth route succeeded
249
+ metadata: {}
250
+ )
251
+
252
+ expect(strategy_result.authenticated?).to be true
253
+ expect(strategy_result.auth_attempt_succeeded?).to be true
254
+ end
255
+ end
256
+ ```
257
+
258
+ ## Checklist
259
+
260
+ - [ ] Remove all usage of `success?` and `failure?` methods
261
+ - [ ] Update registration flows to use `authenticated?`
262
+ - [ ] Update post-login flows to use `auth_attempt_succeeded?` if needed
263
+ - [ ] Remove manual `StrategyResult` creation in controllers
264
+ - [ ] Update test matchers from `be_success`/`be_failure` to type checks
265
+ - [ ] Verify session contract keys match across apps
266
+ - [ ] Run full test suite: `bundle exec rspec`
267
+ - [ ] Test registration while logged in (should be blocked)
268
+ - [ ] Test login redirect flow (should work correctly)
269
+
270
+ ## Configuration Updates
271
+
272
+ ### Authentication Login Path Configuration
273
+
274
+ When authentication fails for HTML requests, Otto redirects to a login page. You can configure this path:
275
+
276
+ ```ruby
277
+ # Initialize with login_path configuration
278
+ otto = Otto.new do
279
+ auth_config[:login_path] = '/auth/login' # Default: '/signin'
280
+ end
281
+
282
+ # Or configure after initialization
283
+ otto.auth_config[:login_path] = '/custom/login'
284
+ ```
285
+
286
+ **Note:** If not configured, the default fallback is `/signin`. Ensure this route exists or configure your actual login path to avoid 404 errors on authentication failures.
287
+
288
+ ## Additional Improvements in v2.0.0-pre2
289
+
290
+ ### Middleware Architecture Enhancements
291
+
292
+ **1. Renamed MCP ValidationMiddleware → SchemaValidationMiddleware**
293
+ - Resolves naming collision with `Otto::Security::ValidationMiddleware`
294
+ - `Otto::MCP::SchemaValidationMiddleware` now clearly indicates JSON schema validation
295
+ - `Otto::Security::ValidationMiddleware` remains for input sanitization
296
+
297
+ **Migration:**
298
+ ```ruby
299
+ # File renamed: lib/otto/mcp/validation.rb → lib/otto/mcp/schema_validation.rb
300
+ # Class renamed automatically if using Otto's MCP server
301
+ # No action needed for most users
302
+ ```
303
+
304
+ **2. Centralized Env Keys Documentation**
305
+ - New file: `lib/otto/env_keys.rb`
306
+ - Documents all `env['otto.*']` keys with types, setters, and users
307
+ - Includes usage examples and multi-app integration patterns
308
+ - Essential reference for custom middleware development
309
+
310
+ **3. RateLimitMiddleware Clarity**
311
+ - Added documentation clarifying it's a CONFIGURATOR, not enforcer
312
+ - Actual rate limiting happens in Rack::Attack middleware
313
+ - `call` method is explicitly a pass-through
314
+
315
+ **4. Middleware Order Enforcement**
316
+ - New method: `MiddlewareStack#validate_mcp_middleware_order`
317
+ - New method: `MiddlewareStack#add_with_position` for explicit ordering
318
+ - MCP Server uses explicit positioning: `position: :first` and `position: :last`
319
+ - Validates middleware order and warns if suboptimal
320
+ - Optimal: RateLimitMiddleware → TokenMiddleware → SchemaValidationMiddleware
321
+ - Validation runs automatically when MCP is enabled
322
+
323
+ **Usage Example:**
324
+ ```ruby
325
+ # Explicit positioning for clarity
326
+ middleware.add_with_position(
327
+ Otto::MCP::RateLimitMiddleware,
328
+ security_config,
329
+ position: :first # Ensures rate limiting runs first
330
+ )
331
+
332
+ middleware.add_with_position(
333
+ Otto::MCP::SchemaValidationMiddleware,
334
+ position: :last # Ensures validation runs last
335
+ )
336
+ ```
337
+
338
+ ## Questions?
339
+
340
+ Review the comprehensive inline documentation in:
341
+ - `lib/otto/security/authentication/strategy_result.rb` (lines 1-90) - Auth semantics
342
+ - `lib/otto/security/authentication/authentication_middleware.rb` - Auth middleware
343
+ - `lib/otto/env_keys.rb` - Complete env key registry
344
+
345
+ The documentation includes detailed usage patterns, session contracts, and examples for common scenarios.
@@ -143,12 +143,12 @@ class Otto
143
143
  # @param default_strategy [String] Default strategy to use when none specified
144
144
  # @example
145
145
  # otto.configure_auth_strategies({
146
- # 'publicly' => Otto::Security::Authentication::Strategies::PublicStrategy.new,
146
+ # 'noauth' => Otto::Security::Authentication::Strategies::NoAuthStrategy.new,
147
147
  # 'authenticated' => Otto::Security::Authentication::Strategies::SessionStrategy.new(session_key: 'user_id'),
148
148
  # 'role:admin' => Otto::Security::Authentication::Strategies::RoleStrategy.new(['admin']),
149
149
  # 'api_key' => Otto::Security::Authentication::Strategies::APIKeyStrategy.new(api_keys: ['secret123'])
150
150
  # })
151
- def configure_auth_strategies(strategies, default_strategy: 'publicly')
151
+ def configure_auth_strategies(strategies, default_strategy: 'noauth')
152
152
  # Update existing @auth_config rather than creating a new one
153
153
  @auth_config[:auth_strategies] = strategies
154
154
  @auth_config[:default_auth_strategy] = default_strategy