verikloak-rails 0.3.0 → 0.3.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: 76efc055243284fee1fae95dac5e73f8a50bbc128310ea255f7fe224b3188c89
4
- data.tar.gz: 0c5b906c0e406192e698454efd12f526021c3c26a8100da49ed9a1f3d71e9823
3
+ metadata.gz: f473cac2688d575650e3c483eac520fcb910ca7a9fd3c08620337386a705ca27
4
+ data.tar.gz: d76e49d03d27b336e6c8727f0cf3b82bb783bbe5153051be4ad4d0c1af5cfe7f
5
5
  SHA512:
6
- metadata.gz: 52ae4d44b34d53dd247010d1af4371850ee811c92f38d505b0d775d99b7e3fb66a4c22471f4bc1ebf2c7634397c9da76c1aef811e11ecc2ad7745d95b6ef1070
7
- data.tar.gz: a47b77285a47d8ce4092398d49d561586e874be1c4f98db2f6261bcd91b13e3bcfb3fd2283d8488a93be919321311c61f5e249ab758118f660fe04edd9e05d8b
6
+ metadata.gz: 6326a2ca5a86a50898ebe00094139733e3295203ec050e9c471093c1d3b21f2d161251d6ecf97ce265267718e83b570f1b9d8d82b9288855f732b557d22ac5a4
7
+ data.tar.gz: 874c03abffe2470e4993b64e0afdcbe42f1b1f27e8b4bbd17aa457445deffce591907a46a62f0be24801786f3e339bfd43fd89deb3d2239defcd3ad37c8410ca
data/CHANGELOG.md CHANGED
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.3.2] - 2026-01-01
11
+
12
+ ### Changed
13
+ - **BFF HeaderGuard safe insertion**: Railtie now validates BFF configuration before inserting `HeaderGuard`. If `trusted_proxies` is not configured (and `disabled` is not set to `true`), insertion is skipped with a warning instead of raising an error.
14
+ - This allows applications to start during initial setup before BFF configuration is complete.
15
+ - A clear warning message guides users to configure `trusted_proxies` to enable header validation.
16
+ - When `disabled: true` is set, HeaderGuard is inserted but internally disabled.
17
+ - **Railtie refactoring**: Extracted BFF configuration logic into `BffConfigurator` module and logging utilities into `RailtieLogger` module to improve maintainability.
18
+
19
+ ### Added
20
+ - `BffConfigurator` module for BFF-related middleware configuration.
21
+ - `RailtieLogger` module for consistent logging across Railtie operations.
22
+ - Comprehensive test coverage for `BffConfigurator.configuration_valid?` method.
23
+
24
+ ---
25
+
26
+ ## [0.3.1] - 2026-01-01
27
+
28
+ ### Added
29
+ - **API mode support**: `auto_include_controller` now automatically includes
30
+ `Verikloak::Rails::Controller` in both `ActionController::Base` and `ActionController::API`
31
+ - Rails API-only applications (`rails new myapp --api`) now work out of the box
32
+ - Skip logic prevents duplicate inclusion when controller already includes the concern
33
+
34
+ ### Changed
35
+ - **Generator template improved**: `initializer.rb.erb` now includes:
36
+ - Descriptive comments for each configuration option
37
+ - ENV variable support for `auto_include_controller` (`VERIKLOAK_AUTO_INCLUDE`)
38
+ - ENV variable prefix changed from `VERIKLOAK_AUDIENCE` to `KEYCLOAK_AUDIENCE` for consistency
39
+
40
+ ### Documentation
41
+ - README updated with API mode usage example
42
+
43
+ ---
44
+
10
45
  ## [0.3.0] - 2026-01-01
11
46
 
12
47
  ### Changed
data/README.md CHANGED
@@ -108,6 +108,16 @@ class ApplicationController < ActionController::Base
108
108
  end
109
109
  ```
110
110
 
111
+ ### API Mode Support
112
+ Both `ActionController::Base` and `ActionController::API` are supported. The controller concern is automatically included in both when `auto_include_controller` is enabled (default).
113
+
114
+ ```ruby
115
+ # Works automatically with Rails API mode (rails new myapp --api)
116
+ class ApplicationController < ActionController::API
117
+ # Verikloak::Rails::Controller is auto-included
118
+ end
119
+ ```
120
+
111
121
  ## Middleware
112
122
  ### Inserted Middleware
113
123
  | Component | Inserted relative to | Purpose |
@@ -1,14 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Rails.application.configure do
4
+ # Discovery URL (required)
4
5
  config.verikloak.discovery_url = ENV.fetch('KEYCLOAK_DISCOVERY_URL', nil)
5
- config.verikloak.audience = ENV.fetch('VERIKLOAK_AUDIENCE', 'rails-api')
6
- config.verikloak.issuer = ENV.fetch('VERIKLOAK_ISSUER', nil)
7
- config.verikloak.leeway = Integer(ENV.fetch('VERIKLOAK_LEEWAY', '60'))
8
- config.verikloak.skip_paths = %w[/up /health /rails/health]
9
6
 
7
+ # Audience validation (optional but recommended)
8
+ config.verikloak.audience = ENV.fetch('KEYCLOAK_AUDIENCE', 'rails-api')
9
+
10
+ # Issuer validation (optional, derived from discovery_url if not set)
11
+ config.verikloak.issuer = ENV.fetch('KEYCLOAK_ISSUER', nil)
12
+
13
+ # JWT clock skew tolerance in seconds
14
+ config.verikloak.leeway = Integer(ENV.fetch('VERIKLOAK_LEEWAY', '60'))
15
+
16
+ # Paths to skip authentication (health checks, etc.)
17
+ config.verikloak.skip_paths = %w[/up /health /rails/health]
18
+
19
+ # Request ID and subject in logs
10
20
  config.verikloak.logger_tags = %i[request_id sub]
11
- config.verikloak.auto_include_controller = true
21
+
22
+ # Auto-include controller concern
23
+ # Set to false if you manually include Verikloak::Rails::Controller
24
+ config.verikloak.auto_include_controller = ENV.fetch('VERIKLOAK_AUTO_INCLUDE', 'true') == 'true'
25
+
26
+ # Render detailed 500 errors (development only recommended)
12
27
  config.verikloak.render_500_json = ENV.fetch('VERIKLOAK_RENDER_500', 'false') == 'true'
13
28
 
14
29
  # Optional Pundit rescue (403 JSON). Leave commented so `verikloak-pundit`
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Verikloak
4
+ module Rails
5
+ # Handles BFF (Backend-for-Frontend) middleware configuration.
6
+ # Extracted from Railtie to maintain class size limits.
7
+ module BffConfigurator
8
+ module_function
9
+
10
+ # Insert the optional HeaderGuard middleware when verikloak-bff is present.
11
+ # Skips insertion with a warning if trusted_proxies is not configured and
12
+ # disabled is not explicitly set to true.
13
+ #
14
+ # @param stack [ActionDispatch::MiddlewareStackProxy]
15
+ # @return [void]
16
+ def configure_bff_guard(stack)
17
+ return unless Verikloak::Rails.config.auto_insert_bff_header_guard
18
+ return unless defined?(::Verikloak::BFF::HeaderGuard)
19
+
20
+ unless configuration_valid?
21
+ RailtieLogger.warn(
22
+ '[verikloak] Skipping BFF::HeaderGuard insertion: trusted_proxies not configured. ' \
23
+ 'Set trusted_proxies in bff_header_guard_options to enable header validation.'
24
+ )
25
+ return
26
+ end
27
+
28
+ insert_header_guard(stack)
29
+ end
30
+
31
+ # Configure the verikloak-bff library when options are supplied.
32
+ #
33
+ # @return [void]
34
+ def configure_library
35
+ options = Verikloak::Rails.config.bff_header_guard_options
36
+ return if options.nil? || (options.respond_to?(:empty?) && options.empty?)
37
+ return unless defined?(::Verikloak::BFF) && ::Verikloak::BFF.respond_to?(:configure)
38
+
39
+ apply_configuration(::Verikloak::BFF, options)
40
+ rescue StandardError => e
41
+ RailtieLogger.warn("[verikloak] Failed to apply BFF configuration: #{e.message}")
42
+ end
43
+
44
+ # Check if BFF configuration is valid for middleware insertion.
45
+ # Returns true if:
46
+ # - disabled: true is set (HeaderGuard will be inserted but internally disabled), OR
47
+ # - trusted_proxies is configured with at least one entry
48
+ #
49
+ # @return [Boolean]
50
+ def configuration_valid?
51
+ return true unless defined?(::Verikloak::BFF)
52
+ return true unless ::Verikloak::BFF.respond_to?(:config)
53
+
54
+ bff_config = ::Verikloak::BFF.config
55
+
56
+ # If disabled is explicitly set to true, allow insertion
57
+ # (HeaderGuard will be inserted but internally disabled)
58
+ return true if bff_config.respond_to?(:disabled) && bff_config.disabled
59
+
60
+ # For legacy versions without trusted_proxies method, allow insertion
61
+ return true unless bff_config.respond_to?(:trusted_proxies)
62
+
63
+ # Require trusted_proxies to be a non-empty Array
64
+ proxies = bff_config.trusted_proxies
65
+ proxies.is_a?(Array) && !proxies.empty?
66
+ end
67
+
68
+ # Insert HeaderGuard middleware into the stack.
69
+ #
70
+ # @param stack [ActionDispatch::MiddlewareStackProxy]
71
+ # @return [void]
72
+ def insert_header_guard(stack)
73
+ guard_before = Verikloak::Rails.config.bff_header_guard_insert_before
74
+ guard_after = Verikloak::Rails.config.bff_header_guard_insert_after
75
+
76
+ if guard_before
77
+ stack.insert_before guard_before, ::Verikloak::BFF::HeaderGuard
78
+ elsif guard_after
79
+ stack.insert_after guard_after, ::Verikloak::BFF::HeaderGuard
80
+ else
81
+ stack.insert_before ::Verikloak::Middleware, ::Verikloak::BFF::HeaderGuard
82
+ end
83
+ end
84
+
85
+ # Apply configuration options to the verikloak-bff namespace.
86
+ # Supports hash-like and callable inputs.
87
+ #
88
+ # @param target [Module] Verikloak::BFF namespace
89
+ # @param options [Hash, Proc, #to_h]
90
+ # @return [void]
91
+ def apply_configuration(target, options)
92
+ if options.respond_to?(:call)
93
+ target.configure(&options)
94
+ return
95
+ end
96
+
97
+ hash = options.respond_to?(:to_h) ? options.to_h : options
98
+ return unless hash.respond_to?(:each)
99
+
100
+ entries = hash.transform_keys(&:to_sym)
101
+ return if entries.empty?
102
+
103
+ target.configure do |config|
104
+ entries.each do |key, value|
105
+ writer = "#{key}="
106
+ config.public_send(writer, value) if config.respond_to?(writer)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'rails/railtie'
4
4
  require 'verikloak/middleware'
5
+ require_relative 'railtie_logger'
6
+ require_relative 'bff_configurator'
5
7
 
6
8
  module Verikloak
7
9
  module Rails
@@ -31,10 +33,16 @@ module Verikloak
31
33
  end
32
34
 
33
35
  # Optionally include the controller concern when ActionController loads.
36
+ # Supports both ActionController::Base and ActionController::API (API mode).
37
+ # Skips inclusion if the controller already includes the concern.
34
38
  # @return [void]
35
39
  initializer 'verikloak.controller' do |_app|
36
- ActiveSupport.on_load(:action_controller_base) do
37
- include Verikloak::Rails::Controller if Verikloak::Rails.config.auto_include_controller
40
+ %i[action_controller_base action_controller_api].each do |hook|
41
+ ActiveSupport.on_load(hook) do
42
+ next if include?(Verikloak::Rails::Controller) # Already included, skip
43
+
44
+ include Verikloak::Rails::Controller if Verikloak::Rails.config.auto_include_controller
45
+ end
38
46
  end
39
47
  end
40
48
 
@@ -47,7 +55,7 @@ module Verikloak
47
55
  # @return [ActionDispatch::MiddlewareStackProxy] configured middleware stack
48
56
  def configure_middleware(app)
49
57
  apply_configuration(app)
50
- configure_bff_library
58
+ BffConfigurator.configure_library
51
59
 
52
60
  unless discovery_url_present?
53
61
  log_missing_discovery_url_warning
@@ -55,55 +63,31 @@ module Verikloak
55
63
  end
56
64
 
57
65
  stack = insert_base_middleware(app)
58
- configure_bff_guard(stack) if stack
66
+ BffConfigurator.configure_bff_guard(stack) if stack
59
67
 
60
68
  stack
61
69
  end
62
70
 
63
- # Insert the optional HeaderGuard middleware when verikloak-bff is present.
71
+ # Check if discovery_url is present and valid.
64
72
  #
65
- # @param stack [ActionDispatch::MiddlewareStackProxy]
66
- # @return [void]
67
- def configure_bff_guard(stack)
68
- return unless Verikloak::Rails.config.auto_insert_bff_header_guard
69
- return unless defined?(::Verikloak::BFF::HeaderGuard)
73
+ # @return [Boolean] true if discovery_url is configured and not empty
74
+ def discovery_url_present?
75
+ discovery_url = Verikloak::Rails.config.discovery_url
76
+ return false unless discovery_url
70
77
 
71
- guard_before = Verikloak::Rails.config.bff_header_guard_insert_before
72
- guard_after = Verikloak::Rails.config.bff_header_guard_insert_after
73
- if guard_before
74
- stack.insert_before guard_before, ::Verikloak::BFF::HeaderGuard
75
- elsif guard_after
76
- stack.insert_after guard_after, ::Verikloak::BFF::HeaderGuard
77
- else
78
- stack.insert_before ::Verikloak::Middleware, ::Verikloak::BFF::HeaderGuard
79
- end
78
+ return !discovery_url.blank? if discovery_url.respond_to?(:blank?)
79
+ return !discovery_url.empty? if discovery_url.respond_to?(:empty?)
80
+
81
+ true
80
82
  end
81
83
 
82
- # Apply configuration options to the verikloak-bff namespace.
83
- # Supports hash-like and callable inputs.
84
+ # Log a warning message when discovery_url is missing.
85
+ # Uses Rails.logger if available, falls back to warn.
84
86
  #
85
- # @param target [Module] Verikloak::BFF or Verikloak::Bff namespace
86
- # @param options [Hash, Proc, #to_h]
87
87
  # @return [void]
88
- def apply_bff_configuration(target, options)
89
- if options.respond_to?(:call)
90
- target.configure(&options)
91
- return
92
- end
93
-
94
- hash = options.respond_to?(:to_h) ? options.to_h : options
95
- return unless hash.respond_to?(:each)
96
-
97
- entries = hash.transform_keys(&:to_sym)
98
-
99
- return if entries.empty?
100
-
101
- target.configure do |config|
102
- entries.each do |key, value|
103
- writer = "#{key}="
104
- config.public_send(writer, value) if config.respond_to?(writer)
105
- end
106
- end
88
+ def log_missing_discovery_url_warning
89
+ message = '[verikloak] discovery_url is not configured; skipping middleware insertion.'
90
+ RailtieLogger.warn(message)
107
91
  end
108
92
 
109
93
  # Sync configuration from the Rails application into Verikloak::Rails.
@@ -120,41 +104,6 @@ module Verikloak
120
104
  end
121
105
  end
122
106
 
123
- # Configure the verikloak-bff library when options are supplied.
124
- #
125
- # @return [void]
126
- def configure_bff_library
127
- options = Verikloak::Rails.config.bff_header_guard_options
128
- return if options.nil? || (options.respond_to?(:empty?) && options.empty?)
129
- return unless defined?(::Verikloak::BFF) && ::Verikloak::BFF.respond_to?(:configure)
130
-
131
- apply_bff_configuration(::Verikloak::BFF, options)
132
- rescue StandardError => e
133
- warn_with_fallback("[verikloak] Failed to apply BFF configuration: #{e.message}")
134
- end
135
-
136
- # Check if discovery_url is present and valid.
137
- #
138
- # @return [Boolean] true if discovery_url is configured and not empty
139
- def discovery_url_present?
140
- discovery_url = Verikloak::Rails.config.discovery_url
141
- return false unless discovery_url
142
-
143
- return !discovery_url.blank? if discovery_url.respond_to?(:blank?)
144
- return !discovery_url.empty? if discovery_url.respond_to?(:empty?)
145
-
146
- true
147
- end
148
-
149
- # Log a warning message when discovery_url is missing.
150
- # Uses Rails.logger if available, falls back to warn.
151
- #
152
- # @return [void]
153
- def log_missing_discovery_url_warning
154
- message = '[verikloak] discovery_url is not configured; skipping middleware insertion.'
155
- warn_with_fallback(message)
156
- end
157
-
158
107
  # Insert the base Verikloak::Middleware into the application middleware stack.
159
108
  # Respects the configured insertion point (before or after specified middleware).
160
109
  #
@@ -237,26 +186,7 @@ module Verikloak
237
186
  def log_middleware_insertion_warning(candidate, error)
238
187
  candidate_name = candidate.is_a?(Class) ? candidate.name : candidate.class.name
239
188
  message = "[verikloak] Unable to insert after #{candidate_name}: #{error.message}"
240
- warn_with_fallback(message)
241
- end
242
-
243
- # Resolve the logger instance used for warnings, if present.
244
- # @return [Object, nil]
245
- def rails_logger
246
- return unless defined?(::Rails) && ::Rails.respond_to?(:logger)
247
-
248
- ::Rails.logger
249
- end
250
-
251
- # Log a warning using Rails.logger when available, otherwise fall back to Kernel#warn.
252
- # @param message [String]
253
- # @return [void]
254
- def warn_with_fallback(message)
255
- if (logger = rails_logger)
256
- logger.warn(message)
257
- else
258
- warn(message)
259
- end
189
+ RailtieLogger.warn(message)
260
190
  end
261
191
  end
262
192
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Verikloak
4
+ module Rails
5
+ # Logging utilities for Railtie operations.
6
+ # Provides consistent warning output across Rails versions.
7
+ module RailtieLogger
8
+ module_function
9
+
10
+ # Log a warning using Rails.logger when available, otherwise fall back to Kernel#warn.
11
+ # @param message [String]
12
+ # @return [void]
13
+ def warn(message)
14
+ if (logger = rails_logger)
15
+ logger.warn(message)
16
+ else
17
+ Kernel.warn(message)
18
+ end
19
+ end
20
+
21
+ # Resolve the logger instance used for warnings, if present.
22
+ # @return [Object, nil]
23
+ def rails_logger
24
+ return unless defined?(::Rails) && ::Rails.respond_to?(:logger)
25
+
26
+ ::Rails.logger
27
+ end
28
+ end
29
+ end
30
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Verikloak
4
4
  module Rails
5
- VERSION = '0.3.0'
5
+ VERSION = '0.3.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.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - taiyaky
@@ -82,10 +82,12 @@ files:
82
82
  - lib/generators/verikloak/install/templates/initializer.rb.erb
83
83
  - lib/verikloak-rails.rb
84
84
  - lib/verikloak/rails.rb
85
+ - lib/verikloak/rails/bff_configurator.rb
85
86
  - lib/verikloak/rails/configuration.rb
86
87
  - lib/verikloak/rails/controller.rb
87
88
  - lib/verikloak/rails/error_renderer.rb
88
89
  - lib/verikloak/rails/railtie.rb
90
+ - lib/verikloak/rails/railtie_logger.rb
89
91
  - lib/verikloak/rails/version.rb
90
92
  homepage: https://github.com/taiyaky/verikloak-rails
91
93
  licenses:
@@ -94,7 +96,7 @@ metadata:
94
96
  source_code_uri: https://github.com/taiyaky/verikloak-rails
95
97
  changelog_uri: https://github.com/taiyaky/verikloak-rails/blob/main/CHANGELOG.md
96
98
  bug_tracker_uri: https://github.com/taiyaky/verikloak-rails/issues
97
- documentation_uri: https://rubydoc.info/gems/verikloak-rails/0.3.0
99
+ documentation_uri: https://rubydoc.info/gems/verikloak-rails/0.3.2
98
100
  rubygems_mfa_required: 'true'
99
101
  rdoc_options: []
100
102
  require_paths: