jwt_signed_request 2.5.4 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
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: []