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 +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +20 -7
- data/lib/generators/verikloak/install/install_generator.rb +2 -0
- data/lib/verikloak/rails/configuration.rb +29 -1
- data/lib/verikloak/rails/controller.rb +45 -3
- data/lib/verikloak/rails/railtie.rb +68 -11
- data/lib/verikloak/rails/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e588f4e83d324112c789986e02a9a2acab3de3a06a1b5f516da2fefe9439e4b4
|
4
|
+
data.tar.gz: e1e59805c5af304ecf4349f5d2031faa79a7e7b5c13188dd32e7866a4f42eb0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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` |
|
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
|
86
|
+
| Component | Inserted relative to | Purpose |
|
87
87
|
| --- | --- | --- |
|
88
|
-
| `Verikloak::
|
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
|
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 |
|
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
|
-
|
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
|
-
|
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.
|
20
|
-
|
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
|
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.
|
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.
|
38
|
+
version: 0.1.5
|
39
39
|
- - "<"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
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.
|
48
|
+
version: 0.1.5
|
49
49
|
- - "<"
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version:
|
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.
|
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:
|