jwt_signed_request 2.5.4 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67651a9db5318ca7f01399d88435341f421ef7b0c94f69554150794908f2b87f
4
- data.tar.gz: e1f4aed3bf88ca12d3ab2c11d4621d23dae4bbae0393683861764af4da0593a6
3
+ metadata.gz: fb9e7a555755580bb4b37387799d44d3bc4d67d7a157060fce83183827250d15
4
+ data.tar.gz: b4ed152d2dfa73fc961280a6d7c09fd480565195c280f76b71e6326268ba894d
5
5
  SHA512:
6
- metadata.gz: b9242de7ec84a4d0a5c69025127d900e1fc4289219b8d2f9c14b2d8caa6aee2c3f28facd5ab1df764f5fb735b5ed83d7396b7160d1738a03dfc65d97a76c6017
7
- data.tar.gz: 0bf336ac071d40ae4d16f76143e8f821a3fba4c4fbd8b47fb94e6a13e33695bb7a0556adce2e19d651e4daafaee5a484d45ae4c0f2c62abfd9fa090105ab854b
6
+ metadata.gz: 6d7f1c2fe8ffac7c069d11231ae797fb92967cece0ed2c441167d02ebec178edb6d9474d02a0e4ec3d9a4840d8df0888cb6b00029f99190e0b24f1fceaf2f0f2
7
+ data.tar.gz: 0b38896799b5021d54266010534f5360819d95363b7f09a276e7b729df8d708bacd9c861e1ae194e3b6f2bd7894e7f32f2e1e60d486d00b07b84705d2cbc4713
data/README.md CHANGED
@@ -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 the key store as your application needs them.
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 = <<-pem.gsub(/^\s+/, "")
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
- pem
44
+ PEM
43
45
 
44
- public_key = <<-pem.gsub(/^\s+/, "")
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
- pem
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 the key store, you will only need to specify the `key_id` you are signing the requests with.
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 faraday
135
+ ### Using Faraday
101
136
 
102
137
  ```ruby
103
138
  require 'faraday'
@@ -105,11 +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 JWTSignedRequest::Middlewares::Faraday,
109
- key_id: 'my-key-id',
110
- issuer: 'my-issuer', # optional
111
- additional_headers_to_sign: ['X-AUTH'], # optional
112
- bearer_schema: true # optional
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
+ )
113
151
 
114
152
  faraday.adapter Faraday.default_adapter
115
153
  end
@@ -134,8 +172,9 @@ Determines whether to use the [Bearer schema](https://auth0.com/docs/jwt#how-do-
134
172
 
135
173
  ## Verifying Requests
136
174
 
137
- Please make sure you have added your verification keys to the key store. Doing so will allow the server to verify requests signed by different signing keys.
138
-
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.
139
178
 
140
179
  ## Using Rails
141
180
 
@@ -149,7 +188,11 @@ class APIController < ApplicationController
149
188
 
150
189
  def verify_request
151
190
  begin
152
- JWTSignedRequest.verify(request: request)
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
+ )
153
196
 
154
197
  rescue JWTSignedRequest::UnauthorizedRequestError => e
155
198
  render :json => {}, :status => :unauthorized
@@ -171,9 +214,12 @@ JWT tokens contain an expiry timestamp. If communication delays are large (or sy
171
214
 
172
215
  ```ruby
173
216
  class Server < Sinatra::Base
174
- use JWTSignedRequest::Middlewares::Rack,
175
- exclude_paths: /public|health/, # optional regex
176
- leeway: 100 # optional
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
+ )
177
223
  end
178
224
  ```
179
225
 
@@ -9,15 +9,15 @@ require 'jwt_signed_request/errors'
9
9
  module JWTSignedRequest
10
10
  extend self
11
11
 
12
- DEFAULT_ALGORITHM = 'ES256'.freeze
13
- EMPTY_BODY = "".freeze
12
+ DEFAULT_ALGORITHM = 'ES256'
13
+ EMPTY_BODY = ''
14
14
 
15
- def configure_keys
16
- yield(key_store)
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
- @key_store ||= KeyStore.new
19
+ def key_store(id = nil)
20
+ KeyStore.find(id)
21
21
  end
22
22
 
23
23
  def sign(**args)
@@ -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(key_id,
12
- {
13
- key: key,
14
- algorithm: algorithm
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(key_id,
20
- {
21
- key: key,
22
- algorithm: algorithm
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,7 +6,8 @@ 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
@@ -19,7 +20,7 @@ module JWTSignedRequest
19
20
  path: env[:url].request_uri,
20
21
  headers: env[:request_headers],
21
22
  body: env[:body],
22
- **optional_settings
23
+ **options,
23
24
  )
24
25
 
25
26
  env[:request_headers].store("Authorization", authorization_header)
@@ -29,24 +30,14 @@ module JWTSignedRequest
29
30
 
30
31
  private
31
32
 
32
- attr_reader :app, :env, :options, :jwt_token
33
+ attr_reader :app, :env, :bearer_schema, :options, :jwt_token
33
34
 
34
35
  def authorization_header
35
36
  bearer_schema? ? "Bearer #{jwt_token}" : jwt_token
36
37
  end
37
38
 
38
39
  def bearer_schema?
39
- options[:bearer_schema] == true
40
- end
41
-
42
- def optional_settings
43
- {
44
- secret_key: options[:secret_key],
45
- algorithm: options[:algorithm],
46
- additional_headers_to_sign: options[:additional_headers_to_sign],
47
- key_id: options[:key_id],
48
- issuer: options[:issuer],
49
- }.reject { |_, value| value.nil? }
40
+ bearer_schema == true
50
41
  end
51
42
  end
52
43
  end
@@ -8,41 +8,40 @@ module JWTSignedRequest
8
8
  class Rack
9
9
  UNAUTHORIZED_STATUS_CODE = 401
10
10
 
11
- def initialize(app, options = {})
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 = options[:secret_key]
14
- @algorithm = options[:algorithm]
15
- @leeway = options[:leeway]
16
- @exclude_paths = options[: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
- begin
21
- unless excluded_path?(env)
22
- args = {
23
- request: ::Rack::Request.new(env),
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
@@ -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, :path, :body, :headers,
43
- :key_id, :lookup_key_id, :issuer, :additional_headers_to_sign
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 ||= JWTSignedRequest.key_store.get_signing_key(key_id: lookup_key_id)
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
@@ -11,12 +11,13 @@ module JWTSignedRequest
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 functionaility
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
- JWTSignedRequest.key_store.get_verification_key(key_id: key_id).tap do |key|
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWTSignedRequest
4
- VERSION = '2.5.4'
4
+ VERSION = '2.6.0'
5
5
  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.5.4
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: 2020-05-01 00:00:00.000000000 Z
11
+ date: 2020-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -132,7 +132,7 @@ metadata:
132
132
  bug_tracker_uri: https://github.com/envato/jwt_signed_request/issues
133
133
  changelog_uri: https://github.com/envato/jwt_signed_request/blob/master/CHANGELOG.md
134
134
  source_code_uri: https://github.com/envato/jwt_signed_request
135
- post_install_message:
135
+ post_install_message:
136
136
  rdoc_options: []
137
137
  require_paths:
138
138
  - lib
@@ -148,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
148
  version: '0'
149
149
  requirements: []
150
150
  rubygems_version: 3.1.2
151
- signing_key:
151
+ signing_key:
152
152
  specification_version: 4
153
153
  summary: JWT request signing and verification for Internal APIs
154
154
  test_files: []