rack-jwt-verifier 0.1.0 → 0.3.0

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.
@@ -1,99 +1,260 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "jwt"
4
- require "net/http"
5
- require "json"
4
+ require_relative "errors"
5
+ require_relative "in_process_cache"
6
+ require_relative "key_source"
7
+ require_relative "replay_guard"
6
8
 
7
9
  module RackJwtVerifier
8
- # This class handles the cryptographic heavy lifting: fetching and caching
9
- # public keys from the SSO provider, and performing the actual JWT decoding
10
- # and signature verification.
10
+ # Decodes and verifies JWTs against key material from a configured source:
11
+ # a static PEM, a PEM served at a URL, a JWKS endpoint, or a shared HMAC
12
+ # secret. Handles caching, key rotation, claim enforcement and optional jti
13
+ # replay protection so the middleware does not have to.
11
14
  class Verifier
12
- # Error raised if we fail to fetch keys from the remote URL
13
- class KeyFetchError < StandardError; end
14
-
15
- # The cache key used to store the public key PEM string
16
- PUBLIC_KEY_CACHE_KEY = 'rack_jwt_verifier:public_key'.freeze
17
- # The TTL for the cache (5 minutes, must match the default in InProcessCache)
18
- CACHE_TTL_SECONDS = 300
19
-
20
- # Default options for JWT decoding to ensure strict security compliance
15
+ # Kept under the old constant so existing `rescue Verifier::KeyFetchError`
16
+ # code keeps working.
17
+ KeyFetchError = RackJwtVerifier::KeyFetchError
18
+
19
+ # Cache namespace for a fetched PEM; the full key also carries a digest of the URL.
20
+ PUBLIC_KEY_CACHE_KEY = KeySource::RemotePem::CACHE_KEY_PREFIX
21
+ # Cache namespace for a fetched JWKS; the full key also carries a digest of the URL.
22
+ JWKS_CACHE_KEY = KeySource::RemoteJwks::CACHE_KEY_PREFIX
23
+ # The default TTL for cached key material (5 minutes)
24
+ CACHE_TTL_SECONDS = KeySource::Remote::DEFAULT_CACHE_TTL
25
+ # Default open/read timeout (seconds) for fetching key material
26
+ DEFAULT_HTTP_TIMEOUT = KeySource::Remote::DEFAULT_HTTP_TIMEOUT
27
+ # Largest response body (bytes) accepted as key material
28
+ MAX_KEY_RESPONSE_BYTES = KeySource::Remote::MAX_RESPONSE_BYTES
29
+ # Minimum gap (seconds) between rotation-triggered refetches
30
+ DEFAULT_REFETCH_INTERVAL = KeySource::Remote::DEFAULT_REFETCH_INTERVAL
31
+
32
+ # Exactly one of these tells the Verifier where its key comes from. Having
33
+ # shared_secret in the same exclusive set is what rules out "a secret next
34
+ # to a public key" — the classic RS256/HS256 confusion setup.
35
+ KEY_SOURCE_OPTIONS = %i[public_key public_key_url jwks_url shared_secret].freeze
36
+
37
+ # The HMAC family, only ever enabled by shared_secret.
38
+ HMAC_ALGORITHMS = %w[HS256 HS384 HS512].freeze
39
+
40
+ # RFC 7518 §3.2: an HMAC key must be at least as long as the hash output.
41
+ # Same numbers jwt_auth_client enforces on the signing side.
42
+ MIN_SECRET_BYTES = { "HS256" => 32, "HS384" => 48, "HS512" => 64 }.freeze
43
+
44
+ # Default accepted algorithms per key-source family.
45
+ DEFAULT_ASYMMETRIC_ALGORITHMS = %w[RS256].freeze
46
+ DEFAULT_HMAC_ALGORITHMS = %w[HS256].freeze
47
+
48
+ # ruby-jwt only validates an expected claim value (e.g. `iss: "..."`) when
49
+ # the matching `verify_*` flag is also set. Map each claim to its flag so we
50
+ # can switch the flag on automatically whenever a value is supplied.
51
+ CLAIM_VERIFY_FLAGS = {
52
+ iss: :verify_iss,
53
+ aud: :verify_aud,
54
+ sub: :verify_sub
55
+ }.freeze
56
+
57
+ # Default options for JWT decoding to ensure strict security compliance.
58
+ # ruby-jwt only checks exp/nbf when the claim is present, so `exp` is also
59
+ # listed as required: a token with no expiry would otherwise be valid forever.
21
60
  DEFAULT_DECODE_OPTIONS = {
22
- algorithm: "RS256", # Must match your SSO provider's algorithm
23
- # THIS MUST BE TRUE: Ensures the 'exp' claim is checked during decoding
24
61
  verify_expiration: true,
25
62
  verify_not_before: true,
26
- leeway: 60, # Allow a 60-second clock skew for "exp" and "nbf" claims
27
- # 'iss' validation will be added later when we configure the Verifier.
28
- # verify_iss: true
63
+ required_claims: %w[exp].freeze,
64
+ leeway: 60 # Allow a 60-second clock skew for "exp" and "nbf" claims
29
65
  }.freeze
30
66
 
31
67
  # @param options [Hash] Configuration options.
32
- # @option options [String] :public_key_url The URL to fetch the public key.
68
+ # @option options [String, OpenSSL::PKey] :public_key A static PEM public key or X.509 certificate.
69
+ # @option options [String] :public_key_url The https:// URL serving a PEM public key or certificate.
70
+ # @option options [String] :jwks_url The https:// URL serving a JSON Web Key Set.
71
+ # @option options [String, Hash] :shared_secret An HMAC secret (String) or `{ env: "VAR" }`.
72
+ # Enables the HS* algorithms; mutually exclusive with the public-key sources.
73
+ # @option options [Array<String>] :algorithms Accepted signing algorithms
74
+ # (default: ["RS256"], or ["HS256"] with :shared_secret).
75
+ # @option options [Boolean] :allow_insecure_http Permit a plain http:// URL (development only).
76
+ # @option options [Numeric] :http_timeout Open/read/write timeout in seconds for the key fetch.
77
+ # @option options [Integer] :cache_ttl Seconds to cache fetched key material.
78
+ # @option options [Numeric] :refetch_interval Minimum seconds between rotation-triggered refetches.
33
79
  # @option options [Object] :cache_store Optional custom cache object (must respond to #read and #write).
80
+ # @option options [Boolean, Object] :replay_cache `true` to track `jti` in :cache_store (or a fresh
81
+ # InProcessCache), or a cache store to track it in. Tokens are then required to carry a `jti`
82
+ # and a second presentation before `exp` is rejected.
83
+ # @option options [Logger] :logger Where cache-store failures are reported (default: silent).
34
84
  # @option options [Hash] :decode_options Custom options for JWT.decode.
85
+ # @raise [ConfigurationError] for an unusable option set.
35
86
  def initialize(options = {})
36
- @public_key_url = options.fetch(:public_key_url)
37
-
38
- # Inject cache store, defaulting to the simple InProcessCache.
39
- # This allows users to pass in a Redis/Memcached client that responds to #read and #write.
40
- @cache = options.fetch(:cache_store, InProcessCache.new)
41
-
42
- # Merge default options over any user-provided options
43
- @decode_options = DEFAULT_DECODE_OPTIONS.merge(options.fetch(:decode_options, {}))
87
+ source_name = key_source_name(options)
88
+ @key_source = build_key_source(source_name, options)
89
+ @algorithms = build_algorithms(source_name, options)
90
+ @decode_options = build_decode_options(options.fetch(:decode_options, {}), replay: replay_wanted?(options))
91
+ @replay_guard = build_replay_guard(options, @decode_options[:leeway])
44
92
  end
45
93
 
94
+ # The algorithms this verifier accepts, after defaults and policy.
95
+ # @return [Array<String>]
96
+ attr_reader :algorithms
97
+
46
98
  # Decodes and verifies the JWT.
47
99
  # @param token [String] The JWT string from the Authorization header.
48
100
  # @return [Hash] The decoded payload (the user claims).
49
- # @raise [JWT::DecodeError] If the token is invalid, expired, or signature fails.
101
+ # @raise [JWT::DecodeError] If the token is invalid, expired, replayed, or signature fails.
102
+ # @raise [KeyFetchError] If the key material could not be obtained.
103
+ # @raise [ReplayCacheError] If replay protection is on and its store is unavailable.
50
104
  def verify(token)
51
- # 1. Fetch the key from cache or network
52
- key = fetch_public_key
53
-
54
- # 2. Perform the cryptographic verification and claim validation
55
- # The `true` is required to enable verification checks.
56
- payload, _header = JWT.decode(token, key, true, @decode_options)
57
-
58
- # For standard usage, we only need the payload hash
105
+ payload = decode_with_rotation(token)
106
+ # Only a token that passed every other check is recorded: an expired or
107
+ # mis-signed token must not be able to "burn" a jti.
108
+ @replay_guard&.check!(payload)
59
109
  payload
60
110
  end
61
111
 
62
112
  private
63
113
 
64
- # Handles fetching the public key from the remote URL, using the injected cache.
65
- def fetch_public_key
66
- # 1. Try to read the PEM string from the cache
67
- cached_pem = @cache.read(PUBLIC_KEY_CACHE_KEY)
68
-
69
- if cached_pem
70
- # Found in cache, convert PEM to OpenSSL object and return
71
- return OpenSSL::PKey::RSA.new(cached_pem)
114
+ def decode_with_rotation(token)
115
+ decode(token)
116
+ rescue JWT::VerificationError
117
+ # A signature mismatch may mean the provider rotated its key. Refresh
118
+ # once (rate-limited) and retry; if nothing was refreshed, or the retry
119
+ # fails too, the token is simply bad.
120
+ raise unless @key_source.refresh!
121
+
122
+ decode(token)
123
+ end
124
+
125
+ # Performs the cryptographic verification and claim validation. The `true`
126
+ # is required to enable verification checks; only the payload is returned.
127
+ def decode(token)
128
+ payload, _header =
129
+ if @key_source.jwks?
130
+ JWT.decode(token, nil, true, @decode_options.merge(jwks: @key_source.jwks_loader))
131
+ else
132
+ JWT.decode(token, @key_source.verification_key, true, @decode_options)
133
+ end
134
+ payload
135
+ end
136
+
137
+ def key_source_name(options)
138
+ given = KEY_SOURCE_OPTIONS.select { |name| options[name] }
139
+ return given.first if given.size == 1
140
+
141
+ raise ConfigurationError,
142
+ "exactly one of #{KEY_SOURCE_OPTIONS.map(&:inspect).join(', ')} must be given" \
143
+ "#{" (got #{given.map(&:inspect).join(' and ')})" unless given.empty?}"
144
+ end
145
+
146
+ def build_key_source(name, options)
147
+ case name
148
+ when :public_key
149
+ KeySource::Static.new(options[:public_key])
150
+ when :public_key_url
151
+ KeySource::RemotePem.new(url: options[:public_key_url], **remote_options(options))
152
+ when :jwks_url
153
+ KeySource::RemoteJwks.new(url: options[:jwks_url], **remote_options(options))
154
+ when :shared_secret
155
+ KeySource::Secret.new(options[:shared_secret])
72
156
  end
157
+ end
158
+
159
+ def remote_options(options)
160
+ {
161
+ # Inject cache store, defaulting to the simple InProcessCache. This
162
+ # allows users to pass in a cache store that responds to #read and #write.
163
+ cache: options.fetch(:cache_store) { InProcessCache.new },
164
+ cache_ttl: options.fetch(:cache_ttl, CACHE_TTL_SECONDS),
165
+ http_timeout: options.fetch(:http_timeout, DEFAULT_HTTP_TIMEOUT),
166
+ refetch_interval: options.fetch(:refetch_interval, DEFAULT_REFETCH_INTERVAL),
167
+ allow_insecure_http: options.fetch(:allow_insecure_http, false),
168
+ logger: options[:logger]
169
+ }
170
+ end
73
171
 
74
- # 2. Key is missing or expired, fetch it from the network
75
- uri = URI(@public_key_url)
76
- response = Net::HTTP.get_response(uri)
172
+ # The effective algorithm list, from (in order of precedence) a
173
+ # decode_options :algorithm, the top-level :algorithms, a decode_options
174
+ # :algorithms, or the family default — then checked against the policy:
175
+ # never "none", and HS* only with a shared secret, never mixed with
176
+ # asymmetric algorithms. ruby-jwt would happily verify an RS256 token's
177
+ # public key as an HMAC secret otherwise.
178
+ def build_algorithms(source_name, options)
179
+ decode_options = options.fetch(:decode_options, {})
180
+ hmac = source_name == :shared_secret
181
+
182
+ list = decode_options[:algorithm] || options[:algorithms] || decode_options[:algorithms] ||
183
+ (hmac ? DEFAULT_HMAC_ALGORITHMS : DEFAULT_ASYMMETRIC_ALGORITHMS)
184
+ list = Array(list).map { |alg| alg.to_s.strip }.reject(&:empty?)
185
+ raise ConfigurationError, "algorithms must list at least one algorithm" if list.empty?
186
+
187
+ if list.any? { |alg| alg.casecmp?("none") }
188
+ raise ConfigurationError, '"none" is not an acceptable algorithm: it disables signature verification'
189
+ end
190
+
191
+ hmac_algs, other_algs = list.partition { |alg| HMAC_ALGORITHMS.include?(alg.upcase) }
192
+ if hmac
193
+ unless other_algs.empty?
194
+ raise ConfigurationError,
195
+ "shared_secret only works with #{HMAC_ALGORITHMS.join('/')}; " \
196
+ "remove #{other_algs.join(', ')} from algorithms or use a public key source"
197
+ end
198
+ check_secret_length!(hmac_algs.map(&:upcase))
199
+ elsif hmac_algs.any?
200
+ raise ConfigurationError,
201
+ "#{hmac_algs.join(', ')} require shared_secret; they cannot be mixed with public-key algorithms " \
202
+ "(a public key would be accepted as the HMAC secret)"
203
+ end
204
+
205
+ list.freeze
206
+ end
207
+
208
+ def check_secret_length!(hmac_algs)
209
+ needed = hmac_algs.map { |alg| MIN_SECRET_BYTES.fetch(alg) }.max
210
+ actual = @key_source.verification_key.bytesize
211
+ return if actual >= needed
212
+
213
+ raise ConfigurationError,
214
+ "shared_secret must be at least #{needed} bytes for #{hmac_algs.join('/')} (got #{actual}); " \
215
+ "generate one with: openssl rand -hex #{needed}"
216
+ end
217
+
218
+ def replay_wanted?(options)
219
+ store = options[:replay_cache]
220
+ !(store.nil? || store == false)
221
+ end
222
+
223
+ def build_replay_guard(options, leeway)
224
+ return nil unless replay_wanted?(options)
225
+
226
+ store = options[:replay_cache]
227
+ store = options.fetch(:cache_store) { InProcessCache.new } if store == true
228
+ unless store.respond_to?(:read) && store.respond_to?(:write)
229
+ raise ConfigurationError, "replay_cache must be true or a cache store responding to #read and #write"
230
+ end
231
+
232
+ ReplayGuard.new(store, leeway: leeway)
233
+ end
234
+
235
+ # Merge default options over any user-provided options, then:
236
+ # - install the policy-checked algorithm list and drop any :algorithm so
237
+ # ruby-jwt (which consults :algorithm first) sees exactly that list;
238
+ # - make sure an expected claim value is actually enforced: `iss: "x"` on
239
+ # its own is a no-op in ruby-jwt unless `verify_iss: true` accompanies
240
+ # it. An explicit `verify_*: false` from the user is left untouched;
241
+ # - require a jti when replay protection is on.
242
+ def build_decode_options(user_options, replay:)
243
+ merged = DEFAULT_DECODE_OPTIONS.merge(user_options)
244
+ merged.delete(:algorithm)
245
+ merged[:algorithms] = @algorithms
246
+
247
+ CLAIM_VERIFY_FLAGS.each do |claim, flag|
248
+ merged[flag] = true if merged.key?(claim) && !merged.key?(flag)
249
+ end
250
+ merged[:verify_jti] = true if replay && !merged.key?(:verify_jti)
77
251
 
78
- unless response.is_a?(Net::HTTPSuccess)
79
- raise KeyFetchError, "Failed to fetch public key from #{@public_key_url}: #{response.code}"
252
+ leeway = merged[:leeway]
253
+ unless leeway.is_a?(Numeric) && leeway >= 0
254
+ raise ConfigurationError, "decode_options[:leeway] must be a non-negative number of seconds"
80
255
  end
81
256
 
82
- # 3. Process response
83
- public_key_pem = response.body
84
-
85
- # 4. Cache the new key PEM string
86
- @cache.write(PUBLIC_KEY_CACHE_KEY, public_key_pem, expires_in: CACHE_TTL_SECONDS)
87
-
88
- # 5. Return the OpenSSL object for verification
89
- OpenSSL::PKey::RSA.new(public_key_pem)
90
-
91
- rescue KeyFetchError
92
- # Re-raise explicit KeyFetchError for easier debugging/rescue in middleware
93
- raise
94
- rescue StandardError => e
95
- # Catch all other network/parsing/OpenSSL errors
96
- raise KeyFetchError, "Error processing public key: #{e.message}"
257
+ merged
97
258
  end
98
259
  end
99
260
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RackJwtVerifier
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,20 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This file serves as the main entry point for the rack_jwt_verifier gem.
4
- # When a user calls `require 'rack_jwt_verifier'`, this file is loaded.
3
+ # Entry point for the gem: `require "rack_jwt_verifier"` loads everything.
4
+ # (`require "rack-jwt-verifier"`, the gem's name, works too.)
5
5
 
6
6
  require_relative "rack_jwt_verifier/version"
7
-
8
- # Require core component files so they are available under the RackJwtVerifier module.
9
- # IMPORTANT: These paths rely on you moving `jwt_helper.rb` into the
10
- # `lib/rack_jwt_verifier/` directory.
11
- require_relative "rack_jwt_verifier/jwt_helper"
7
+ require_relative "rack_jwt_verifier/errors"
8
+ require_relative "rack_jwt_verifier/in_process_cache"
9
+ require_relative "rack_jwt_verifier/key_source"
10
+ require_relative "rack_jwt_verifier/replay_guard"
11
+ require_relative "rack_jwt_verifier/scopes"
12
12
  require_relative "rack_jwt_verifier/verifier"
13
13
  require_relative "rack_jwt_verifier/middleware"
14
- require_relative "rack_jwt_verifier/in_process_cache"
15
-
14
+ require_relative "rack_jwt_verifier/jwt_helper"
16
15
 
17
- # The main namespace module for the gem. All classes (JwtHelper, Verifier,
18
- # Middleware) are now accessible via RackJwtVerifier::ClassName.
16
+ # Namespace for the gem: Middleware, Verifier, KeySource, Scopes, ReplayGuard,
17
+ # InProcessCache and the deprecated JwtHelper.
19
18
  module RackJwtVerifier
20
19
  end
metadata CHANGED
@@ -1,146 +1,92 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-jwt-verifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniele Frisanco
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-20 00:00:00.000000000 Z
11
+ date: 2026-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '2.8'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '4'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '2.8'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '4'
27
33
  - !ruby/object:Gem::Dependency
28
- name: rack
34
+ name: logger
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - ">="
32
38
  - !ruby/object:Gem::Version
33
- version: '2.0'
39
+ version: '1.4'
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - ">="
39
45
  - !ruby/object:Gem::Version
40
- version: '2.0'
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '2.0'
55
- - !ruby/object:Gem::Dependency
56
- name: rake
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '13.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '13.0'
46
+ version: '1.4'
69
47
  - !ruby/object:Gem::Dependency
70
- name: rspec
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.0'
83
- - !ruby/object:Gem::Dependency
84
- name: webmock
48
+ name: rack
85
49
  requirement: !ruby/object:Gem::Requirement
86
50
  requirements:
87
- - - "~>"
51
+ - - ">="
88
52
  - !ruby/object:Gem::Version
89
- version: '3.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
53
+ version: '2.2'
54
+ - - "<"
95
55
  - !ruby/object:Gem::Version
96
- version: '3.0'
97
- - !ruby/object:Gem::Dependency
98
- name: timecop
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '0.9'
104
- type: :development
56
+ version: '4'
57
+ type: :runtime
105
58
  prerelease: false
106
59
  version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '0.9'
111
- - !ruby/object:Gem::Dependency
112
- name: rack-test
113
- requirement: !ruby/object:Gem::Requirement
114
60
  requirements:
115
61
  - - ">="
116
62
  - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
63
+ version: '2.2'
64
+ - - "<"
123
65
  - !ruby/object:Gem::Version
124
- version: '0'
125
- description: Verifies JWT signature, validates claims (expiration, issuer), and handles
126
- public key retrieval to ensure requests are securely authenticated by an external
127
- SSO provider.
66
+ version: '4'
67
+ description: Verifies JWT signatures against a JWKS endpoint, a PEM URL, a static
68
+ key or (opt-in) a shared HMAC secret; enforces exp/nbf/iss/aud, scopes and optional
69
+ jti replay protection; caches key material, handles key rotation, and exposes the
70
+ verified claims to the application through the Rack environment. Pairs with the
71
+ jwt_auth_client gem.
128
72
  email:
129
73
  - daniele.frisanco@gmail.com
130
74
  executables: []
131
75
  extensions: []
132
76
  extra_rdoc_files: []
133
77
  files:
134
- - ".rspec_status"
135
78
  - CHANGELOG.md
136
- - Gemfile
137
- - Gemfile.lock
138
79
  - LICENSE.md
139
80
  - README.md
81
+ - lib/rack-jwt-verifier.rb
140
82
  - lib/rack_jwt_verifier.rb
83
+ - lib/rack_jwt_verifier/errors.rb
141
84
  - lib/rack_jwt_verifier/in_process_cache.rb
142
85
  - lib/rack_jwt_verifier/jwt_helper.rb
86
+ - lib/rack_jwt_verifier/key_source.rb
143
87
  - lib/rack_jwt_verifier/middleware.rb
88
+ - lib/rack_jwt_verifier/replay_guard.rb
89
+ - lib/rack_jwt_verifier/scopes.rb
144
90
  - lib/rack_jwt_verifier/verifier.rb
145
91
  - lib/rack_jwt_verifier/version.rb
146
92
  homepage: https://github.com/danielefrisanco/rack_jwt_verifier
@@ -150,7 +96,8 @@ metadata:
150
96
  allowed_push_host: https://rubygems.org
151
97
  homepage_uri: https://github.com/danielefrisanco/rack_jwt_verifier
152
98
  source_code_uri: https://github.com/danielefrisanco/rack_jwt_verifier
153
- changelog_uri: https://github.com/danielefrisanco/rack_jwt_verifier/CHANGELOG.md
99
+ changelog_uri: https://github.com/danielefrisanco/rack_jwt_verifier/blob/main/CHANGELOG.md
100
+ rubygems_mfa_required: 'true'
154
101
  post_install_message:
155
102
  rdoc_options: []
156
103
  require_paths:
@@ -159,16 +106,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
106
  requirements:
160
107
  - - ">="
161
108
  - !ruby/object:Gem::Version
162
- version: 2.6.6
109
+ version: '3.1'
163
110
  required_rubygems_version: !ruby/object:Gem::Requirement
164
111
  requirements:
165
112
  - - ">="
166
113
  - !ruby/object:Gem::Version
167
114
  version: '0'
168
115
  requirements: []
169
- rubygems_version: 3.2.3
116
+ rubygems_version: 3.3.26
170
117
  signing_key:
171
118
  specification_version: 4
172
- summary: A Rack middleware for authenticating requests using JWTs (JSON Web Tokens)
173
- and injecting user data into the Rack environment.
119
+ summary: Rack middleware that authenticates requests with JWTs from an external identity
120
+ provider.
174
121
  test_files: []
data/.rspec_status DELETED
@@ -1,26 +0,0 @@
1
- example_id | status | run_time |
2
- ---------------------------------------------------- | ------ | --------------- |
3
- ./spec/rack_jwt_verifier/jwt_helper_spec.rb[1:1:1] | passed | 0.06296 seconds |
4
- ./spec/rack_jwt_verifier/jwt_helper_spec.rb[1:2:1] | passed | 0.05566 seconds |
5
- ./spec/rack_jwt_verifier/jwt_helper_spec.rb[1:2:2] | passed | 0.03186 seconds |
6
- ./spec/rack_jwt_verifier/jwt_helper_spec.rb[1:3:1:1] | passed | 0.04063 seconds |
7
- ./spec/rack_jwt_verifier/jwt_helper_spec.rb[1:3:2:1] | passed | 0.06923 seconds |
8
- ./spec/rack_jwt_verifier/jwt_helper_spec.rb[1:3:3:1] | passed | 0.03564 seconds |
9
- ./spec/rack_jwt_verifier/middleware_spec.rb[1:1:1] | passed | 0.02249 seconds |
10
- ./spec/rack_jwt_verifier/middleware_spec.rb[1:2:1:1] | passed | 0.05022 seconds |
11
- ./spec/rack_jwt_verifier/middleware_spec.rb[1:2:1:2] | passed | 0.02597 seconds |
12
- ./spec/rack_jwt_verifier/middleware_spec.rb[1:2:2:1] | passed | 0.02197 seconds |
13
- ./spec/rack_jwt_verifier/middleware_spec.rb[1:2:3:1] | passed | 0.03074 seconds |
14
- ./spec/rack_jwt_verifier/middleware_spec.rb[1:2:4:1] | passed | 0.02496 seconds |
15
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:1:1] | passed | 0.01668 seconds |
16
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:1:2] | passed | 0.00633 seconds |
17
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:1:3] | passed | 0.05474 seconds |
18
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:1:4] | passed | 0.04057 seconds |
19
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:2:1] | passed | 0.03279 seconds |
20
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:2:2] | passed | 0.04898 seconds |
21
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:2:3:1] | passed | 0.03709 seconds |
22
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:2:3:2] | passed | 0.01822 seconds |
23
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:3:1] | passed | 0.01993 seconds |
24
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:3:2] | passed | 0.01473 seconds |
25
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:3:3] | passed | 0.03322 seconds |
26
- ./spec/rack_jwt_verifier/verifier_spec.rb[1:3:4] | passed | 0.01368 seconds |
data/Gemfile DELETED
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in rack_jwt_verifier.gemspec
6
- gemspec
7
-
8
- # Dependencies for development and testing
9
- group :development, :test do
10
- gem "rack"
11
- gem "rspec", "~> 3.12"
12
- gem "rack-test"
13
- gem "webmock", "~> 3.14" # Added for mocking network requests in Verifier
14
- end