jwt_signed_request 2.5.0 → 2.6.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 +5 -5
- data/README.md +77 -18
- data/lib/jwt_signed_request.rb +10 -10
- data/lib/jwt_signed_request/key_store.rb +18 -10
- data/lib/jwt_signed_request/middlewares/faraday.rb +16 -14
- data/lib/jwt_signed_request/middlewares/rack.rb +21 -22
- data/lib/jwt_signed_request/sign.rb +21 -7
- data/lib/jwt_signed_request/verify.rb +11 -6
- data/lib/jwt_signed_request/version.rb +1 -1
- metadata +6 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fb9e7a555755580bb4b37387799d44d3bc4d67d7a157060fce83183827250d15
|
4
|
+
data.tar.gz: b4ed152d2dfa73fc961280a6d7c09fd480565195c280f76b71e6326268ba894d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d7f1c2fe8ffac7c069d11231ae797fb92967cece0ed2c441167d02ebec178edb6d9474d02a0e4ec3d9a4840d8df0888cb6b00029f99190e0b24f1fceaf2f0f2
|
7
|
+
data.tar.gz: 0b38896799b5021d54266010534f5360819d95363b7f09a276e7b729df8d708bacd9c861e1ae194e3b6f2bd7894e7f32f2e1e60d486d00b07b84705d2cbc4713
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ $ bundle
|
|
19
19
|
|
20
20
|
## Generating EC Keys
|
21
21
|
|
22
|
-
We should be using a public key encryption
|
22
|
+
We should be using a public key encryption algorithm such as **ES256**. To generate your public/private key pair using **ES256** run:
|
23
23
|
|
24
24
|
```sh
|
25
25
|
$ openssl ecparam -genkey -name prime256v1 -noout -out myprivatekey.pem
|
@@ -30,24 +30,32 @@ Store and encrypt these in your application secrets.
|
|
30
30
|
|
31
31
|
## Configuration
|
32
32
|
|
33
|
-
You can add signing and verification keys to
|
33
|
+
You can add signing and verification keys to one or more key stores as your application needs them.
|
34
|
+
|
35
|
+
For example, given the following keys:
|
34
36
|
|
35
37
|
```ruby
|
36
|
-
private_key = <<-
|
38
|
+
private_key = <<-PEM.gsub(/^\s+/, "")
|
37
39
|
-----BEGIN EC PRIVATE KEY-----
|
38
40
|
MHcCAQEEIBOQ3YIILYMV1glTKbF9oeZWzHe3SNQjAx4IbPIxNygQoAoGCCqGSM49
|
39
41
|
AwEHoUQDQgAEuOC3ufTTnW0hVmCPNERb4LxaDE/OexDdlmXEjHYaixzYIduluGXd
|
40
42
|
3cjg4H2gjqsY/NCpJ9nM8/AAINSrq+qPuA==
|
41
43
|
-----END EC PRIVATE KEY-----
|
42
|
-
|
44
|
+
PEM
|
43
45
|
|
44
|
-
public_key = <<-
|
46
|
+
public_key = <<-PEM.gsub(/^\s+/, "")
|
45
47
|
-----BEGIN PUBLIC KEY-----
|
46
48
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuOC3ufTTnW0hVmCPNERb4LxaDE/O
|
47
49
|
exDdlmXEjHYaixzYIduluGXd3cjg4H2gjqsY/NCpJ9nM8/AAINSrq+qPuA==
|
48
50
|
-----END PUBLIC KEY-----
|
49
|
-
|
51
|
+
PEM
|
52
|
+
```
|
53
|
+
|
54
|
+
### Single key store
|
55
|
+
|
56
|
+
If your application only needs a single key store, configure it like so:
|
50
57
|
|
58
|
+
```ruby
|
51
59
|
require 'openssl'
|
52
60
|
|
53
61
|
JWTSignedRequest.configure_keys do |config|
|
@@ -65,9 +73,35 @@ JWTSignedRequest.configure_keys do |config|
|
|
65
73
|
end
|
66
74
|
```
|
67
75
|
|
76
|
+
### Multiple key stores
|
77
|
+
|
78
|
+
If your application requires multiple key stores, configure them like so:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
key_store_id = 'widget_admin'
|
82
|
+
|
83
|
+
JWTSignedRequest.configure_keys(key_store_id) do |config|
|
84
|
+
config.add_signing_key(
|
85
|
+
key_id: 'client_a',
|
86
|
+
key: OpenSSL::PKey::EC.new(private_key),
|
87
|
+
algorithm: 'ES256',
|
88
|
+
)
|
89
|
+
|
90
|
+
config.add_verification_key(
|
91
|
+
key_id: 'client_a',
|
92
|
+
key: OpenSSL::PKey::EC.new(public_key),
|
93
|
+
algorithm: 'ES256',
|
94
|
+
)
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
68
98
|
## Signing Requests
|
69
99
|
|
70
|
-
If you have added your signing keys to
|
100
|
+
If you have added your signing keys to a key store, you will only need to
|
101
|
+
specify the `key_id` you are signing the requests with.
|
102
|
+
|
103
|
+
If you are using multiple key stores, you will also need to pass the
|
104
|
+
appropriate `key_store_id`.
|
71
105
|
|
72
106
|
### Using net/http
|
73
107
|
|
@@ -86,6 +120,7 @@ jwt_token = JWTSignedRequest.sign(
|
|
86
120
|
body: "",
|
87
121
|
key_id: 'my-key-id', # used for looking up key and kid header
|
88
122
|
lookup_key_id: 'my-alt-key-id', # optionally override lookup key
|
123
|
+
key_store_id: 'widget_admin', # optionally specify named key store ID
|
89
124
|
issuer: 'my-issuer' # optional
|
90
125
|
additional_headers_to_sign: ['X-AUTH'] # optional
|
91
126
|
)
|
@@ -97,7 +132,7 @@ res = Net::HTTP.start(uri.hostname, uri.port) {|http|
|
|
97
132
|
}
|
98
133
|
```
|
99
134
|
|
100
|
-
### Using
|
135
|
+
### Using Faraday
|
101
136
|
|
102
137
|
```ruby
|
103
138
|
require 'faraday'
|
@@ -105,10 +140,14 @@ require 'openssl'
|
|
105
140
|
require 'jwt_signed_request/middlewares/faraday'
|
106
141
|
|
107
142
|
conn = Faraday.new(url: URI.parse('http://example.com')) do |faraday|
|
108
|
-
faraday.use
|
109
|
-
|
110
|
-
|
111
|
-
|
143
|
+
faraday.use(
|
144
|
+
JWTSignedRequest::Middlewares::Faraday,
|
145
|
+
key_id: 'my-key-id',
|
146
|
+
key_store_id: 'my-key-store-id', # optional
|
147
|
+
issuer: 'my-issuer', # optional
|
148
|
+
additional_headers_to_sign: ['X-AUTH'], # optional
|
149
|
+
bearer_schema: true, # optional
|
150
|
+
)
|
112
151
|
|
113
152
|
faraday.adapter Faraday.default_adapter
|
114
153
|
end
|
@@ -119,10 +158,23 @@ conn.post do |req|
|
|
119
158
|
end
|
120
159
|
```
|
121
160
|
|
122
|
-
|
161
|
+
#### Additional options
|
162
|
+
|
163
|
+
##### bearer_schema (boolean)
|
123
164
|
|
124
|
-
|
165
|
+
Determines whether to use the [Bearer schema](https://auth0.com/docs/jwt#how-do-json-web-tokens-work-) when assigning the JWT token to the `Authorization` request header
|
125
166
|
|
167
|
+
| bearer_schema value | Authorization header value|
|
168
|
+
|---------------------|---------------------------|
|
169
|
+
| false (default) | `<jwt_token>` |
|
170
|
+
| true | `Bearer <jwt_token>` |
|
171
|
+
|
172
|
+
|
173
|
+
## Verifying Requests
|
174
|
+
|
175
|
+
Please make sure you have added your verification keys to the appropriate key
|
176
|
+
store. Doing so will allow the server to verify requests signed by different
|
177
|
+
signing keys.
|
126
178
|
|
127
179
|
## Using Rails
|
128
180
|
|
@@ -136,7 +188,11 @@ class APIController < ApplicationController
|
|
136
188
|
|
137
189
|
def verify_request
|
138
190
|
begin
|
139
|
-
JWTSignedRequest.verify(
|
191
|
+
JWTSignedRequest.verify(
|
192
|
+
request: request,
|
193
|
+
# Use optional `key_store_id` kwarg when working with multiple key stores, eg:
|
194
|
+
key_store_id: 'widget_admin',
|
195
|
+
)
|
140
196
|
|
141
197
|
rescue JWTSignedRequest::UnauthorizedRequestError => e
|
142
198
|
render :json => {}, :status => :unauthorized
|
@@ -158,9 +214,12 @@ JWT tokens contain an expiry timestamp. If communication delays are large (or sy
|
|
158
214
|
|
159
215
|
```ruby
|
160
216
|
class Server < Sinatra::Base
|
161
|
-
use
|
162
|
-
|
163
|
-
|
217
|
+
use(
|
218
|
+
JWTSignedRequest::Middlewares::Rack,
|
219
|
+
exclude_paths: /public|health/, # optional regex
|
220
|
+
leeway: 100, # optional
|
221
|
+
key_store_id: 'my-key-store-id', # optional
|
222
|
+
)
|
164
223
|
end
|
165
224
|
```
|
166
225
|
|
data/lib/jwt_signed_request.rb
CHANGED
@@ -9,22 +9,22 @@ require 'jwt_signed_request/errors'
|
|
9
9
|
module JWTSignedRequest
|
10
10
|
extend self
|
11
11
|
|
12
|
-
DEFAULT_ALGORITHM = 'ES256'
|
13
|
-
EMPTY_BODY =
|
12
|
+
DEFAULT_ALGORITHM = 'ES256'
|
13
|
+
EMPTY_BODY = ''
|
14
14
|
|
15
|
-
def configure_keys
|
16
|
-
yield(
|
15
|
+
def configure_keys(key_store_id = nil)
|
16
|
+
yield KeyStore.find(key_store_id)
|
17
17
|
end
|
18
18
|
|
19
|
-
def key_store
|
20
|
-
|
19
|
+
def key_store(id = nil)
|
20
|
+
KeyStore.find(id)
|
21
21
|
end
|
22
22
|
|
23
|
-
def sign(
|
24
|
-
Sign.call(
|
23
|
+
def sign(**args)
|
24
|
+
Sign.call(**args)
|
25
25
|
end
|
26
26
|
|
27
|
-
def verify(
|
28
|
-
Verify.call(
|
27
|
+
def verify(**args)
|
28
|
+
Verify.call(**args)
|
29
29
|
end
|
30
30
|
end
|
@@ -2,25 +2,33 @@
|
|
2
2
|
|
3
3
|
module JWTSignedRequest
|
4
4
|
class KeyStore
|
5
|
+
def self.find(id)
|
6
|
+
all[id]
|
7
|
+
end
|
8
|
+
|
9
|
+
private_class_method def self.all
|
10
|
+
@all ||= Hash.new { |result, key| result[key] = KeyStore.new }
|
11
|
+
end
|
12
|
+
|
5
13
|
def initialize
|
6
14
|
@signing_keys = {}
|
7
15
|
@verification_keys = {}
|
8
16
|
end
|
9
17
|
|
10
18
|
def add_signing_key(key_id:, key:, algorithm:)
|
11
|
-
@signing_keys.store(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
@signing_keys.store(
|
20
|
+
key_id,
|
21
|
+
key: key,
|
22
|
+
algorithm: algorithm,
|
23
|
+
)
|
16
24
|
end
|
17
25
|
|
18
26
|
def add_verification_key(key_id:, key:, algorithm:)
|
19
|
-
@verification_keys.store(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
@verification_keys.store(
|
28
|
+
key_id,
|
29
|
+
key: key,
|
30
|
+
algorithm: algorithm,
|
31
|
+
)
|
24
32
|
end
|
25
33
|
|
26
34
|
def get_signing_key(key_id:)
|
@@ -6,36 +6,38 @@ require 'jwt_signed_request'
|
|
6
6
|
module JWTSignedRequest
|
7
7
|
module Middlewares
|
8
8
|
class Faraday < Faraday::Middleware
|
9
|
-
def initialize(app, options)
|
9
|
+
def initialize(app, bearer_schema: nil, **options)
|
10
|
+
@bearer_schema = bearer_schema
|
10
11
|
@options = options
|
11
12
|
super(app)
|
12
13
|
end
|
13
14
|
|
14
15
|
def call(env)
|
15
|
-
|
16
|
+
env[:body] ||= ::JWTSignedRequest::EMPTY_BODY
|
17
|
+
|
18
|
+
@jwt_token = ::JWTSignedRequest.sign(
|
16
19
|
method: env[:method],
|
17
20
|
path: env[:url].request_uri,
|
18
21
|
headers: env[:request_headers],
|
19
|
-
body: env
|
20
|
-
**
|
22
|
+
body: env[:body],
|
23
|
+
**options,
|
21
24
|
)
|
22
25
|
|
23
|
-
env[:request_headers].store("Authorization",
|
26
|
+
env[:request_headers].store("Authorization", authorization_header)
|
27
|
+
|
24
28
|
app.call(env)
|
25
29
|
end
|
26
30
|
|
27
31
|
private
|
28
32
|
|
29
|
-
attr_reader :app, :env, :options
|
33
|
+
attr_reader :app, :env, :bearer_schema, :options, :jwt_token
|
34
|
+
|
35
|
+
def authorization_header
|
36
|
+
bearer_schema? ? "Bearer #{jwt_token}" : jwt_token
|
37
|
+
end
|
30
38
|
|
31
|
-
def
|
32
|
-
|
33
|
-
secret_key: options[:secret_key],
|
34
|
-
algorithm: options[:algorithm],
|
35
|
-
additional_headers_to_sign: options[:additional_headers_to_sign],
|
36
|
-
key_id: options[:key_id],
|
37
|
-
issuer: options[:issuer],
|
38
|
-
}.reject { |_, value| value.nil? }
|
39
|
+
def bearer_schema?
|
40
|
+
bearer_schema == true
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -8,41 +8,40 @@ module JWTSignedRequest
|
|
8
8
|
class Rack
|
9
9
|
UNAUTHORIZED_STATUS_CODE = 401
|
10
10
|
|
11
|
-
def initialize(app,
|
11
|
+
def initialize(app, secret_key: nil, algorithm: nil, leeway: nil, exclude_paths: nil, key_store_id: nil)
|
12
12
|
@app = app
|
13
|
-
@secret_key =
|
14
|
-
@algorithm =
|
15
|
-
@leeway =
|
16
|
-
@exclude_paths =
|
13
|
+
@secret_key = secret_key
|
14
|
+
@algorithm = algorithm
|
15
|
+
@leeway = leeway
|
16
|
+
@exclude_paths = exclude_paths
|
17
|
+
@key_store_id = key_store_id
|
17
18
|
end
|
18
19
|
|
19
20
|
def call(env)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
secret_key: secret_key,
|
25
|
-
algorithm: algorithm,
|
26
|
-
leeway: leeway
|
27
|
-
}.reject { |_, value| value.nil? }
|
28
|
-
|
29
|
-
::JWTSignedRequest.verify(**args)
|
30
|
-
end
|
31
|
-
|
32
|
-
app.call(env)
|
33
|
-
rescue ::JWTSignedRequest::UnauthorizedRequestError => e
|
34
|
-
[UNAUTHORIZED_STATUS_CODE, {'Content-Type' => 'application/json'} , []]
|
35
|
-
end
|
21
|
+
::JWTSignedRequest.verify(**verification_args(env)) unless excluded_path?(env)
|
22
|
+
app.call(env)
|
23
|
+
rescue ::JWTSignedRequest::UnauthorizedRequestError
|
24
|
+
[UNAUTHORIZED_STATUS_CODE, {'Content-Type' => 'application/json'}, []]
|
36
25
|
end
|
37
26
|
|
38
27
|
private
|
39
28
|
|
40
|
-
attr_reader :app, :secret_key, :algorithm, :leeway, :exclude_paths
|
29
|
+
attr_reader :app, :secret_key, :algorithm, :leeway, :exclude_paths, :key_store_id
|
41
30
|
|
42
31
|
def excluded_path?(env)
|
43
32
|
!exclude_paths.nil? &&
|
44
33
|
env['PATH_INFO'].match(exclude_paths)
|
45
34
|
end
|
35
|
+
|
36
|
+
def verification_args(env)
|
37
|
+
{
|
38
|
+
request: ::Rack::Request.new(env),
|
39
|
+
secret_key: secret_key,
|
40
|
+
algorithm: algorithm,
|
41
|
+
leeway: leeway,
|
42
|
+
key_store_id: key_store_id,
|
43
|
+
}
|
44
|
+
end
|
46
45
|
end
|
47
46
|
end
|
48
47
|
end
|
@@ -4,8 +4,8 @@ require 'jwt_signed_request/claims'
|
|
4
4
|
|
5
5
|
module JWTSignedRequest
|
6
6
|
class Sign
|
7
|
-
def self.call(
|
8
|
-
new(
|
7
|
+
def self.call(**args)
|
8
|
+
new(**args).call
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(
|
@@ -18,7 +18,8 @@ module JWTSignedRequest
|
|
18
18
|
key_id: nil,
|
19
19
|
lookup_key_id: key_id,
|
20
20
|
issuer: nil,
|
21
|
-
additional_headers_to_sign: nil
|
21
|
+
additional_headers_to_sign: nil,
|
22
|
+
key_store_id: nil
|
22
23
|
)
|
23
24
|
@method = method
|
24
25
|
@path = path
|
@@ -30,6 +31,7 @@ module JWTSignedRequest
|
|
30
31
|
@lookup_key_id = lookup_key_id
|
31
32
|
@issuer = issuer
|
32
33
|
@additional_headers_to_sign = additional_headers_to_sign
|
34
|
+
@key_store_id = key_store_id
|
33
35
|
end
|
34
36
|
|
35
37
|
def call
|
@@ -38,12 +40,24 @@ module JWTSignedRequest
|
|
38
40
|
|
39
41
|
private
|
40
42
|
|
41
|
-
attr_reader
|
42
|
-
:method,
|
43
|
-
:
|
43
|
+
attr_reader(
|
44
|
+
:method,
|
45
|
+
:path,
|
46
|
+
:body,
|
47
|
+
:headers,
|
48
|
+
:key_id,
|
49
|
+
:lookup_key_id,
|
50
|
+
:issuer,
|
51
|
+
:additional_headers_to_sign,
|
52
|
+
:key_store_id,
|
53
|
+
)
|
44
54
|
|
45
55
|
def stored_key
|
46
|
-
@stored_key ||=
|
56
|
+
@stored_key ||= key_store.get_signing_key(key_id: lookup_key_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def key_store
|
60
|
+
KeyStore.find(key_store_id)
|
47
61
|
end
|
48
62
|
|
49
63
|
def secret_key
|
@@ -6,17 +6,18 @@ require 'jwt/version'
|
|
6
6
|
|
7
7
|
module JWTSignedRequest
|
8
8
|
class Verify
|
9
|
-
def self.call(
|
10
|
-
new(
|
9
|
+
def self.call(**args)
|
10
|
+
new(**args).call
|
11
11
|
end
|
12
12
|
|
13
13
|
# TODO: secret_key & algorithm is deprecated and will be removed in future.
|
14
|
-
# For now we will support its
|
15
|
-
def initialize(request:, secret_key: nil, algorithm: nil, leeway: nil)
|
14
|
+
# For now we will support its functionality
|
15
|
+
def initialize(request:, secret_key: nil, algorithm: nil, leeway: nil, key_store_id: nil)
|
16
16
|
@request = request
|
17
17
|
@secret_key = secret_key
|
18
18
|
@algorithm = algorithm
|
19
19
|
@leeway = leeway
|
20
|
+
@key_store_id = key_store_id
|
20
21
|
end
|
21
22
|
|
22
23
|
def call
|
@@ -29,19 +30,23 @@ module JWTSignedRequest
|
|
29
30
|
|
30
31
|
private
|
31
32
|
|
32
|
-
attr_reader :request, :leeway
|
33
|
+
attr_reader :request, :leeway, :key_store_id
|
33
34
|
|
34
35
|
def stored_key
|
35
36
|
_body, jwt_header = ::JWT.decode(jwt_token, nil, false)
|
36
37
|
key_id = jwt_header.fetch('kid') { raise MissingKeyIdError }
|
37
38
|
signed_algorithm = jwt_header.fetch('alg')
|
38
|
-
|
39
|
+
key_store.get_verification_key(key_id: key_id).tap do |key|
|
39
40
|
if signed_algorithm != key[:algorithm]
|
40
41
|
raise AlgorithmMismatchError
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
46
|
+
def key_store
|
47
|
+
KeyStore.find(key_store_id)
|
48
|
+
end
|
49
|
+
|
45
50
|
def algorithm
|
46
51
|
@algorithm ||= stored_key.fetch(:algorithm) { raise MissingAlgorithmError }
|
47
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt_signed_request
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Envato
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -17,9 +17,6 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.5.0
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 2.2.0
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +24,6 @@ dependencies:
|
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: 1.5.0
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 2.2.0
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: rack
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -138,7 +132,7 @@ metadata:
|
|
138
132
|
bug_tracker_uri: https://github.com/envato/jwt_signed_request/issues
|
139
133
|
changelog_uri: https://github.com/envato/jwt_signed_request/blob/master/CHANGELOG.md
|
140
134
|
source_code_uri: https://github.com/envato/jwt_signed_request
|
141
|
-
post_install_message:
|
135
|
+
post_install_message:
|
142
136
|
rdoc_options: []
|
143
137
|
require_paths:
|
144
138
|
- lib
|
@@ -153,9 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
147
|
- !ruby/object:Gem::Version
|
154
148
|
version: '0'
|
155
149
|
requirements: []
|
156
|
-
|
157
|
-
|
158
|
-
signing_key:
|
150
|
+
rubygems_version: 3.1.2
|
151
|
+
signing_key:
|
159
152
|
specification_version: 4
|
160
153
|
summary: JWT request signing and verification for Internal APIs
|
161
154
|
test_files: []
|