jwk-loader 0.1.1 → 1.1.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +17 -5
- data/Gemfile +3 -6
- data/README.md +47 -4
- data/jwk-loader.gemspec +1 -0
- data/lib/jwk-loader.rb +30 -2
- data/lib/jwk_loader/config/config.rb +35 -0
- data/lib/jwk_loader/jwks.rb +4 -0
- data/lib/jwk_loader/jwks_uri_provider.rb +9 -8
- data/lib/jwk_loader/test.rb +32 -0
- data/lib/jwk_loader/version.rb +1 -1
- data/release-please-config.json +9 -0
- metadata +26 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e77eb1ce66789b0d37f9d200ae80d5af43b7de7745f00bfe0548b87301120c79
|
4
|
+
data.tar.gz: 5d71e2e2cc5f4caa96b076c7528e03e6e257e306176f935e2683d3781cb1f8db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3d808d24e6c331db89d5cd8cad4bce5ce94e36e9d8ad99c2a18588bcb8043416b04239da6a0b899d4c193591e6736cee0198358bbb5abc163b007251e445397
|
7
|
+
data.tar.gz: 025ce184c452c2d8dcf632ee823d98e215a80c011db6d75bd253d44ad4a66e8a4f95dd1eb013fd2fdb51a6dbc1540ea59fbaef931e8239a8b18951da327b86f1
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,21 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [1.1.0](https://github.com/anakinj/jwk-loader/compare/v1.0.0...v1.1.0) (2024-08-10)
|
4
4
|
|
5
|
-
- make sure 'net/http' is required [#1](https://github.com/anakinj/jwk-loader/pull/2) ([@lukad](https://github.com/lukad)).
|
6
5
|
|
7
|
-
|
6
|
+
### Features
|
8
7
|
|
9
|
-
|
8
|
+
* Official support for Ruby 3.2 and 3.3 ([2f6079f](https://github.com/anakinj/jwk-loader/commit/2f6079fd490a4918524974ffb1d897abbf875787))
|
9
|
+
|
10
|
+
## [1.0.0](https://github.com/anakinj/jwk-loader/compare/v0.1.1...v1.0.0) (2023-12-28)
|
11
|
+
|
12
|
+
### Features
|
13
|
+
|
14
|
+
- `jwk_loader/test` for convenience for testing without external dependencies. [#6](https://github.com/anakinj/jwk-loader/pull/6) ([@anakinj](https://github.com/anakinj))
|
15
|
+
- Serialize the cached key sets into `JWT::JWK:Set` to avoid generating OpenSSL PKeys for each time the keys are used. [#6](https://github.com/anakinj/jwk-loader/pull/6) ([@anakinj](https://github.com/anakinj))
|
16
|
+
|
17
|
+
## [1.0.0](https://github.com/anakinj/jwk-loader/compare/v0.1.0...v0.1.1) (2022-08-26)
|
18
|
+
|
19
|
+
### Fixes
|
20
|
+
|
21
|
+
- make sure 'net/http' is required [#2](https://github.com/anakinj/jwk-loader/pull/2) ([@lukad](https://github.com/lukad)).
|
data/Gemfile
CHANGED
@@ -2,14 +2,11 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
# Specify your gem's dependencies in jwk-loader.gemspec
|
6
5
|
gemspec
|
7
6
|
|
8
|
-
gem "rake"
|
9
|
-
gem "rspec"
|
10
|
-
gem "rubocop"
|
7
|
+
gem "rake"
|
8
|
+
gem "rspec"
|
9
|
+
gem "rubocop"
|
11
10
|
gem "simplecov"
|
12
11
|
gem "vcr"
|
13
12
|
gem "webmock"
|
14
|
-
|
15
|
-
gem "jwt"
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
#
|
1
|
+
# jwk-loader
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/jwk-loader)
|
4
|
+
[](https://github.com/anakinj/jwk-loader/actions/workflows/test.yml)
|
5
|
+
|
6
|
+
This gem can be used in combination with the [ruby-jwt](https://rubygems.org/gems/jwt) gem as the mechanism to load and cache the JWKs.
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -16,16 +19,56 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
19
|
|
17
20
|
### Using as a jwks loader when decoding JWT tokens
|
18
21
|
|
19
|
-
```
|
22
|
+
```ruby
|
20
23
|
require "jwt"
|
21
24
|
require "jwk-loader"
|
22
25
|
|
23
26
|
JWT.decode(token, nil, true, algorithm: "RS512", jwks: JwkLoader.for_uri(uri: "https://url/to/public/jwks") )
|
24
27
|
```
|
25
28
|
|
29
|
+
### Testing endpoints protected by JWT tokens
|
30
|
+
|
31
|
+
When testing HTTP endpoints protected by asymmetric JWT keys the mechanism in `jwk_loader/test` can be used to simplify the process.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'jwk_loader/test'
|
35
|
+
|
36
|
+
RSpec.describe 'GET /protected' do
|
37
|
+
include JwkLoader::Test
|
38
|
+
|
39
|
+
context 'when called with a valid token' do
|
40
|
+
let(:token) { sign_test_token(token_payload: { user_id: "user" }, jwk_endpoint: "https://url/to/public/jwks") }
|
41
|
+
subject(:response) { get('/protected', { 'HTTP_AUTHORIZATION' => "Bearer #{token}" }) }
|
42
|
+
|
43
|
+
it 'is a success' do
|
44
|
+
expect(response.status).to eq(200)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
26
50
|
### Configuring the gem
|
27
51
|
|
28
|
-
|
52
|
+
```ruby
|
53
|
+
require "jwt-loader"
|
54
|
+
|
55
|
+
JwkLoader.configure do |config|
|
56
|
+
config[:cache] = YetAnotherCache.new
|
57
|
+
config[:cache_grace_period] = 999
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
or in alternative
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
require "jwt-loader"
|
65
|
+
|
66
|
+
JwkLoader.configure do |config|
|
67
|
+
config.cache = YetAnotherCache.new
|
68
|
+
config.cache_grace_period = 999
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
29
72
|
|
30
73
|
## Development
|
31
74
|
|
data/jwk-loader.gemspec
CHANGED
data/lib/jwk-loader.rb
CHANGED
@@ -5,9 +5,37 @@ require_relative "jwk_loader/jwks"
|
|
5
5
|
require_relative "jwk_loader/jwks_uri_provider"
|
6
6
|
require_relative "jwk_loader/memory_cache"
|
7
7
|
require_relative "jwk_loader/error"
|
8
|
+
require_relative "jwk_loader/config/config"
|
8
9
|
|
9
10
|
module JwkLoader
|
10
|
-
|
11
|
-
|
11
|
+
class << self
|
12
|
+
def for_uri(**options)
|
13
|
+
options[:cache] ||= config[:cache]
|
14
|
+
options[:cache_grace_period] ||= config[:cache_grace_period]
|
15
|
+
JwksUriProvider.new(**options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache
|
19
|
+
config[:cache]
|
20
|
+
end
|
21
|
+
|
22
|
+
def configure
|
23
|
+
yield config
|
24
|
+
end
|
25
|
+
|
26
|
+
def config
|
27
|
+
@config ||= JwkLoader::Config.new.tap do |cfg|
|
28
|
+
cfg[:cache] = MemoryCache.new
|
29
|
+
cfg[:cache_grace_period] = 900
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset!
|
34
|
+
@config = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def memory_store
|
38
|
+
@memory_store ||= MemoryCache.new
|
39
|
+
end
|
12
40
|
end
|
13
41
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JwkLoader
|
4
|
+
class Config
|
5
|
+
class ConfigurationNotFound < JwkLoader::Error
|
6
|
+
def initialize(key)
|
7
|
+
super("Configuration for #{key} not available")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
registry[key] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
registry[key] || (raise ConfigurationNotFound, key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(name, *args)
|
20
|
+
return send(:[]=, name.to_s[0..-2].to_sym, *args) if name.to_s.end_with?("=")
|
21
|
+
|
22
|
+
send(:[], name, *args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to_missing?(_name, _include_private)
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def registry
|
32
|
+
@registry ||= {}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/jwk_loader/jwks.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JwkLoader
|
4
|
-
def self.cache
|
5
|
-
@cache ||= MemoryCache.new
|
6
|
-
end
|
7
|
-
|
8
4
|
class JwksUriProvider
|
9
5
|
attr_reader :uri, :cache, :cache_grace_period
|
10
6
|
|
11
|
-
def initialize(uri:, cache
|
7
|
+
def initialize(uri:, cache:, cache_grace_period:)
|
12
8
|
@uri = uri
|
13
|
-
@cache = cache
|
9
|
+
@cache = cache
|
14
10
|
@cache_grace_period = cache_grace_period
|
15
11
|
end
|
16
12
|
|
@@ -22,7 +18,7 @@ module JwkLoader
|
|
22
18
|
private
|
23
19
|
|
24
20
|
def jwks
|
25
|
-
from_cache || from_uri
|
21
|
+
from_cache || from_memory || from_uri
|
26
22
|
end
|
27
23
|
|
28
24
|
def invalidate_cache!
|
@@ -35,12 +31,17 @@ module JwkLoader
|
|
35
31
|
cache_entry&.fetch(:jwks)
|
36
32
|
end
|
37
33
|
|
34
|
+
def from_memory
|
35
|
+
JwkLoader::Jwks.from_memory(uri)
|
36
|
+
end
|
37
|
+
|
38
38
|
def cache_entry
|
39
39
|
cache.fetch(uri)
|
40
40
|
end
|
41
41
|
|
42
42
|
def from_uri
|
43
|
-
JwkLoader::Jwks.from_uri(uri)
|
43
|
+
data = JwkLoader::Jwks.from_uri(uri)
|
44
|
+
JWT::JWK::Set.new(data).tap do |jwks|
|
44
45
|
cache.store(uri, jwks: jwks, fetched_at: Time.now)
|
45
46
|
end
|
46
47
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JwkLoader
|
4
|
+
module Test
|
5
|
+
def generate_signing_key(algorithm:)
|
6
|
+
case algorithm
|
7
|
+
when "RS256", "RS384", "RS512"
|
8
|
+
OpenSSL::PKey::RSA.new(2048)
|
9
|
+
when "ES256"
|
10
|
+
OpenSSL::PKey::EC.generate("prime256v1")
|
11
|
+
else
|
12
|
+
raise "Unsupported algorithm: #{algorithm}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_signing_key_for(jwk_endpoint:, algorithm: "RS512")
|
17
|
+
key_set = JwkLoader.memory_store.fetch(jwk_endpoint)
|
18
|
+
|
19
|
+
if key_set.nil?
|
20
|
+
key_set = JWT::JWK::Set.new([generate_signing_key(algorithm: algorithm)])
|
21
|
+
JwkLoader.memory_store.store(jwk_endpoint, key_set)
|
22
|
+
end
|
23
|
+
|
24
|
+
key_set.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def sign_test_token(token_payload:, jwk_endpoint:, algorithm: "RS512")
|
28
|
+
key = test_signing_key_for(jwk_endpoint: jwk_endpoint, algorithm: algorithm)
|
29
|
+
JWT.encode(token_payload, key.signing_key, algorithm, kid: key[:kid])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/jwk_loader/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwk-loader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joakim Antman
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -24,13 +24,28 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.6'
|
41
|
+
description:
|
28
42
|
email:
|
29
43
|
- antmanj@gmail.com
|
30
44
|
executables: []
|
31
45
|
extensions: []
|
32
46
|
extra_rdoc_files: []
|
33
47
|
files:
|
48
|
+
- ".release-please-manifest.json"
|
34
49
|
- ".rspec"
|
35
50
|
- ".rubocop.yml"
|
36
51
|
- CHANGELOG.md
|
@@ -41,11 +56,14 @@ files:
|
|
41
56
|
- Rakefile
|
42
57
|
- jwk-loader.gemspec
|
43
58
|
- lib/jwk-loader.rb
|
59
|
+
- lib/jwk_loader/config/config.rb
|
44
60
|
- lib/jwk_loader/error.rb
|
45
61
|
- lib/jwk_loader/jwks.rb
|
46
62
|
- lib/jwk_loader/jwks_uri_provider.rb
|
47
63
|
- lib/jwk_loader/memory_cache.rb
|
64
|
+
- lib/jwk_loader/test.rb
|
48
65
|
- lib/jwk_loader/version.rb
|
66
|
+
- release-please-config.json
|
49
67
|
homepage: https://github.com/anakinj/jwk-loader
|
50
68
|
licenses:
|
51
69
|
- MIT
|
@@ -53,9 +71,9 @@ metadata:
|
|
53
71
|
allowed_push_host: https://rubygems.org
|
54
72
|
homepage_uri: https://github.com/anakinj/jwk-loader
|
55
73
|
source_code_uri: https://github.com/anakinj/jwk-loader
|
56
|
-
changelog_uri: https://github.com/anakinj/jwk-loader/blob/
|
74
|
+
changelog_uri: https://github.com/anakinj/jwk-loader/blob/1.1.0/CHANGELOG.md
|
57
75
|
rubygems_mfa_required: 'true'
|
58
|
-
post_install_message:
|
76
|
+
post_install_message:
|
59
77
|
rdoc_options: []
|
60
78
|
require_paths:
|
61
79
|
- lib
|
@@ -70,8 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
88
|
- !ruby/object:Gem::Version
|
71
89
|
version: '0'
|
72
90
|
requirements: []
|
73
|
-
rubygems_version: 3.
|
74
|
-
signing_key:
|
91
|
+
rubygems_version: 3.5.11
|
92
|
+
signing_key:
|
75
93
|
specification_version: 4
|
76
94
|
summary: Tooling for handling JWK loading, parsing and caching
|
77
95
|
test_files: []
|