verikloak-rails 0.2.0 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dda87b3f2293f4c0cf05710290a503fd5c15712758f685987385d1b0677938cc
4
- data.tar.gz: a299ad22f31b4871fde437e026557b106134e32dece4e7cb45be4073d0269a48
3
+ metadata.gz: e588f4e83d324112c789986e02a9a2acab3de3a06a1b5f516da2fefe9439e4b4
4
+ data.tar.gz: e1e59805c5af304ecf4349f5d2031faa79a7e7b5c13188dd32e7866a4f42eb0d
5
5
  SHA512:
6
- metadata.gz: '0149e3053f208dd90c26ec07a960ff14969cdcda74400403f4ab02376110b31624189b6612bf2d6d3d7857c15d1c2b3190da9b472c487d9a3866eaf4de3aa8f7'
7
- data.tar.gz: 2fa3507b18f362cb3523332256a0608bd95564e0a0e0dfb4a17b97b9fafbdb1ddad31fa34bbe8788d52546e1f22dd43b97174ce1474c24385838fef743f5b8ae
6
+ metadata.gz: 4af9b198e39240cc234c96b897549f893680a45f45223cc0bd9c09ad40139627e8f01a8fb5374c517adadf28aaaee498d189d765eab33ba5a9e5bf42b4e748ed
7
+ data.tar.gz: 77a17a8d8a44d3b92d8dee19b5e9e7940cd2ddc676166790a5c17cc8ba5064690db080b563b0d38ea7a9ef3ac75e977f6328d0e5cb8c41dd13c6710dc51a83da
data/CHANGELOG.md CHANGED
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.2.2] - 2025-09-21
11
+
12
+ ### Added
13
+ - Automatically insert `Verikloak::Bff::HeaderGuard` when the optional gem is available, with configuration toggles and ordering controls.
14
+ - Configuration knobs for positioning the base `Verikloak::Middleware` within the Rack stack.
15
+
16
+ ### Changed
17
+ - Disable the built-in Pundit rescue when `verikloak-pundit` is loaded unless explicitly configured.
18
+
19
+ ### Documentation
20
+ - Note related gems in the installer output and README, including new configuration options for middleware ordering and BFF auto-insertion.
21
+
22
+ ---
23
+
24
+ ## [0.2.1] - 2025-09-21
25
+
26
+ ### Changed
27
+ - Simplify `with_required_audience!` to always raise `Verikloak::Error`, letting the shared handler render forbidden responses
28
+
29
+ ### Fixed
30
+ - Ensure the 500 JSON renderer logs exceptions against the actual Rails logger even when wrapped by tagged logging adapters
31
+
32
+ ### Documentation
33
+ - Describe the `rescue_pundit` configuration flag and default initializer settings
34
+
10
35
  ## [0.2.0] - 2025-09-14
11
36
 
12
37
  ### Breaking
data/README.md CHANGED
@@ -40,7 +40,7 @@ Then configure `config/initializers/verikloak.rb`.
40
40
  | `current_user_claims` | Verified JWT claims (string keys) | `Hash` or `nil` | — |
41
41
  | `current_subject` | Convenience accessor for `sub` claim | `String` or `nil` | — |
42
42
  | `current_token` | Raw Bearer token from the request | `String` or `nil` | — |
43
- | `with_required_audience!(*aud)` | Enforce that `aud` includes all required entries | `void` | Renders standardized 403 JSON when requirements are not met |
43
+ | `with_required_audience!(*aud)` | Enforce that `aud` includes all required entries | `void` | Raises `Verikloak::Error('forbidden')` so the concern renders standardized 403 JSON and halts the action |
44
44
 
45
45
  ### Data Sources
46
46
  | Value | Rack env keys | Fallback (RequestStore) | Notes |
@@ -83,9 +83,10 @@ end
83
83
 
84
84
  ## Middleware
85
85
  ### Inserted Middleware
86
- | Component | Inserted after | Purpose |
86
+ | Component | Inserted relative to | Purpose |
87
87
  | --- | --- | --- |
88
- | `Verikloak::Middleware` | `Rails::Rack::Logger` | Validate Bearer JWT (OIDC discovery + JWKS), set `verikloak.user`/`verikloak.token`, and honor `skip_paths` |
88
+ | `Verikloak::Bff::HeaderGuard` (optional) | Before `Verikloak::Middleware` by default when the gem is present | Normalize or enforce trusted proxy headers such as `X-Forwarded-Access-Token` |
89
+ | `Verikloak::Middleware` | After `Rails::Rack::Logger` by default (configurable) | Validate Bearer JWT (OIDC discovery + JWKS), set `verikloak.user`/`verikloak.token`, and honor `skip_paths` |
89
90
 
90
91
  ### BFF Integration
91
92
  Support for BFF header handling (e.g., normalizing or enforcing `X-Forwarded-Access-Token`) now lives in a dedicated gem: verikloak-bff.
@@ -94,6 +95,8 @@ Note: verikloak-bff's `HeaderGuard` never overwrites an existing `Authorization`
94
95
  - Gem: https://github.com/taiyaky/verikloak-bff
95
96
  - Rails guide: `docs/rails.md` in that repository
96
97
 
98
+ When `verikloak-bff` is on the load path, `verikloak-rails` automatically inserts `Verikloak::Bff::HeaderGuard` before the base middleware so forwarded headers are normalized before verification. Control this via `config.verikloak.auto_insert_bff_header_guard` and the `bff_header_guard_insert_before/after` knobs.
99
+
97
100
  Use verikloak-bff alongside this gem when you front Rails with a BFF/proxy such as oauth2-proxy and need to enforce trusted forwarding and header consistency.
98
101
 
99
102
  ## Configuration (initializer)
@@ -110,8 +113,13 @@ Keys under `config.verikloak`:
110
113
  | `logger_tags` | Array<Symbol> | Tags to add to Rails logs. Supports `:request_id`, `:sub` | `[:request_id, :sub]` |
111
114
  | `error_renderer` | Object responding to `render(controller, error)` | Override error rendering | built-in JSON renderer |
112
115
  | `auto_include_controller` | Boolean | Auto-include controller concern | `true` |
113
- | `render_500_json` | Boolean | Rescue `StandardError` and render JSON 500 | `false` |
114
- | `rescue_pundit` | Boolean | Rescue `Pundit::NotAuthorizedError` to 403 JSON when Pundit is present | `true` |
116
+ | `render_500_json` | Boolean | Rescue `StandardError`, log the exception, and render JSON 500 | `false` |
117
+ | `rescue_pundit` | Boolean | Rescue `Pundit::NotAuthorizedError` to 403 JSON when Pundit is present (auto-disabled when `verikloak-pundit` is loaded) | `true` |
118
+ | `middleware_insert_before` | Object/String/Symbol | Insert `Verikloak::Middleware` before this Rack middleware | `nil` |
119
+ | `middleware_insert_after` | Object/String/Symbol | Insert `Verikloak::Middleware` after this Rack middleware (`Rails::Rack::Logger` when `nil`) | `nil` |
120
+ | `auto_insert_bff_header_guard` | Boolean | Auto insert `Verikloak::Bff::HeaderGuard` when the gem is present | `true` |
121
+ | `bff_header_guard_insert_before` | Object/String/Symbol | Insert the header guard before this middleware (`Verikloak::Middleware` when `nil`) | `nil` |
122
+ | `bff_header_guard_insert_after` | Object/String/Symbol | Insert the header guard after this middleware | `nil` |
115
123
 
116
124
  Environment variable examples are in the generated initializer.
117
125
 
@@ -127,7 +135,10 @@ Rails.application.configure do
127
135
  # Optional but recommended when you know it
128
136
  # config.verikloak.issuer = 'https://idp.example.com/realms/myrealm'
129
137
 
130
- # For BFF/proxy header handling, see verikloak-bff
138
+ # For BFF/proxy header handling, see verikloak-bff (auto inserted when present)
139
+ # To customize ordering:
140
+ # config.verikloak.middleware_insert_before = Rack::Attack
141
+ # config.verikloak.auto_insert_bff_header_guard = false
131
142
  end
132
143
  ```
133
144
 
@@ -221,8 +232,10 @@ end
221
232
  ## Optional Pundit Rescue
222
233
  If the `pundit` gem is present, `Pundit::NotAuthorizedError` is rescued to a standardized 403 JSON. This is a lightweight convenience only; deeper Pundit integration (policies, helpers) is out of scope and can live in a separate plugin.
223
234
 
235
+ When the optional [`verikloak-pundit`](https://github.com/taiyaky/verikloak-pundit) gem is loaded, the built-in rescue is automatically disabled to avoid double-handling errors. Explicitly set `config.verikloak.rescue_pundit` if you prefer different behavior.
236
+
224
237
  ### Toggle
225
- Toggle with `config.verikloak.rescue_pundit` (default: true). Environment example:
238
+ Toggle with `config.verikloak.rescue_pundit` (default: true unless overridden by `verikloak-pundit`). Environment example:
226
239
 
227
240
  ```ruby
228
241
  # config/initializers/verikloak.rb
@@ -30,6 +30,8 @@ module Verikloak
30
30
  2) Set discovery_url / audience in config/initializers/verikloak.rb
31
31
  3) (Optional) If you disable auto-include, add this line to ApplicationController:
32
32
  include Verikloak::Rails::Controller
33
+ 4) (Optional) For BFF/proxy setups, add gem 'verikloak-bff' to normalize headers.
34
+ 5) (Optional) When using Pundit policies, consider gem 'verikloak-pundit' for richer errors.
33
35
  MSG
34
36
  end
35
37
  end
@@ -35,11 +35,34 @@ module Verikloak
35
35
  # @!attribute [rw] render_500_json
36
36
  # Rescue StandardError and render a JSON 500 response.
37
37
  # @return [Boolean]
38
+ # @!attribute [rw] rescue_pundit
39
+ # Rescue `Pundit::NotAuthorizedError` and render JSON 403 responses.
40
+ # @return [Boolean]
41
+ # @!attribute [rw] middleware_insert_before
42
+ # Rack middleware to insert `Verikloak::Middleware` before.
43
+ # @return [Object, String, Symbol, nil]
44
+ # @!attribute [rw] middleware_insert_after
45
+ # Rack middleware to insert `Verikloak::Middleware` after.
46
+ # @return [Object, String, Symbol, nil]
47
+ # @!attribute [rw] auto_insert_bff_header_guard
48
+ # Auto-insert `Verikloak::Bff::HeaderGuard` when available.
49
+ # @return [Boolean]
50
+ # @!attribute [rw] bff_header_guard_insert_before
51
+ # Rack middleware to insert the header guard before.
52
+ # @return [Object, String, Symbol, nil]
53
+ # @!attribute [rw] bff_header_guard_insert_after
54
+ # Rack middleware to insert the header guard after.
55
+ # @return [Object, String, Symbol, nil]
38
56
  class Configuration
39
57
  attr_accessor :discovery_url, :audience, :issuer, :leeway, :skip_paths,
40
58
  :logger_tags, :error_renderer, :auto_include_controller,
41
- :render_500_json, :rescue_pundit
59
+ :render_500_json, :rescue_pundit,
60
+ :middleware_insert_before, :middleware_insert_after,
61
+ :auto_insert_bff_header_guard,
62
+ :bff_header_guard_insert_before, :bff_header_guard_insert_after
42
63
 
64
+ # Initialize configuration with sensible defaults for Rails apps.
65
+ # @return [void]
43
66
  def initialize
44
67
  @discovery_url = nil
45
68
  @audience = nil
@@ -51,6 +74,11 @@ module Verikloak
51
74
  @auto_include_controller = true
52
75
  @render_500_json = false
53
76
  @rescue_pundit = true
77
+ @middleware_insert_before = nil
78
+ @middleware_insert_after = nil
79
+ @auto_insert_bff_header_guard = true
80
+ @bff_header_guard_insert_before = nil
81
+ @bff_header_guard_insert_after = nil
54
82
  end
55
83
 
56
84
  # Options forwarded to the base Verikloak Rack middleware.
@@ -16,7 +16,8 @@ module Verikloak
16
16
  before_action :authenticate_user!
17
17
  # Register generic error handler first so specific handlers take precedence.
18
18
  if Verikloak::Rails.config.render_500_json
19
- rescue_from StandardError do |_e|
19
+ rescue_from StandardError do |e|
20
+ _verikloak_log_internal_error(e)
20
21
  render json: { error: 'internal_server_error', message: 'An unexpected error occurred' },
21
22
  status: :internal_server_error
22
23
  end
@@ -84,13 +85,14 @@ module Verikloak
84
85
  #
85
86
  # @param required [Array<String>] one or more audiences to require
86
87
  # @return [void]
88
+ # @raise [Verikloak::Error] when the required audience is missing
87
89
  # @example
88
90
  # with_required_audience!('my-api', 'payments')
89
91
  def with_required_audience!(*required)
90
92
  aud = Array(current_user_claims&.dig('aud'))
91
93
  return if required.flatten.all? { |r| aud.include?(r) }
92
94
 
93
- render json: { error: 'forbidden', message: 'Required audience not satisfied' }, status: :forbidden
95
+ raise ::Verikloak::Error.new('forbidden', 'Required audience not satisfied')
94
96
  end
95
97
 
96
98
  private
@@ -118,10 +120,50 @@ module Verikloak
118
120
  end
119
121
  if Verikloak::Rails.config.logger_tags.include?(:sub)
120
122
  sub = current_subject
121
- tags << "sub:#{sub}" if sub
123
+ if sub
124
+ sanitized = sub.to_s.gsub(/[[:cntrl:]]+/, ' ').strip
125
+ tags << "sub:#{sanitized}" unless sanitized.empty?
126
+ end
122
127
  end
123
128
  tags
124
129
  end
130
+
131
+ # Write StandardError details to the controller or Rails logger when
132
+ # rendering the generic 500 JSON response. Logging ensures the
133
+ # underlying failure is still visible to operators even though the
134
+ # response body is static.
135
+ #
136
+ # @param exception [Exception]
137
+ # @return [void]
138
+ def _verikloak_log_internal_error(exception)
139
+ target_logger = _verikloak_base_logger
140
+ return unless target_logger.respond_to?(:error)
141
+
142
+ target_logger.error("[Verikloak] #{exception.class}: #{exception.message}")
143
+ backtrace = exception.backtrace
144
+ target_logger.error(backtrace.join("\n")) if backtrace&.any?
145
+ rescue StandardError
146
+ # Never allow logging failures to interfere with request handling.
147
+ nil
148
+ end
149
+
150
+ # Locate the innermost logger that responds to `error`.
151
+ # @return [Object, nil]
152
+ def _verikloak_base_logger
153
+ root_logger = if defined?(::Rails) && ::Rails.respond_to?(:logger)
154
+ ::Rails.logger
155
+ elsif respond_to?(:logger)
156
+ logger
157
+ end
158
+ current = root_logger
159
+ while current.respond_to?(:logger)
160
+ next_logger = current.logger
161
+ break if next_logger.nil? || next_logger.equal?(current)
162
+
163
+ current = next_logger
164
+ end
165
+ current
166
+ end
125
167
  end
126
168
  end
127
169
  end
@@ -16,17 +16,8 @@ module Verikloak
16
16
  # Apply configuration and insert middleware.
17
17
  # @return [void]
18
18
  initializer 'verikloak.configure' do |app|
19
- Verikloak::Rails.configure do |c|
20
- rails_cfg = app.config.verikloak
21
- %i[discovery_url audience issuer leeway skip_paths
22
- logger_tags error_renderer auto_include_controller
23
- render_500_json rescue_pundit].each do |key|
24
- c.send("#{key}=", rails_cfg[key]) if rails_cfg.key?(key)
25
- end
26
- end
27
- app.middleware.insert_after ::Rails::Rack::Logger,
28
- ::Verikloak::Middleware,
29
- **Verikloak::Rails.config.middleware_options
19
+ stack = ::Verikloak::Rails::Railtie.send(:configure_middleware, app)
20
+ ::Verikloak::Rails::Railtie.send(:configure_bff_guard, stack)
30
21
  end
31
22
 
32
23
  # Optionally include the controller concern when ActionController loads.
@@ -36,6 +27,72 @@ module Verikloak
36
27
  include Verikloak::Rails::Controller if Verikloak::Rails.config.auto_include_controller
37
28
  end
38
29
  end
30
+
31
+ class << self
32
+ private
33
+
34
+ # Apply configured options and insert the base middleware into the stack.
35
+ #
36
+ # @param app [Rails::Application] application being initialized
37
+ # @return [ActionDispatch::MiddlewareStackProxy] configured middleware stack
38
+ def configure_middleware(app)
39
+ apply_configuration(app)
40
+ base_options = Verikloak::Rails.config.middleware_options
41
+ stack = app.middleware
42
+ if (before = Verikloak::Rails.config.middleware_insert_before)
43
+ stack.insert_before before,
44
+ ::Verikloak::Middleware,
45
+ **base_options
46
+ else
47
+ after = Verikloak::Rails.config.middleware_insert_after || ::Rails::Rack::Logger
48
+ if after
49
+ stack.insert_after after,
50
+ ::Verikloak::Middleware,
51
+ **base_options
52
+ else
53
+ stack.use ::Verikloak::Middleware, **base_options
54
+ end
55
+ end
56
+ stack
57
+ end
58
+
59
+ # Insert the optional HeaderGuard middleware when verikloak-bff is present.
60
+ #
61
+ # @param stack [ActionDispatch::MiddlewareStackProxy]
62
+ # @return [void]
63
+ def configure_bff_guard(stack)
64
+ return unless Verikloak::Rails.config.auto_insert_bff_header_guard
65
+ return unless defined?(::Verikloak::Bff::HeaderGuard)
66
+
67
+ guard_before = Verikloak::Rails.config.bff_header_guard_insert_before
68
+ guard_after = Verikloak::Rails.config.bff_header_guard_insert_after
69
+ if guard_before
70
+ stack.insert_before guard_before, ::Verikloak::Bff::HeaderGuard
71
+ elsif guard_after
72
+ stack.insert_after guard_after, ::Verikloak::Bff::HeaderGuard
73
+ else
74
+ stack.insert_before ::Verikloak::Middleware, ::Verikloak::Bff::HeaderGuard
75
+ end
76
+ end
77
+
78
+ # Sync configuration from the Rails application into Verikloak::Rails.
79
+ #
80
+ # @param app [Rails::Application]
81
+ # @return [void]
82
+ def apply_configuration(app)
83
+ Verikloak::Rails.configure do |c|
84
+ rails_cfg = app.config.verikloak
85
+ %i[discovery_url audience issuer leeway skip_paths
86
+ logger_tags error_renderer auto_include_controller
87
+ render_500_json rescue_pundit middleware_insert_before
88
+ middleware_insert_after auto_insert_bff_header_guard
89
+ bff_header_guard_insert_before bff_header_guard_insert_after].each do |key|
90
+ c.send("#{key}=", rails_cfg[key]) if rails_cfg.key?(key)
91
+ end
92
+ c.rescue_pundit = false if !rails_cfg.key?(:rescue_pundit) && defined?(::Verikloak::Pundit)
93
+ end
94
+ end
95
+ end
39
96
  end
40
97
  end
41
98
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Verikloak
4
4
  module Rails
5
- VERSION = '0.2.0'
5
+ VERSION = '0.2.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verikloak-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - taiyaky
@@ -35,20 +35,20 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: 0.1.2
38
+ version: 0.1.5
39
39
  - - "<"
40
40
  - !ruby/object:Gem::Version
41
- version: '0.2'
41
+ version: 1.0.0
42
42
  type: :runtime
43
43
  prerelease: false
44
44
  version_requirements: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 0.1.2
48
+ version: 0.1.5
49
49
  - - "<"
50
50
  - !ruby/object:Gem::Version
51
- version: '0.2'
51
+ version: 1.0.0
52
52
  description: 'Rails integration for Verikloak: auto middleware, helpers, and standardized
53
53
  JSON errors.'
54
54
  executables: []
@@ -73,7 +73,7 @@ metadata:
73
73
  source_code_uri: https://github.com/taiyaky/verikloak-rails
74
74
  changelog_uri: https://github.com/taiyaky/verikloak-rails/blob/main/CHANGELOG.md
75
75
  bug_tracker_uri: https://github.com/taiyaky/verikloak-rails/issues
76
- documentation_uri: https://rubydoc.info/gems/verikloak-rails/0.2.0
76
+ documentation_uri: https://rubydoc.info/gems/verikloak-rails/0.2.2
77
77
  rubygems_mfa_required: 'true'
78
78
  rdoc_options: []
79
79
  require_paths: