verikloak 0.1.2 → 0.1.4

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: 05c91b8c75eff0da030bf668fa5c1ab1dece887a6cfaae8717ee7d82fbf5a637
4
- data.tar.gz: 7b7c2188b441e81fea22194fb1309c4436d321702ffc5ce5dc215286263821f2
3
+ metadata.gz: 3b7e22a96384ccea0cb43c2c611f7106d0e88b23793279f2ee105d68da226bb0
4
+ data.tar.gz: d83ba8ebe7834895d1a3f6e39c26dae02713611fc767177c909987ef1111cf50
5
5
  SHA512:
6
- metadata.gz: 6cf6fedad6a1ff8fd77ad25c91e97920e778a533b802da33f3b9c478637a271732c4c47f9ee8c8fa53e2b23716c60c75f34d068ef7177d1555dc9ceb8c7704bf
7
- data.tar.gz: fc32dda5c37fef1d339f4478f1b96db406b24910ec2e19c402d53b685a84194336cd66ef96996b10f44a81964d03ae42f906da72aa07af8aae87496fbf68c2af
6
+ metadata.gz: 40481fc255490d0c22c2808cf00e8c0e136488f755e6dc2e388c4ab81978975c6ee478a2916d61fe564bd25984898f74ddeccacfe1ae8006a081b3bfc441bc41
7
+ data.tar.gz: 0aaf942838e33a618197d0fb6d5446c6a9ffaa5b529db50902f2ab403b884b03ee0c87bdf8c99e4c8b069cad1f566026bb05fb16ab3583339493a4891eeb4f88
data/CHANGELOG.md CHANGED
@@ -7,16 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.1.4] - 2025-09-20
11
+
12
+ ### Chore
13
+ - Bump dev dependency `rexml` to 3.4.2 (PR #15).
14
+
15
+ ---
16
+
17
+ ## [0.1.3] - 2025-09-15
18
+
19
+ ### Changed
20
+ - Relax `jwt` runtime dependency to `>= 2.7, < 4.0` to allow jwt 3.x (PR #11).
21
+
22
+ ### Chore
23
+ - Bump dev dependency `rubocop` to 1.80.2 (PR #13).
24
+ - Bump dev dependency `rubocop-rspec` to 3.7.0 (PR #12).
25
+
26
+ ---
27
+
10
28
  ## [0.1.2] - 2025-08-31
11
29
 
12
30
  ### Added
13
- - Middleware: new `connection:` option to inject a Faraday::Connection, shared by Discovery and JWKS.
31
+ - Middleware: new `connection:` option to inject a Faraday::Connection, shared by Discovery and JWKs.
14
32
  - Middleware: new `leeway:` and `token_verify_options:` options, delegated to TokenDecoder.
15
33
  - README: documented usage of `connection`, leeway/options, and clarified `skip_paths` behavior.
16
34
 
17
35
  ### Changed
18
36
  - Middleware: `skip_paths` semantics clarified — plain paths are exact-match only, use `/*` for prefix matching.
19
- - Middleware: TokenDecoder instances are now cached per JWKS fetch for performance improvement.
37
+ - Middleware: TokenDecoder instances are now cached per JWKs fetch for performance improvement.
20
38
  - Internal: RuboCop style fixes (`HashExcept`, `HashTransformKeys`, long line splits).
21
39
 
22
40
  ---
@@ -38,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
56
  - Rack middleware for verifying JWT access tokens from Keycloak
39
57
  - Support for OpenID Connect Discovery (`.well-known/openid-configuration`)
40
58
  - Handles up to 3 HTTP redirects and resolves relative `Location` headers
41
- - JWKS fetching with in-memory caching and ETag validation
59
+ - JWKs fetching with in-memory caching and ETag validation
42
60
  - RS256 JWT verification with `kid` matching
43
61
  - Claim validation: `aud`, `iss`, `exp`, `nbf`
44
62
  - Configurable via `discovery_url`, `audience`, and `skip_paths` options
@@ -55,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
55
73
  - RuboCop static analysis configuration
56
74
  - Structured error handling & responses:
57
75
  - Token/auth errors → **401 Unauthorized** with `WWW-Authenticate` header (RFC 6750)
58
- - Discovery/JWKS errors → **503 Service Unavailable**
76
+ - Discovery/JWKs errors → **503 Service Unavailable**
59
77
  - Structured error codes: `invalid_token`, `expired_token`, `not_yet_valid`,
60
78
  `invalid_issuer`, `invalid_audience`, `unsupported_algorithm`,
61
79
  `jwks_fetch_failed`, `jwks_parse_failed`, `jwks_cache_miss`,
data/README.md CHANGED
@@ -2,19 +2,19 @@
2
2
 
3
3
  [![CI](https://github.com/taiyaky/verikloak/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/taiyaky/verikloak/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://img.shields.io/gem/v/verikloak)](https://rubygems.org/gems/verikloak)
5
- ![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%203.0-blue)
5
+ ![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%203.1-blue)
6
6
  [![Downloads](https://img.shields.io/gem/dt/verikloak)](https://rubygems.org/gems/verikloak)
7
7
 
8
8
  A lightweight Rack middleware for verifying Keycloak JWT access tokens via OpenID Connect.
9
9
 
10
- Verikloak is a plug-and-play solution for Ruby (especially Rails API) apps that need to validate incoming `Bearer` tokens issued by Keycloak. It uses OpenID Connect Discovery and JWKS to fetch the public keys and verify JWT signatures securely.
10
+ Verikloak is a plug-and-play solution for Ruby (especially Rails API) apps that need to validate incoming `Bearer` tokens issued by Keycloak. It uses OpenID Connect Discovery and JWKs to fetch the public keys and verify JWT signatures securely.
11
11
 
12
12
  ---
13
13
 
14
14
  ## Features
15
15
 
16
16
  - OpenID Connect Discovery (`.well-known/openid-configuration`)
17
- - JWKs auto-fetch and in-memory caching with ETag support
17
+ - JWKs auto-fetching with in-memory caching and ETag support
18
18
  - RS256 JWT verification using `kid`
19
19
  - `aud`, `iss`, `exp`, `nbf` claim validation
20
20
  - Rails/Rack middleware support
@@ -53,7 +53,7 @@ config.middleware.use Verikloak::Middleware,
53
53
 
54
54
  #### Handling Authentication Failures
55
55
 
56
- When you use the Rack middleware, authentication failures are automatically converted into JSON error responses (for example, `401` for token issues, `503` for JWKS/discovery errors). In most cases **you do not need to add custom `rescue_from` handlers** in Rails controllers.
56
+ When you use the Rack middleware, authentication failures are automatically converted into JSON error responses (for example, `401` for token issues, `503` for JWKs/discovery errors). In most cases **you do not need to add custom `rescue_from` handlers** in Rails controllers.
57
57
 
58
58
  If you use Verikloak components directly (bypassing the Rack middleware) or prefer centralized error handling, rescue from the base class `Verikloak::Error`. You can also match subclasses such as `Verikloak::TokenDecoderError`, `Verikloak::DiscoveryError`, or `Verikloak::JwksCacheError` depending on your needs:
59
59
 
@@ -88,7 +88,7 @@ All Verikloak errors inherit from `Verikloak::Error`:
88
88
 
89
89
  - `Verikloak::TokenDecoderError` – token parsing/verification (`401 Unauthorized`)
90
90
  - `Verikloak::DiscoveryError` – OIDC discovery fetch/parse (`503 Service Unavailable`)
91
- - `Verikloak::JwksCacheError` – JWKS fetch/parse/cache (`503 Service Unavailable`)
91
+ - `Verikloak::JwksCacheError` – JWKs fetch/parse/cache (`503 Service Unavailable`)
92
92
  - `Verikloak::MiddlewareError` – header/infra issues surfaced by the middleware (usually `401`, sometimes `503`)
93
93
  ---
94
94
  #### Recommended: use environment variables in production
@@ -99,14 +99,7 @@ config.middleware.use Verikloak::Middleware,
99
99
  audience: ENV.fetch("CLIENT_ID"),
100
100
  skip_paths: ['/', '/health', '/public/*', '/rails/*']
101
101
  ```
102
- #### In production, set these variables in your environment for security and flexibility.
103
-
104
102
  This makes the configuration secure and flexible across environments.
105
-
106
- ```ruby
107
- request.env["verikloak.user"] # => JWT claims hash
108
- request.env["verikloak.token"] # => Raw JWT string
109
- ```
110
103
  ---
111
104
  ### Accessing claims in controllers
112
105
 
@@ -146,7 +139,7 @@ run ->(env) {
146
139
 
147
140
  1. Extracts the `Authorization: Bearer <token>` header
148
141
  2. Fetches the OIDC discovery document (only once or when expired)
149
- 3. Downloads JWKS public keys from the provided `jwks_uri`
142
+ 3. Downloads JWKs public keys from the provided `jwks_uri`
150
143
  4. Matches the `kid` from JWT header to select the right JWK
151
144
  5. Decodes and verifies the JWT using `RS256`
152
145
  6. Validates the following claims:
@@ -160,12 +153,12 @@ run ->(env) {
160
153
 
161
154
  ## Error Responses
162
155
 
163
- Verikloak returns JSON error responses in a consistent format with structured error codes. The HTTP status code reflects the nature of the error: 401 for client-side authentication issues, 503 for server-side discovery/JWKS errors, and 500 for unexpected internal errors.
156
+ Verikloak returns JSON error responses in a consistent format with structured error codes. The HTTP status code reflects the nature of the error: 401 for client-side authentication issues, 503 for server-side discovery/JWKs errors, and 500 for unexpected internal errors.
164
157
 
165
158
  ### Common HTTP Responses
166
159
 
167
160
  - `401 Unauthorized`: The access token is missing, invalid, expired, or otherwise not valid.
168
- - `503 Service Unavailable`: Discovery or JWKS fetch/parsing failed (server-side issue).
161
+ - `503 Service Unavailable`: Discovery or JWKs fetch/parsing failed (server-side issue).
169
162
  - `500 Internal Server Error`: An unexpected error occurred.
170
163
 
171
164
  ### Representative Examples
@@ -187,14 +180,14 @@ Verikloak returns JSON error responses in a consistent format with structured er
187
180
  ```json
188
181
  {
189
182
  "error": "jwks_fetch_failed",
190
- "message": "Failed to fetch JWKS keys"
183
+ "message": "Failed to fetch JWKs"
191
184
  }
192
185
  ```
193
186
 
194
187
  ```json
195
188
  {
196
189
  "error": "jwks_parse_failed",
197
- "message": "Failed to parse JWKS keys"
190
+ "message": "Failed to parse JWKs"
198
191
  }
199
192
  ```
200
193
 
@@ -225,9 +218,9 @@ Verikloak returns JSON error responses in a consistent format with structured er
225
218
  | `invalid_issuer` | 401 Unauthorized | Invalid `iss` claim |
226
219
  | `invalid_audience` | 401 Unauthorized | Invalid `aud` claim |
227
220
  | `not_yet_valid` | 401 Unauthorized | The token is not yet valid (`nbf` in the future) |
228
- | `jwks_fetch_failed` | 503 Service Unavailable | Failed to fetch JWKS keys |
229
- | `jwks_parse_failed` | 503 Service Unavailable | Failed to parse JWKS keys |
230
- | `jwks_cache_miss` | 503 Service Unavailable | JWKS cache is empty (e.g., 304 Not Modified without prior cache) |
221
+ | `jwks_fetch_failed` | 503 Service Unavailable | Failed to fetch JWKs |
222
+ | `jwks_parse_failed` | 503 Service Unavailable | Failed to parse JWKs |
223
+ | `jwks_cache_miss` | 503 Service Unavailable | JWKs cache is empty (e.g., 304 Not Modified without prior cache) |
231
224
  | `discovery_metadata_fetch_failed` | 503 Service Unavailable | Failed to fetch OIDC discovery document |
232
225
  | `discovery_metadata_invalid` | 503 Service Unavailable | Failed to parse OIDC discovery document |
233
226
  | `discovery_redirect_error` | 503 Service Unavailable | Discovery response was a redirect without a valid Location header |
@@ -249,7 +242,7 @@ For a full list of error cases and detailed explanations, please see the [ERRORS
249
242
  | `jwks_cache` | No | Inject custom JwksCache instance (advanced/testing) |
250
243
  | `leeway` | No | Clock skew tolerance (seconds) applied during JWT verification. Defaults to `TokenDecoder::DEFAULT_LEEWAY`. |
251
244
  | `token_verify_options` | No | Hash of advanced JWT verification options passed through to TokenDecoder. For example: `{ verify_iat: false, leeway: 10, algorithms: ["RS256"] }`. If both `leeway:` and `token_verify_options[:leeway]` are set, the latter takes precedence. |
252
- | `connection` | No | Inject a Faraday::Connection used for both Discovery and JWKS fetches. Allows unified timeout, retry, and headers. |
245
+ | `connection` | No | Inject a Faraday::Connection used for both Discovery and JWKs fetches. Allows unified timeout, retry, and headers. |
253
246
 
254
247
  #### Option: `skip_paths`
255
248
 
@@ -271,7 +264,7 @@ Paths **not matched** by any `skip_paths` entry will require a valid JWT.
271
264
  **Note:** Regex patterns are not supported. Only literal paths and `*` wildcards are allowed.
272
265
  Internally, `*` expands to match nested paths, so patterns like `/rails/*` are valid. This differs from regex — for example, `'/rails'` alone matches only `/rails`, while `'/rails/*'` covers both `/rails` and deeper subpaths.
273
266
 
274
- #### Customizing Faraday for Discovery and JWKS
267
+ #### Customizing Faraday for Discovery and JWKs
275
268
 
276
269
  Both `Discovery` and `JwksCache` accept a `Faraday::Connection`.
277
270
  This allows you to configure timeouts, retries, logging, and shared headers:
@@ -289,7 +282,7 @@ config.middleware.use Verikloak::Middleware,
289
282
  connection: connection
290
283
  )
291
284
  ```
292
- This makes it easy to apply consistent Faraday settings across both discovery and JWKS fetches.
285
+ This makes it easy to apply consistent Faraday settings across both discovery and JWKs fetches.
293
286
 
294
287
  ```ruby
295
288
  # Alternatively, you can pass the connection directly to the middleware:
@@ -325,9 +318,9 @@ config.middleware.use Verikloak::Middleware,
325
318
 
326
319
  #### Performance note
327
320
 
328
- Internally, Verikloak caches `TokenDecoder` instances per JWKS fetch to avoid reinitializing
321
+ Internally, Verikloak caches `TokenDecoder` instances per JWKs fetch to avoid reinitializing
329
322
  them on every request. This improves performance while still ensuring that keys are
330
- revalidated when JWKS is refreshed.
323
+ revalidated when JWKs is refreshed.
331
324
 
332
325
  ## Architecture
333
326
 
@@ -337,7 +330,7 @@ Verikloak consists of modular components, each with a focused responsibility:
337
330
  |----------------|--------------------------------------------------------|--------------|
338
331
  | `Middleware` | Rack-compatible entry point for token validation | Rack layer |
339
332
  | `Discovery` | Fetches OIDC discovery metadata (`.well-known`) | Network layer|
340
- | `JwksCache` | Fetches & caches JWKS public keys (with ETag) | Cache layer |
333
+ | `JwksCache` | Fetches & caches JWKs public keys (with ETag) | Cache layer |
341
334
  | `TokenDecoder` | Decodes and verifies JWTs (signature, exp, nbf, iss, aud) | Crypto layer |
342
335
  | `Errors` | Centralized error hierarchy | Core layer |
343
336
 
@@ -405,4 +398,4 @@ See [CHANGELOG.md](CHANGELOG.md) for release history.
405
398
 
406
399
  - [OpenID Connect Discovery 1.0 Spec](https://openid.net/specs/openid-connect-discovery-1_0.html)
407
400
  - [Keycloak Documentation: Securing Apps](https://www.keycloak.org/docs/latest/securing_apps/#openid-connect)
408
- - [JWT RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)
401
+ - [JWT RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)
@@ -35,7 +35,7 @@ module Verikloak
35
35
 
36
36
  # Raised for middleware-level failures while processing a Rack request.
37
37
  #
38
- # Examples include missing/invalid Authorization headers, JWKS cache
38
+ # Examples include missing/invalid Authorization headers, JWKs cache
39
39
  # initialization failures, or infrastructure issues detected by the
40
40
  # middleware itself.
41
41
  #
@@ -55,7 +55,7 @@ module Verikloak
55
55
  # @raise [TokenDecoderError] from {Verikloak::TokenDecoder#decode!}
56
56
  class TokenDecoderError < Error; end
57
57
 
58
- # Raised when JWKS fetching, validation, or cache handling fails.
58
+ # Raised when JWKs fetching, validation, or cache handling fails.
59
59
  #
60
60
  # Causes include HTTP failures, invalid JSON, missing required JWK fields,
61
61
  # or receiving 304 Not Modified without a prior cached value.
@@ -4,14 +4,14 @@ require 'faraday'
4
4
  require 'json'
5
5
 
6
6
  module Verikloak
7
- # Caches and revalidates JSON Web Key Sets (JWKS) fetched from a remote endpoint.
7
+ # Caches and revalidates JSON Web Key Sets (JWKs) fetched from a remote endpoint.
8
8
  #
9
9
  # This cache supports two HTTP cache mechanisms:
10
10
  # - **ETag revalidation** via `If-None-Match` → returns `304 Not Modified` when unchanged.
11
11
  # - **TTL freshness** via `Cache-Control: max-age` → avoids HTTP requests while fresh.
12
12
  #
13
13
  # On a successful `200 OK`, the cache:
14
- # - Parses the JWKS JSON (`{"keys":[...]}`) and validates each JWK has `kid`, `kty`, `n`, `e`.
14
+ # - Parses the JWKs JSON (`{"keys":[...]}`) and validates each JWK has `kid`, `kty`, `n`, `e`.
15
15
  # - Stores the keys in-memory, records `ETag`, and computes freshness from `Cache-Control`.
16
16
  #
17
17
  # On a `304 Not Modified`, the cache:
@@ -34,12 +34,12 @@ module Verikloak
34
34
  # adapters, and shared headers (kept consistent with Discovery).
35
35
  # `JwksCache.new(jwks_uri: "...", connection: Faraday.new { |f| f.request :retry })`
36
36
  class JwksCache
37
- # @param jwks_uri [String] HTTPS URL of the JWKS endpoint
37
+ # @param jwks_uri [String] HTTPS URL of the JWKs endpoint
38
38
  # @param connection [Faraday::Connection, nil] Optional Faraday connection for HTTP requests
39
39
  # @raise [JwksCacheError] if the URI is not an HTTP(S) URL
40
40
  def initialize(jwks_uri:, connection: nil)
41
41
  unless jwks_uri.is_a?(String) && jwks_uri.strip.match?(%r{^https?://})
42
- raise JwksCacheError.new('Invalid JWKS URI: must be a non-empty HTTP(S) URL', code: 'jwks_fetch_failed')
42
+ raise JwksCacheError.new('Invalid JWKs URI: must be a non-empty HTTP(S) URL', code: 'jwks_fetch_failed')
43
43
  end
44
44
 
45
45
  @jwks_uri = jwks_uri
@@ -50,7 +50,7 @@ module Verikloak
50
50
  @max_age = nil
51
51
  end
52
52
 
53
- # Fetches the JWKS and updates the in-memory cache.
53
+ # Fetches the JWKs and updates the in-memory cache.
54
54
  #
55
55
  # Performs an HTTP GET with `If-None-Match` when an ETag is present and handles:
56
56
  # - 200: parses/validates body, updates keys, ETag, TTL and `fetched_at`.
@@ -103,11 +103,11 @@ module Verikloak
103
103
  rescue Faraday::ConnectionFailed, Faraday::TimeoutError
104
104
  raise JwksCacheError.new('Connection failed', code: 'jwks_fetch_failed')
105
105
  rescue Faraday::Error => e
106
- raise JwksCacheError.new("JWKS fetch failed: #{e.message}", code: 'jwks_fetch_failed')
106
+ raise JwksCacheError.new("JWKs fetch failed: #{e.message}", code: 'jwks_fetch_failed')
107
107
  rescue JSON::ParserError
108
108
  raise JwksCacheError.new('Response is not valid JSON', code: 'jwks_parse_failed')
109
109
  rescue StandardError => e
110
- raise JwksCacheError.new("Unexpected JWKS fetch error: #{e.message}", code: 'jwks_fetch_failed')
110
+ raise JwksCacheError.new("Unexpected JWKs fetch error: #{e.message}", code: 'jwks_fetch_failed')
111
111
  end
112
112
 
113
113
  # @api private
@@ -161,7 +161,7 @@ module Verikloak
161
161
  end
162
162
 
163
163
  # @api private
164
- # Extracts and validates the `keys` array from a JWKS JSON document.
164
+ # Extracts and validates the `keys` array from a JWKs JSON document.
165
165
  # Ensures each key has `kid`, `kty`, `n`, and `e`.
166
166
  #
167
167
  # @param json [Hash]
@@ -213,12 +213,12 @@ module Verikloak
213
213
  # Revalidation succeeded; update freshness from 304 headers if present
214
214
  process_not_modified(response)
215
215
  else
216
- raise JwksCacheError.new("Failed to fetch JWKS: status #{response.status}", code: 'jwks_fetch_failed')
216
+ raise JwksCacheError.new("Failed to fetch JWKs: status #{response.status}", code: 'jwks_fetch_failed')
217
217
  end
218
218
  end
219
219
 
220
220
  # @api private
221
- # Handles a 200 OK JWKS response.
221
+ # Handles a 200 OK JWKs response.
222
222
  # @param response [Faraday::Response]
223
223
  # @return [Array&lt;Hash&gt;] parsed and cached keys
224
224
  def process_successful_response(response)
@@ -229,7 +229,7 @@ module Verikloak
229
229
  end
230
230
 
231
231
  # @api private
232
- # Handles a 304 Not Modified JWKS response: updates TTL and timestamp, returns cached keys.
232
+ # Handles a 304 Not Modified JWKs response: updates TTL and timestamp, returns cached keys.
233
233
  # @param response [Faraday::Response]
234
234
  # @return [Array&lt;Hash&gt;]
235
235
  # @raise [JwksCacheError] when cache is empty
@@ -246,7 +246,7 @@ module Verikloak
246
246
  # @return [Array&lt;Hash&gt;]
247
247
  # @raise [JwksCacheError]
248
248
  def return_from_cache_or_fail
249
- @cached_keys || raise(JwksCacheError.new('JWKS cache is empty but received 304 Not Modified',
249
+ @cached_keys || raise(JwksCacheError.new('JWKs cache is empty but received 304 Not Modified',
250
250
  code: 'jwks_cache_miss'))
251
251
  end
252
252
  end
@@ -86,12 +86,12 @@ module Verikloak
86
86
 
87
87
  # @api private
88
88
  #
89
- # Internal mixin for JWT verification and discovery/JWKS management.
89
+ # Internal mixin for JWT verification and discovery/JWKs management.
90
90
  # Extracted from Middleware to reduce class length and improve clarity.
91
91
  module MiddlewareTokenVerification
92
92
  private
93
93
 
94
- # Determines whether a token verification failure warrants a one-time JWKS refresh
94
+ # Determines whether a token verification failure warrants a one-time JWKs refresh
95
95
  # and retry (e.g., after key rotation).
96
96
  #
97
97
  # @param error [Exception]
@@ -105,7 +105,7 @@ module Verikloak
105
105
  end
106
106
 
107
107
  # Returns a cached TokenDecoder instance for current inputs.
108
- # Cache key uses issuer, audience, leeway, token_verify_options, and JWKS fetched_at timestamp.
108
+ # Cache key uses issuer, audience, leeway, token_verify_options, and JWKs fetched_at timestamp.
109
109
  def decoder_for
110
110
  keys = @jwks_cache.cached
111
111
  fetched_at = @jwks_cache.respond_to?(:fetched_at) ? @jwks_cache.fetched_at : nil
@@ -127,7 +127,7 @@ module Verikloak
127
127
  end
128
128
  end
129
129
 
130
- # Ensures JWKS are up-to-date by invoking {#ensure_jwks_cache!}.
130
+ # Ensures JWKs are up-to-date by invoking {#ensure_jwks_cache!}.
131
131
  # Errors are not swallowed and are handled by the caller.
132
132
  #
133
133
  # @return [void]
@@ -136,8 +136,8 @@ module Verikloak
136
136
  ensure_jwks_cache!
137
137
  end
138
138
 
139
- # Decodes and verifies the JWT using the cached JWKS. On certain verification
140
- # failures (e.g., key rotation), it refreshes the JWKS and retries once.
139
+ # Decodes and verifies the JWT using the cached JWKs. On certain verification
140
+ # failures (e.g., key rotation), it refreshes the JWKs and retries once.
141
141
  #
142
142
  # @param token [String]
143
143
  # @return [Hash] decoded JWT claims
@@ -145,7 +145,7 @@ module Verikloak
145
145
  def decode_token(token)
146
146
  ensure_jwks_cache!
147
147
  if @jwks_cache.cached.nil? || @jwks_cache.cached.empty?
148
- raise MiddlewareError.new('JWKS cache is empty, cannot verify token', code: 'jwks_cache_miss')
148
+ raise MiddlewareError.new('JWKs cache is empty, cannot verify token', code: 'jwks_cache_miss')
149
149
  end
150
150
 
151
151
  # First attempt
@@ -154,7 +154,7 @@ module Verikloak
154
154
  begin
155
155
  decoder.decode!(token)
156
156
  rescue TokenDecoderError => e
157
- # On key rotation or signature mismatch, refresh JWKS and retry once.
157
+ # On key rotation or signature mismatch, refresh JWKs and retry once.
158
158
  raise unless retryable_decoder_error?(e)
159
159
 
160
160
  refresh_jwks!
@@ -165,11 +165,11 @@ module Verikloak
165
165
  end
166
166
  end
167
167
 
168
- # Ensures that discovery metadata and JWKS cache are initialized and up-to-date.
168
+ # Ensures that discovery metadata and JWKs cache are initialized and up-to-date.
169
169
  # This method is thread-safe.
170
170
  #
171
171
  # * When the cache instance is missing, it is created from discovery metadata.
172
- # * JWKS are (re)fetched every time; ETag/Cache-Control headers minimize traffic.
172
+ # * JWKs are (re)fetched every time; ETag/Cache-Control headers minimize traffic.
173
173
  #
174
174
  # @return [void]
175
175
  # @raise [Verikloak::DiscoveryError, Verikloak::JwksCacheError, Verikloak::MiddlewareError]
@@ -188,7 +188,7 @@ module Verikloak
188
188
  # Re-raise so that specific error codes can be mapped in the middleware
189
189
  raise e
190
190
  rescue StandardError => e
191
- raise MiddlewareError.new("Failed to initialize JWKS cache: #{e.message}", code: 'jwks_fetch_failed')
191
+ raise MiddlewareError.new("Failed to initialize JWKs cache: #{e.message}", code: 'jwks_fetch_failed')
192
192
  end
193
193
  end
194
194
 
@@ -267,7 +267,7 @@ module Verikloak
267
267
  end
268
268
 
269
269
  # Rack middleware that verifies incoming JWT access tokens (Keycloak) using
270
- # OpenID Connect discovery and JWKS. On success, it populates:
270
+ # OpenID Connect discovery and JWKs. On success, it populates:
271
271
  #
272
272
  # * `env['verikloak.token']` — the raw JWT string
273
273
  # * `env['verikloak.user']` — the decoded JWT claims Hash
@@ -283,7 +283,7 @@ module Verikloak
283
283
  # @param audience [String] expected `aud` claim
284
284
  # @param skip_paths [Array<String>] literal paths or wildcard patterns to bypass auth
285
285
  # @param discovery [Discovery, nil] custom discovery instance (for DI/tests)
286
- # @param jwks_cache [JwksCache, nil] custom JWKS cache instance (for DI/tests)
286
+ # @param jwks_cache [JwksCache, nil] custom JWKs cache instance (for DI/tests)
287
287
  # @param connection [Faraday::Connection, nil] Optional injected Faraday connection (defaults to Faraday.new)
288
288
  # @param leeway [Integer] Clock skew tolerance in seconds for token verification (delegated to TokenDecoder)
289
289
  # @param token_verify_options [Hash] Additional JWT verification options passed through
@@ -332,7 +332,7 @@ module Verikloak
332
332
 
333
333
  private
334
334
 
335
- # Returns the Faraday connection used for HTTP operations (Discovery/JWKS).
335
+ # Returns the Faraday connection used for HTTP operations (Discovery/JWKs).
336
336
  # Exposed for tests; not part of public API.
337
337
  def http_connection
338
338
  @connection
@@ -3,7 +3,7 @@
3
3
  require 'jwt'
4
4
 
5
5
  module Verikloak
6
- # Verifies JWT tokens using a JWKS key set.
6
+ # Verifies JWT tokens using a JWKs.
7
7
  #
8
8
  # This class validates a JWT's signature and standard claims (`iss`, `aud`, `exp`, `nbf`, etc.)
9
9
  # using the appropriate RSA public key selected by the JWT's `kid` header.
@@ -24,7 +24,7 @@ module Verikloak
24
24
  # Default clock skew tolerance in seconds.
25
25
  DEFAULT_LEEWAY = 60
26
26
 
27
- # Initializes the decoder with a JWKS and verification criteria.
27
+ # Initializes the decoder with a JWKs and verification criteria.
28
28
  #
29
29
  # @param jwks [Array<Hash>] List of JWKs from the discovery document.
30
30
  # @param issuer [String] Expected `iss` value in the token.
@@ -106,7 +106,7 @@ module Verikloak
106
106
  kid = fetch_indifferent(header, 'kid')
107
107
  jwk = @jwk_by_kid[kid]
108
108
 
109
- raise TokenDecoderError.new("Key with kid=#{kid} not found in JWKS", code: 'invalid_token') unless jwk
109
+ raise TokenDecoderError.new("Key with kid=#{kid} not found in JWKs", code: 'invalid_token') unless jwk
110
110
 
111
111
  jwk
112
112
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Verikloak
4
4
  # Defines the current version of the Verikloak gem.
5
- VERSION = '0.1.2'
5
+ VERSION = '0.1.4'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verikloak
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - taiyaky
@@ -47,20 +47,26 @@ dependencies:
47
47
  name: jwt
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
50
- - - "~>"
50
+ - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: '2.7'
53
+ - - "<"
54
+ - !ruby/object:Gem::Version
55
+ version: '4.0'
53
56
  type: :runtime
54
57
  prerelease: false
55
58
  version_requirements: !ruby/object:Gem::Requirement
56
59
  requirements:
57
- - - "~>"
60
+ - - ">="
58
61
  - !ruby/object:Gem::Version
59
62
  version: '2.7'
63
+ - - "<"
64
+ - !ruby/object:Gem::Version
65
+ version: '4.0'
60
66
  description: |
61
67
  Verikloak is a lightweight Ruby gem that provides JWT access token verification middleware
62
68
  for Rack-based applications, including Rails API mode. It uses OpenID Connect discovery
63
- and JWKS to securely validate tokens issued by Keycloak.
69
+ and JWKs to securely validate tokens issued by Keycloak.
64
70
  executables: []
65
71
  extensions: []
66
72
  extra_rdoc_files: []
@@ -82,7 +88,7 @@ metadata:
82
88
  source_code_uri: https://github.com/taiyaky/verikloak
83
89
  changelog_uri: https://github.com/taiyaky/verikloak/blob/main/CHANGELOG.md
84
90
  bug_tracker_uri: https://github.com/taiyaky/verikloak/issues
85
- documentation_uri: https://rubydoc.info/gems/verikloak/0.1.2
91
+ documentation_uri: https://rubydoc.info/gems/verikloak/0.1.4
86
92
  rubygems_mfa_required: 'true'
87
93
  rdoc_options: []
88
94
  require_paths:
@@ -91,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
97
  requirements:
92
98
  - - ">="
93
99
  - !ruby/object:Gem::Version
94
- version: '3.0'
100
+ version: '3.1'
95
101
  required_rubygems_version: !ruby/object:Gem::Requirement
96
102
  requirements:
97
103
  - - ">="