grape-jwt-authentication 1.3.0 → 2.0.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: a33372149e82a854964a634712e715c7bfecb81116d54f182e0bacaad7969630
4
- data.tar.gz: c97fced07a3740c14dcaab1c037cdf2f2d39b1ede386938bda483bcf246e1942
3
+ metadata.gz: 30a5d4d0a29a146c657ef5452513e1ffd546fca42ee41c08ed6096537b4b454f
4
+ data.tar.gz: ab6fc80154deca0a7c61b775678aa22965283cb1f9b528cca0db97c4ebdcc301
5
5
  SHA512:
6
- metadata.gz: 3a3b8e0cf468782c570178879c2223763b00893081e76a0682cf230df332a40207d02fe02a1b6bf09e16426a51ed3694a326b2e8453b3e885fbfdeae131c1f09
7
- data.tar.gz: 2e45e3eeb4c9ce9345a751f138c765230bfc622616b51afc843c6781c2f947d6136bc35b1523eea0aa511513ad9c973f0a642a1fc06130868a56bafb01f57dba
6
+ metadata.gz: 460a1222afd6240182b328f4bcc69fa44a4e2a18d9c63f478121e59853da3585265b589a50ebd8bdd0d67e483b44d8abfc743cdbfff04fef9c2b00357fc759b7
7
+ data.tar.gz: a38e6870649e183a3329a8eb3b078c7254c6cb01dac1e704ef7d164d0ef8795999e7d7fe8ca6bdc3f3d192b628869c3a089702b06c6eb398d3c10acc82fffb63
@@ -1,3 +1,14 @@
1
+ ### 2.0.0
2
+
3
+ * Extracted the JWT verification functionality into its own gem
4
+ ([keyless](https://github.com/hausgold/keyless)) (#6)
5
+ * This extraction allows users to use the JWT/RSA key handling without Grape
6
+ * The API/configuration stays the same
7
+ * With the major update to 2.0 we dropped a lot of code which is now located at
8
+ the Keyless gem:
9
+ * `Grape::Jwt::Authentication::Jwt` was replace with `Keyless::Jwt`
10
+ * `Grape::Jwt::Authentication::RsaPublicKey` was replace with `Keyless::RsaPublicKey`
11
+
1
12
  ### 1.3.0
2
13
 
3
14
  * Dropped support for EOL Ruby 2.3 (in addition to Grape)
data/README.md CHANGED
@@ -185,18 +185,18 @@ have your own mechanism.
185
185
 
186
186
  ```ruby
187
187
  # Get your public key, by using the global configuration
188
- public_key = Grape::Jwt::Authentication::RsaPublicKey.fetch
188
+ public_key = Keyless::RsaPublicKey.fetch
189
189
  # => OpenSSL::PKey::RSA
190
190
 
191
191
  # Using a local configuration
192
- fetcher = Grape::Jwt::Authentication::RsaPublicKey.instance
192
+ fetcher = Keyless::RsaPublicKey.instance
193
193
  fetcher.url = 'https://your.identity.provider/rsa_public_key'
194
194
  public_key = fetcher.fetch
195
195
  # => OpenSSL::PKey::RSA
196
196
  ```
197
197
 
198
198
  The following examples show you how to configure the
199
- `Grape::Jwt::Authentication::RsaPublicKey` class the global way. This is useful
199
+ `Keyless::RsaPublicKey` class the global way. This is useful
200
200
  for a shared initializer place.
201
201
 
202
202
  ##### RSA public key location (URL)
@@ -261,7 +261,7 @@ the `RsaPublicKey` fetcher class. (by default)
261
261
  raw_token = 'eyJ0eXAiOiJKV1QifQ.eyJ0ZXN0Ijp0cnVlfQ.'
262
262
 
263
263
  # Parse the raw token and create a instance of it
264
- token = Grape::Jwt::Authentication::Jwt.new(raw_token)
264
+ token = Keyless::Jwt.new(raw_token)
265
265
 
266
266
  # Access the payload easily (recursive-open-struct)
267
267
  token.payload.test
@@ -274,7 +274,7 @@ token.valid?
274
274
  ```
275
275
 
276
276
  The following examples show you how to configure the
277
- `Grape::Jwt::Authentication::Jwt` class the global way. This is useful for a
277
+ `Keyless::Jwt` class the global way. This is useful for a
278
278
  shared initializer place.
279
279
 
280
280
  ##### Issuer verification
@@ -323,7 +323,7 @@ end
323
323
  You can configure your own verification key on the `Jwt` wrapper class. This
324
324
  way you can pass your HMAC secret or your ECDSA public key to the JSON Web
325
325
  Token validation method. Here you need to pass a proc, on the
326
- `Grape::Jwt::Authentication::Jwt` class it has to be a scalar value. By default
326
+ `Keyless::Jwt` class it has to be a scalar value. By default
327
327
  we use the `RsaPublicKey` class to retrieve the RSA public key.
328
328
 
329
329
  ```ruby
@@ -394,7 +394,7 @@ Grape::Jwt::Authentication.configure do |conf|
394
394
  # Custom verification logic.
395
395
  conf.authenticator = proc do |token|
396
396
  # Parse and instantiate a JWT verification instance
397
- jwt = Grape::Jwt::Authentication::Jwt.new(token)
397
+ jwt = Keyless::Jwt.new(token)
398
398
 
399
399
  # We just allow valid access tokens
400
400
  jwt.access_token? && jwt.valid?
@@ -36,4 +36,5 @@ Gem::Specification.new do |spec|
36
36
  spec.add_runtime_dependency 'httparty'
37
37
  spec.add_runtime_dependency 'jwt', '~> 2.1'
38
38
  spec.add_runtime_dependency 'recursive-open-struct', '~> 1.0'
39
+ spec.add_runtime_dependency 'keyless', '~> 1.0'
39
40
  end
@@ -7,15 +7,13 @@ require 'active_support/cache'
7
7
  require 'active_support/core_ext/hash'
8
8
  require 'active_support/time'
9
9
  require 'active_support/time_with_zone'
10
-
11
10
  require 'jwt'
12
-
11
+ require 'keyless'
13
12
  require 'grape'
14
13
  require 'grape/jwt/authentication/version'
15
14
  require 'grape/jwt/authentication/configuration'
15
+ require 'grape/jwt/authentication/dependencies'
16
16
  require 'grape/jwt/authentication/jwt_handler'
17
- require 'grape/jwt/authentication/jwt'
18
- require 'grape/jwt/authentication/rsa_public_key'
19
17
 
20
18
  module Grape
21
19
  module Jwt
@@ -43,11 +41,13 @@ module Grape
43
41
  # end
44
42
  def self.configure
45
43
  yield(configuration)
44
+ configure_dependencies
46
45
  end
47
46
 
48
47
  # Reset the current configuration with the default one.
49
48
  def self.reset_configuration!
50
49
  self.configuration = Configuration.new
50
+ configure_dependencies
51
51
  end
52
52
 
53
53
  included do
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Jwt
5
+ module Authentication
6
+ # Specifies which configuration keys are shared between keyless
7
+ # and grape-jwt-authentication, so that we can easily pass through
8
+ # our configuration to keyless.
9
+ KEYLESS_CONFIGURATION = %i[
10
+ authenticator rsa_public_key_url rsa_public_key_caching
11
+ rsa_public_key_expiration jwt_issuer jwt_beholder jwt_options
12
+ jwt_verification_key
13
+ ]
14
+
15
+ # (Re)configure our gem dependencies. We take care of setting up
16
+ # +Keyless+, which has been extracted from this gem.
17
+ def self.configure_dependencies
18
+ configure_keyless
19
+ end
20
+
21
+ # Configure the +Keyless+ gem with our configuration.
22
+ def self.configure_keyless
23
+ configuration = Grape::Jwt::Authentication.configuration
24
+
25
+ Keyless.configure do |keyless|
26
+ KEYLESS_CONFIGURATION.each do |option|
27
+ keyless.send("#{option}=", configuration.send(option))
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -91,8 +91,8 @@ module Grape
91
91
  # @param env [Hash{String => Mixed}] the Rack environment
92
92
  # @param token [String] the token parsed from the HTTP header
93
93
  def inject_token_into_env(env, token)
94
- env['grape_jwt_auth.parsed_token'] = Jwt.new(token)
95
- rescue *Jwt::RESCUE_JWT_EXCEPTIONS
94
+ env['grape_jwt_auth.parsed_token'] = Keyless::Jwt.new(token)
95
+ rescue *Keyless::Jwt::RESCUE_JWT_EXCEPTIONS
96
96
  env['grape_jwt_auth.parsed_token'] = nil
97
97
  ensure
98
98
  env['grape_jwt_auth.original_token'] = token
@@ -3,7 +3,7 @@
3
3
  module Grape
4
4
  module Jwt
5
5
  module Authentication
6
- VERSION = '1.3.0'.freeze
6
+ VERSION = '2.0.0'.freeze
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-jwt-authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hermann Mayer
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-04 00:00:00.000000000 Z
11
+ date: 2020-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -212,6 +212,20 @@ dependencies:
212
212
  - - "~>"
213
213
  - !ruby/object:Gem::Version
214
214
  version: '1.0'
215
+ - !ruby/object:Gem::Dependency
216
+ name: keyless
217
+ requirement: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '1.0'
222
+ type: :runtime
223
+ prerelease: false
224
+ version_requirements: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - "~>"
227
+ - !ruby/object:Gem::Version
228
+ version: '1.0'
215
229
  description: A reusable Grape JWT authentication concern
216
230
  email:
217
231
  - hermann.mayer92@gmail.com
@@ -236,14 +250,13 @@ files:
236
250
  - grape-jwt-authentication.gemspec
237
251
  - lib/grape/jwt/authentication.rb
238
252
  - lib/grape/jwt/authentication/configuration.rb
239
- - lib/grape/jwt/authentication/jwt.rb
253
+ - lib/grape/jwt/authentication/dependencies.rb
240
254
  - lib/grape/jwt/authentication/jwt_handler.rb
241
- - lib/grape/jwt/authentication/rsa_public_key.rb
242
255
  - lib/grape/jwt/authentication/version.rb
243
256
  homepage: https://github.com/hausgold/grape-jwt-authentication
244
257
  licenses: []
245
258
  metadata: {}
246
- post_install_message:
259
+ post_install_message:
247
260
  rdoc_options: []
248
261
  require_paths:
249
262
  - lib
@@ -258,8 +271,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
271
  - !ruby/object:Gem::Version
259
272
  version: '0'
260
273
  requirements: []
261
- rubygems_version: 3.1.2
262
- signing_key:
274
+ rubygems_version: 3.1.4
275
+ signing_key:
263
276
  specification_version: 4
264
277
  summary: A reusable Grape JWT authentication concern
265
278
  test_files: []
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'recursive-open-struct'
4
-
5
- module Grape
6
- module Jwt
7
- module Authentication
8
- # A easy to use model for verification of JSON Web Tokens. This is just a
9
- # wrapper class for the excellent ruby-jwt gem. It's completely up to you
10
- # to use it. But be aware, its a bit optinionated by default.
11
- class Jwt
12
- # All the following JWT verification issues lead to a failed validation.
13
- RESCUE_JWT_EXCEPTIONS = [
14
- ::JWT::DecodeError,
15
- ::JWT::VerificationError,
16
- ::JWT::ExpiredSignature,
17
- ::JWT::IncorrectAlgorithm,
18
- ::JWT::ImmatureSignature,
19
- ::JWT::InvalidIssuerError,
20
- ::JWT::InvalidIatError,
21
- ::JWT::InvalidAudError,
22
- ::JWT::InvalidSubError,
23
- ::JWT::InvalidJtiError,
24
- ::JWT::InvalidPayload
25
- ].freeze
26
-
27
- # :reek:Attribute because its fine to be extern-modifiable at these
28
- # instances
29
- attr_reader :payload, :token
30
- attr_writer :verification_key, :jwt_options
31
- attr_accessor :issuer, :beholder
32
-
33
- # Setup a new JWT instance. You have to pass the raw JSON Web Token to
34
- # the initializer. Example:
35
- #
36
- # Jwt.new('j.w.t')
37
- # # => <Jwt>
38
- #
39
- # @return [Jwt]
40
- def initialize(token)
41
- parsed_payload = JWT.decode(token, nil, false).first.symbolize_keys
42
- @token = token
43
- @payload = RecursiveOpenStruct.new(parsed_payload)
44
- end
45
-
46
- # Checks if the payload says this is a refresh token.
47
- #
48
- # @return [Boolean] Whenever this is a access token
49
- def access_token?
50
- payload.typ == 'access'
51
- end
52
-
53
- # Checks if the payload says this is a refresh token.
54
- #
55
- # @return [Boolean] Whenever this is a refresh token
56
- def refresh_token?
57
- payload.typ == 'refresh'
58
- end
59
-
60
- # Retrives the expiration date from the payload when set.
61
- #
62
- # @return [nil|ActiveSupport::TimeWithZone] The expiration date
63
- def expires_at
64
- exp = payload.exp
65
- return nil unless exp
66
-
67
- Time.zone.at(exp)
68
- end
69
-
70
- # Deliver the public key for verification by default. This uses the
71
- # {RsaPublicKey} class, but you can configure the verification key the
72
- # way you like. (Especially for different algorithms, like HMAC or
73
- # ECDSA) Just make use of the same named setter.
74
- #
75
- # @return [OpenSSL::PKey::RSA|Mixed] The verification key
76
- def verification_key
77
- unless @verification_key
78
- conf = Grape::Jwt::Authentication.configuration
79
- return conf.jwt_verification_key.call
80
- end
81
- @verification_key
82
- end
83
-
84
- # This getter passes back the default JWT verification option hash
85
- # which is optinionated. You can change this the way you like by
86
- # configuring your options with the help of the same named setter.
87
- #
88
- # @return [Hash] The JWT verification options hash
89
- def jwt_options
90
- unless @jwt_options
91
- conf = Grape::Jwt::Authentication.configuration
92
- return conf.jwt_options.call
93
- end
94
- @jwt_options
95
- end
96
-
97
- # Verify the current token by our hard and strict rules. Whenever the
98
- # token was not parsed from a string, we encode the current state to a
99
- # JWT string representation and check this.
100
- #
101
- # @return [Boolean] Whenever the token is valid or not
102
- #
103
- # :reek:NilCheck because we have to check the token
104
- # origin and react on it
105
- def valid?
106
- JWT.decode(token, verification_key, true, jwt_options) && true
107
- rescue *RESCUE_JWT_EXCEPTIONS
108
- false
109
- end
110
- end
111
- end
112
- end
113
- end
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'singleton'
4
- require 'openssl'
5
- require 'httparty'
6
-
7
- module Grape
8
- module Jwt
9
- module Authentication
10
- # A common purpose RSA public key fetching/caching helper. With the help
11
- # of this class you are able to retrieve the RSA public key from a remote
12
- # server or a local file. This is naturally only useful if you care about
13
- # JSON Web Token which are signed by the RSA algorithm.
14
- class RsaPublicKey
15
- # A internal exception handling for failed fetch attempts.
16
- class FetchError < StandardError; end
17
-
18
- include Singleton
19
-
20
- # Setup all the getters and setters.
21
- attr_accessor :cache
22
- attr_writer :url, :expiration, :caching
23
-
24
- # Setup the instance.
25
- def initialize
26
- @expiration = 1.hour
27
- @cache = ActiveSupport::Cache::MemoryStore.new
28
- end
29
-
30
- # Just a simple shortcut class method to access the fetch method
31
- # without specifying the singleton instance.
32
- #
33
- # @return [OpenSSL::PKey::RSA]
34
- def self.fetch
35
- instance.fetch
36
- end
37
-
38
- # Configure the single instance. This is just a wrapper (like tap)
39
- # to the instance itself.
40
- def configure
41
- yield(self)
42
- end
43
-
44
- # Fetch the public key with the help of the configuration. You can
45
- # configure the public key location (local file, remote (HTTP/HTTPS)
46
- # file), whenever we should cache and how long to cache.
47
- #
48
- # @return [OpenSSL::PKey::RSA]
49
- def fetch
50
- encoded_key = if cache?
51
- cache.fetch('encoded_key', expires_in: expiration) do
52
- fetch_encoded_key
53
- end
54
- else
55
- fetch_encoded_key
56
- end
57
-
58
- OpenSSL::PKey::RSA.new(encoded_key)
59
- end
60
-
61
- # Fetch the encoded (DER, or PEM) public key from a remote or local
62
- # location.
63
- #
64
- # @return [String] The encoded public key
65
- def fetch_encoded_key
66
- raise ArgumentError, 'No URL for RsaPublicKey configured' unless url
67
-
68
- if remote?
69
- res = HTTParty.get(url)
70
- raise FetchError, res.inspect unless (200..299).include? res.code
71
- res.body
72
- else
73
- File.read(url)
74
- end
75
- end
76
-
77
- # A helper for the caching configuration.
78
- #
79
- # @return [Boolean]
80
- def cache?
81
- caching && true
82
- end
83
-
84
- # A helper to determine if the configured URL is on a remote server or
85
- # it is local on the filesystem. Whenever the configured URL specifies
86
- # the HTTP/HTTPS protocol, we assume it is remote.
87
- #
88
- # @return [Boolean]
89
- def remote?
90
- !(url =~ /^https?/).nil?
91
- end
92
-
93
- # This getter passes back the default RSA public key. You can change
94
- # this the way you like by configuring your URL with the help of the
95
- # same named setter.
96
- #
97
- # @return [String] The configured public key location
98
- def url
99
- unless @url
100
- conf = Grape::Jwt::Authentication.configuration
101
- return conf.rsa_public_key_url
102
- end
103
- @url
104
- end
105
-
106
- # This getter passes back the default public key cache expiration time.
107
- # You can change this time with the help of the same named setter.
108
- #
109
- # @return [Integer] The configured cache expiration time
110
- def expiration
111
- unless @expiration
112
- conf = Grape::Jwt::Authentication.configuration
113
- return conf.rsa_public_key_expiration
114
- end
115
- @expiration
116
- end
117
-
118
- # This getter passes back the caching flag. You can change this flag
119
- # with the help of the same named setter.
120
- #
121
- # @return [Boolean] Whenever we should cache or not
122
- def caching
123
- unless @caching
124
- conf = Grape::Jwt::Authentication.configuration
125
- return conf.rsa_public_key_caching
126
- end
127
- @caching
128
- end
129
- end
130
- end
131
- end
132
- end