rack-jwt 0.2.0 → 0.3.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
  SHA1:
3
- metadata.gz: 19267b5f494a49fb28e4a58a34a51aed16313480
4
- data.tar.gz: 085968e3d783b4e7e1bcc13cf85e0944da44a97e
3
+ metadata.gz: cfa47cb2c138bc46c403e896f61fa82773ef0afa
4
+ data.tar.gz: e8a2497f654c31721a1e7943ed0a880607fc2f0d
5
5
  SHA512:
6
- metadata.gz: 95d36e6594b6cae68ec827bc7b53774814ce42a8a1d551ef99d697c7ed28fce6dfb30ff8a62c0f3644f6070d51b6d22ee6dbb09897366323a4059b7dce696a97
7
- data.tar.gz: 9a7b0274512ba049c1a76071c7fb9850e4f36ba5b55d4401054a3206ef095b99c22340d0f12530796e8dea0f6c8f57dfa67539f9a81f10426336e9558c7c5128
6
+ metadata.gz: b52bb55b1d499d5bf1fc3c959845e52a24dc0e5650fa28fbd9cefa561e796cbee4e6f0227f7de66eaa91c2ebf9298ab4468ba87012461c45e759346a0ba04885
7
+ data.tar.gz: 7fdeabeb1ebdc4f1baf6a40ef6fc40c018c71673d222ba6171d318a575215eefc470e16e407277fddbde97f85f3921a822693228543e174ccaf0af06190d5a3c
data/LICENSE.txt CHANGED
@@ -1,22 +1,21 @@
1
- Copyright (c) 2015 Mr. Eigenbart
1
+ The MIT License (MIT)
2
2
 
3
- MIT License
3
+ Copyright (c) 2015 Mr. Eigenbart
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
12
11
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
15
14
 
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -4,11 +4,13 @@
4
4
  [![Build Status](https://travis-ci.org/eigenbart/rack-jwt.svg)](https://travis-ci.org/eigenbart/rack-jwt)
5
5
  [![Code Climate](https://codeclimate.com/github/eigenbart/rack-jwt/badges/gpa.svg)](https://codeclimate.com/github/eigenbart/rack-jwt)
6
6
 
7
+ ## About
8
+
7
9
  This gem provides JSON Web Token (JWT) based authentication.
8
10
 
9
11
  ## Installation
10
12
 
11
- Add this line to your application's Gemfile:
13
+ Add this line to your application's `Gemfile`:
12
14
 
13
15
  ```ruby
14
16
  gem 'rack-jwt'
@@ -16,45 +18,90 @@ gem 'rack-jwt'
16
18
 
17
19
  And then execute:
18
20
 
19
- $ bundle
21
+ ```
22
+ $ bundle install
23
+ ```
20
24
 
21
- Or install it yourself as:
25
+ Or install it directly with:
22
26
 
23
- $ gem install rack-jwt
27
+ ```
28
+ $ gem install rack-jwt
29
+ ```
24
30
 
25
31
  ## Usage
26
32
 
27
- `Rack::JWT::Auth` accepts several configuration options:
33
+ `Rack::JWT::Auth` accepts several configuration options. All options are passed in a single Ruby Hash:
34
+
35
+ * `secret` : required : `String` || `OpenSSL::PKey::RSA` || `OpenSSL::PKey::EC` : A cryptographically secure String (for HMAC algorithms) or a public key object of an appropriate type for public key algorithms. Set to `nil` if you are using the `'none'` algorithm.
28
36
 
29
- * `secret` : required : String : A cryptographically secure String that serves as the HMAC SHA-256 secret for the JSON Web Token.
30
- * `verify` : optional : Boolean : Determines whether JWT will verify tokens when decoded. Default is `true`.
31
- * `options` : optional : Hash : A hash of options that are passed through to JWT to configure supported claims. See [the ruby-jwt docs](https://github.com/progrium/ruby-jwt#support-for-reserved-claim-names) for the available options. By default only expiration (exp) and Not Before (nbf) claims are verified.
32
- * `exclude` : optional : Array : An Array of path strings representing paths that should not check for JWT authentication tokens before allowing access.
37
+ * `verify` : optional : Boolean : Determines whether JWT will verify tokens keys for mismatch key types when decoded. Default is `true`. Set to `false` if you are using the `'none'` algorithm.
33
38
 
39
+ * `options` : optional : Hash : A hash of options that are passed through to JWT to configure supported claims and algorithms. See [the ruby-jwt docs](https://github.com/progrium/ruby-jwt#support-for-reserved-claim-names) for much more info on the available options and how they work. These options are passed through without change to the underlying `ruby-jwt` gem. By default only expiration (exp) and Not Before (nbf) claims are verified. Pass in an algorithm choice like `{ algorithm: 'HS256' }`.
40
+
41
+ * `exclude` : optional : Array : An Array of path strings representing paths that should not be checked for the presence of a valid JWT token. Excludes sub-paths as of specified paths as well (e.g. `%w(/docs)` excludes `/docs/some/thing.html` also). Each path should start with a `/`. If a path matches the current request path this entire middleware is skipped and no authentication or verification of tokens takes place.
42
+
43
+ ## Example Server-Side Config
44
+
45
+ Where `my_args` is a `Hash` containing valid keys. See `spec/example_spec.rb`
46
+ for a more complete example of the valid arguments for creating and verifying
47
+ tokens.
34
48
 
35
49
  ### Sinatra
36
50
 
37
- ```
38
- use Rack::JWT::Auth, secret: 'you_secret_token_goes_here', verify: true, options: {}, exclude: ['/api/docs']
51
+ ```ruby
52
+ use Rack::JWT::Auth, my_args
39
53
  ```
40
54
 
41
55
  ### Cuba
42
56
 
43
- ```
44
- Cuba.use Rack::JWT::Auth, secret: 'you_secret_token_goes_here', verify: true, options: {}, exclude: ['/api/docs']
57
+ ```ruby
58
+ Cuba.use Rack::JWT::Auth, my_args
45
59
  ```
46
60
 
47
61
  ### Rails
48
62
 
49
- ```
50
- Rails.application.config.middleware.use, Rack::JWT::Auth, secret: Rails.application.secrets.secret_key_base, verify: true, options: {}, exclude: ['/api/docs']
63
+ ```ruby
64
+ Rails.application.config.middleware.use, Rack::JWT::Auth, my_args
51
65
  ```
52
66
 
53
67
  ## Generating tokens
54
- You can generate JSON Wen Tokens for your users using the `Token#encode` method
68
+ You can generate JSON Web Tokens for your users using the
69
+ `Rack::JWT::Token#encode` method which takes `payload`,
70
+ `secret`, and `algorithm` params.
71
+
72
+ The secret will be either a cryptographically strong random string, or the
73
+ secret key component of a public/private keypair of an accepted type depending on
74
+ the algorithm you choose. You can see examples of using the various key types at
75
+ the [ruby-jwt gem repo](https://github.com/jwt/ruby-jwt/blob/master/README.md)
76
+
77
+ The `algorithm` is an optional String and can be one of the following (default HMAC 'HS256'):
55
78
 
79
+ ```ruby
80
+ %w(none HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512)
81
+
82
+ HS256 is the default
56
83
  ```
57
- Rack::JWT::Token.encode(payload, secret)
84
+
85
+ Here is a sample payload with illustrative data. You don't have to use all,
86
+ or even most, of these.
87
+
88
+ ```ruby
89
+ secret = 'your_secret_token_or_key'
90
+
91
+ my_payload = {
92
+ data: 'data',
93
+ exp: Time.now.to_i + 4 * 3600,
94
+ nbf: Time.now.to_i - 3600,
95
+ iss: 'https://my.awesome.website/',
96
+ aud: 'audience',
97
+ jti: Digest::MD5.hexdigest([hmac_secret, iat].join(':').to_s),
98
+ iat: Time.now.to_i,
99
+ sub: 'subject'
100
+ }
101
+
102
+ alg = 'HS256'
103
+
104
+ Rack::JWT::Token.encode(my_payload, secret, alg)
58
105
  ```
59
106
 
60
107
  ## Contributing
data/lib/rack/jwt.rb CHANGED
@@ -1,6 +1,7 @@
1
- require "rack/jwt/version"
1
+ require 'rack/jwt/version'
2
2
 
3
3
  module Rack
4
+ # JSON Web Token
4
5
  module JWT
5
6
  autoload :Auth, 'rack/jwt/auth'
6
7
  autoload :Token, 'rack/jwt/token'
data/lib/rack/jwt/auth.rb CHANGED
@@ -2,57 +2,179 @@ require 'jwt'
2
2
 
3
3
  module Rack
4
4
  module JWT
5
+ # Authentication middleware
5
6
  class Auth
7
+ attr_reader :secret
8
+ attr_reader :verify
9
+ attr_reader :options
10
+ attr_reader :exclude
11
+
12
+ SUPPORTED_ALGORITHMS = %w(none HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512).freeze
13
+ DEFAULT_ALGORITHM = 'HS256'.freeze
14
+
15
+ # The last segment gets dropped for 'none' algorithm since there is no
16
+ # signature so both of these patterns are valid. All character chunks
17
+ # are base64url format and periods.
18
+ # Bearer abc123.abc123.abc123
19
+ # Bearer abc123.abc123.
20
+ BEARER_TOKEN_REGEX = %r{
21
+ ^Bearer\s{1}( # starts with Bearer and a single space
22
+ [a-zA-Z0-9\-\_]+\. # 1 or more chars followed by a single period
23
+ [a-zA-Z0-9\-\_]+\. # 1 or more chars followed by a single period
24
+ [a-zA-Z0-9\-\_]* # 0 or more chars, no trailing chars
25
+ )$
26
+ }x
27
+
28
+ # Initialization should fail fast with an ArgumentError
29
+ # if any args are invalid.
6
30
  def initialize(app, opts = {})
7
- @app = app
8
- @jwt_secret = opts.fetch(:secret)
9
- @jwt_verify = opts.fetch(:verify, true)
10
- @jwt_options = opts.fetch(:options, {})
11
- @exclude = opts.fetch(:exclude, [])
31
+ @app = app
32
+ @secret = opts.fetch(:secret, nil)
33
+ @verify = opts.fetch(:verify, true)
34
+ @options = opts.fetch(:options, {})
35
+ @exclude = opts.fetch(:exclude, [])
36
+
37
+ @secret = @secret.strip if @secret.is_a?(String)
38
+ @options[:algorithm] = DEFAULT_ALGORITHM if @options[:algorithm].nil?
39
+
40
+ check_secret_type!
41
+ check_secret!
42
+ check_secret_and_verify_for_none_alg!
43
+ check_verify_type!
44
+ check_options_type!
45
+ check_valid_algorithm!
46
+ check_exclude_type!
12
47
  end
13
48
 
14
49
  def call(env)
15
- if @exclude.include? env['PATH_INFO']
50
+ if path_matches_excluded_path?(env)
16
51
  @app.call(env)
52
+ elsif missing_auth_header?(env)
53
+ return_error('Missing Authorization header')
54
+ elsif invalid_auth_header?(env)
55
+ return_error('Invalid Authorization header format')
17
56
  else
18
- check_jwt_token(env)
57
+ verify_token(env)
19
58
  end
20
59
  end
21
60
 
22
61
  private
23
62
 
24
- def check_jwt_token(env)
25
- if valid_header?(env)
26
- begin
27
- token = env['HTTP_AUTHORIZATION'].split(' ')[-1]
28
- decoded_token = Token.decode(token, @jwt_secret, @jwt_verify, @jwt_options)
29
- env['jwt.header'] = decoded_token.last unless decoded_token.nil?
30
- env['jwt.payload'] = decoded_token.first unless decoded_token.nil?
31
- @app.call(env)
32
- rescue ::JWT::DecodeError
33
- return_error('Invalid JWT token')
63
+ def verify_token(env)
64
+ # extract the token from the Authorization: Bearer header
65
+ # with a regex capture group.
66
+ token = BEARER_TOKEN_REGEX.match(env['HTTP_AUTHORIZATION'])[1]
67
+
68
+ begin
69
+ decoded_token = Token.decode(token, @secret, @verify, @options)
70
+ env['jwt.payload'] = decoded_token.first
71
+ env['jwt.header'] = decoded_token.last
72
+ @app.call(env)
73
+ rescue ::JWT::VerificationError
74
+ return_error('Invalid JWT token : Signature Verification Error')
75
+ rescue ::JWT::ExpiredSignature
76
+ return_error('Invalid JWT token : Expired Signature (exp)')
77
+ rescue ::JWT::IncorrectAlgorithm
78
+ return_error('Invalid JWT token : Incorrect Key Algorithm')
79
+ rescue ::JWT::ImmatureSignature
80
+ return_error('Invalid JWT token : Immature Signature (nbf)')
81
+ rescue ::JWT::InvalidIssuerError
82
+ return_error('Invalid JWT token : Invalid Issuer (iss)')
83
+ rescue ::JWT::InvalidIatError
84
+ return_error('Invalid JWT token : Invalid Issued At (iat)')
85
+ rescue ::JWT::InvalidAudError
86
+ return_error('Invalid JWT token : Invalid Audience (aud)')
87
+ rescue ::JWT::InvalidSubError
88
+ return_error('Invalid JWT token : Invalid Subject (sub)')
89
+ rescue ::JWT::InvalidJtiError
90
+ return_error('Invalid JWT token : Invalid JWT ID (jti)')
91
+ rescue ::JWT::DecodeError
92
+ return_error('Invalid JWT token : Decode Error')
93
+ end
94
+ end
95
+
96
+ def check_secret_type!
97
+ unless @secret.nil? ||
98
+ @secret.is_a?(String) ||
99
+ @secret.is_a?(OpenSSL::PKey::RSA) ||
100
+ @secret.is_a?(OpenSSL::PKey::EC)
101
+ raise ArgumentError, 'secret argument must be a valid type'
102
+ end
103
+ end
104
+
105
+ def check_secret!
106
+ if @secret.nil? || (@secret.is_a?(String) && @secret.empty?)
107
+ unless @options[:algorithm] == 'none'
108
+ raise ArgumentError, 'secret argument can only be nil/empty for the "none" algorithm'
34
109
  end
35
- else
36
- return_jwt_header_error(env)
37
110
  end
38
111
  end
39
112
 
40
- def return_jwt_header_error(env)
41
- if env['HTTP_AUTHORIZATION'].nil?
42
- return_error('Missing Authorization header')
43
- elsif env['HTTP_AUTHORIZATION'].split(' ').first != 'Bearer'
44
- return_error('Invalid Authorization header format')
113
+ def check_secret_and_verify_for_none_alg!
114
+ if @options && @options[:algorithm] && @options[:algorithm] == 'none'
115
+ unless @secret.nil? && @verify.is_a?(FalseClass)
116
+ raise ArgumentError, 'when "none" the secret must be "nil" and verify "false"'
117
+ end
118
+ end
119
+ end
120
+
121
+ def check_verify_type!
122
+ unless verify.is_a?(TrueClass) || verify.is_a?(FalseClass)
123
+ raise ArgumentError, 'verify argument must be true or false'
45
124
  end
46
125
  end
47
126
 
48
- def valid_header?(env)
49
- env['HTTP_AUTHORIZATION'] =~ /\ABearer \S*\.\S*\.\S*\z/
127
+ def check_options_type!
128
+ raise ArgumentError, 'options argument must be a Hash' unless options.is_a?(Hash)
129
+ end
130
+
131
+ def check_valid_algorithm!
132
+ unless @options &&
133
+ @options[:algorithm] &&
134
+ SUPPORTED_ALGORITHMS.include?(@options[:algorithm])
135
+ raise ArgumentError, 'algorithm argument must be a supported type'
136
+ end
137
+ end
138
+
139
+ def check_exclude_type!
140
+ unless @exclude.is_a?(Array)
141
+ raise ArgumentError, 'exclude argument must be an Array'
142
+ end
143
+
144
+ @exclude.each do |x|
145
+ unless x.is_a?(String)
146
+ raise ArgumentError, 'each exclude Array element must be a String'
147
+ end
148
+
149
+ if x.empty?
150
+ raise ArgumentError, 'each exclude Array element must not be empty'
151
+ end
152
+
153
+ unless x.start_with?('/')
154
+ raise ArgumentError, 'each exclude Array element must start with a /'
155
+ end
156
+ end
157
+ end
158
+
159
+ def path_matches_excluded_path?(env)
160
+ @exclude.any? { |ex| env['PATH_INFO'].start_with?(ex) }
161
+ end
162
+
163
+ def valid_auth_header?(env)
164
+ env['HTTP_AUTHORIZATION'] =~ BEARER_TOKEN_REGEX
165
+ end
166
+
167
+ def invalid_auth_header?(env)
168
+ !valid_auth_header?(env)
169
+ end
170
+
171
+ def missing_auth_header?(env)
172
+ env['HTTP_AUTHORIZATION'].nil? || env['HTTP_AUTHORIZATION'].strip.empty?
50
173
  end
51
174
 
52
175
  def return_error(message)
53
176
  body = { error: message }.to_json
54
- headers = { 'Content-Type' => 'application/json',
55
- 'Content-Length' => body.bytesize.to_s }
177
+ headers = { 'Content-Type' => 'application/json', 'Content-Length' => body.bytesize.to_s }
56
178
 
57
179
  [401, headers, [body]]
58
180
  end
@@ -1,15 +1,67 @@
1
1
  module Rack
2
2
  module JWT
3
+ # Token encoding and decoding
3
4
  class Token
4
- # TODO : Support all algorithms, not just default 'HS256'
5
- # https://github.com/progrium/ruby-jwt#algorithms-and-usage
6
- def self.encode(payload, secret)
7
- ::JWT.encode(payload, secret, 'HS256')
5
+ # abc123.abc123.abc123 (w/ signature)
6
+ # abc123.abc123. ('none')
7
+ TOKEN_REGEX = /\A([a-zA-Z0-9\-\_\~\+\\]+\.[a-zA-Z0-9\-\_\~\+\\]+\.[a-zA-Z0-9\-\_\~\+\\]*)\z/
8
+
9
+ def self.encode(payload, secret, alg = 'HS256')
10
+ raise 'Invalid payload. Must be a Hash.' unless payload.is_a?(Hash)
11
+ raise 'Invalid secret type.' unless secret_of_valid_type?(secret)
12
+ raise 'Unsupported algorithm' unless algorithm_supported?(alg)
13
+
14
+ # if using an unsigned token ('none' alg) you *must* set the `secret`
15
+ # to `nil` in which case any user provided `secret` will be ignored.
16
+ if alg == 'none'
17
+ ::JWT.encode(payload, nil, alg)
18
+ else
19
+ ::JWT.encode(payload, secret, alg)
20
+ end
21
+ end
22
+
23
+ def self.decode(token, secret, verify, options = {})
24
+ raise 'Invalid token format.' unless valid_token_format?(token)
25
+ raise 'Invalid secret type.' unless secret_of_valid_type?(secret)
26
+ raise 'Unsupported verify value.' unless verify_of_valid_type?(verify)
27
+ options[:algorithm] = 'HS256' if options[:algorithm].nil?
28
+ raise 'Unsupported algorithm' unless algorithm_supported?(options[:algorithm])
29
+
30
+ # If using an unsigned 'none' algorithm token you *must* set the
31
+ # `secret` to `nil` and `verify` to `false` or it won't work per
32
+ # the ruby-jwt docs. Using 'none' is probably not recommended.
33
+ if options[:algorithm] == 'none'
34
+ ::JWT.decode(token, nil, false, options)
35
+ else
36
+ ::JWT.decode(token, secret, verify, options)
37
+ end
38
+ end
39
+
40
+ # Private Utility Class Methods
41
+ # See : https://gist.github.com/Integralist/bb8760d11a03c88da151
42
+
43
+ def self.valid_token_format?(token)
44
+ token =~ TOKEN_REGEX
45
+ end
46
+ private_class_method :valid_token_format?
47
+
48
+ def self.algorithm_supported?(alg)
49
+ Rack::JWT::Auth::SUPPORTED_ALGORITHMS.include?(alg)
50
+ end
51
+ private_class_method :algorithm_supported?
52
+
53
+ def self.verify_of_valid_type?(verify)
54
+ verify.nil? || verify.is_a?(FalseClass) || verify.is_a?(TrueClass)
8
55
  end
56
+ private_class_method :verify_of_valid_type?
9
57
 
10
- def self.decode(token, secret, verify, options)
11
- ::JWT.decode(token, secret, verify, options)
58
+ def self.secret_of_valid_type?(secret)
59
+ secret.nil? ||
60
+ secret.is_a?(String) ||
61
+ secret.is_a?(OpenSSL::PKey::RSA) ||
62
+ secret.is_a?(OpenSSL::PKey::EC)
12
63
  end
64
+ private_class_method :secret_of_valid_type?
13
65
  end
14
66
  end
15
67
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module JWT
3
- VERSION = "0.2.0"
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mr. Eigenbart
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-16 00:00:00.000000000 Z
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.7'
19
+ version: '1.6'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.7'
26
+ version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 10.0.0
33
+ version: '10.5'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 10.0.0
40
+ version: '10.5'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rack-test
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,42 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.2.0
61
+ version: 3.4.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.2.0
68
+ version: 3.4.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.11.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.11.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.37.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.37.2
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rack
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +114,14 @@ dependencies:
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: 1.5.0
117
+ version: 1.5.2
90
118
  type: :runtime
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: 1.5.0
124
+ version: 1.5.2
97
125
  description: Rack middleware that provides authentication based on JSON Web Tokens.
98
126
  email:
99
127
  - eigenbart@gmail.com
@@ -101,21 +129,13 @@ executables: []
101
129
  extensions: []
102
130
  extra_rdoc_files: []
103
131
  files:
104
- - ".gitignore"
105
- - ".rspec"
106
- - ".travis.yml"
107
- - Gemfile
108
132
  - LICENSE.txt
109
133
  - README.md
110
- - Rakefile
111
134
  - lib/rack/jwt.rb
112
135
  - lib/rack/jwt/auth.rb
113
136
  - lib/rack/jwt/token.rb
114
137
  - lib/rack/jwt/version.rb
115
- - rack-jwt.gemspec
116
- - spec/auth_spec.rb
117
- - spec/spec_helper.rb
118
- homepage: ''
138
+ homepage: https://github.com/eigenbart/rack-jwt
119
139
  licenses:
120
140
  - MIT
121
141
  metadata: {}
@@ -127,7 +147,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
147
  requirements:
128
148
  - - ">="
129
149
  - !ruby/object:Gem::Version
130
- version: 1.9.3
150
+ version: 2.1.0
131
151
  required_rubygems_version: !ruby/object:Gem::Requirement
132
152
  requirements:
133
153
  - - ">="
@@ -139,6 +159,4 @@ rubygems_version: 2.4.5
139
159
  signing_key:
140
160
  specification_version: 4
141
161
  summary: Rack middleware that provides authentication based on JSON Web Tokens.
142
- test_files:
143
- - spec/auth_spec.rb
144
- - spec/spec_helper.rb
162
+ test_files: []
data/.gitignore DELETED
@@ -1,14 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- *.bundle
11
- *.so
12
- *.o
13
- *.a
14
- mkmf.log
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- rvm:
2
- - 1.9.3
3
- - 2.0.0
4
- - 2.2.0
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in rack-jwt.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require 'rspec/core/rake_task'
3
-
4
- desc "Run RSpec"
5
- RSpec::Core::RakeTask.new do |t|
6
- t.verbose = false
7
- end
8
-
9
- task :default => :spec
data/rack-jwt.gemspec DELETED
@@ -1,29 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'rack/jwt/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "rack-jwt"
8
- spec.version = Rack::JWT::VERSION
9
- spec.authors = ["Mr. Eigenbart"]
10
- spec.email = ["eigenbart@gmail.com"]
11
- spec.summary = %q{Rack middleware that provides authentication based on JSON Web Tokens.}
12
- spec.description = %q{Rack middleware that provides authentication based on JSON Web Tokens.}
13
- spec.homepage = ""
14
- spec.license = "MIT"
15
-
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
- spec.required_ruby_version = '>= 1.9.3'
21
-
22
- spec.add_development_dependency 'bundler', '~> 1.7'
23
- spec.add_development_dependency 'rake', '~> 10.0.0'
24
- spec.add_development_dependency 'rack-test', '~> 0.6.3'
25
- spec.add_development_dependency 'rspec', '~> 3.2.0'
26
-
27
- spec.add_runtime_dependency 'rack', '>= 1.6.0'
28
- spec.add_runtime_dependency 'jwt', '~> 1.5.0'
29
- end
data/spec/auth_spec.rb DELETED
@@ -1,243 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Rack::JWT::Auth do
4
- include Rack::Test::Methods
5
-
6
- let(:issuer) { Rack::JWT::Token }
7
- let(:secret) { 'foo' }
8
- let(:verify) { true }
9
- let(:options) { {} }
10
- let(:body) {{ 'foo' => 'bar' }}
11
-
12
- let(:app) do
13
- main_app = lambda { |env| [200, env, [body.to_json]] }
14
- Rack::JWT::Auth.new(main_app, { secret: secret })
15
- end
16
-
17
- def perform_request
18
- get('/', {}, headers)
19
- end
20
-
21
- context 'when no secret provided' do
22
- let(:headers) { {} }
23
-
24
- it 'raises an exception' do
25
- expect{ Rack::JWT::Auth.new(main_app, {}) }.to raise_error
26
- end
27
- end
28
-
29
- context 'when no authorization header provided' do
30
- let(:headers) { {} }
31
-
32
- subject { JSON.parse(last_response.body) }
33
-
34
- before { perform_request }
35
-
36
- it 'returns 401 status code' do
37
- expect(last_response.status).to eq(401)
38
- end
39
-
40
- it 'returns an error message' do
41
- expect(subject['error']).to eq('Missing Authorization header')
42
- end
43
- end
44
-
45
- context 'when authorization header does not contain the schema' do
46
- let(:token) { issuer.encode({ iss: 1 }, secret) }
47
- let(:headers) {{ 'HTTP_AUTHORIZATION' => token }}
48
-
49
- subject { JSON.parse(last_response.body) }
50
-
51
- before { perform_request }
52
-
53
- it 'returns 401 status code' do
54
- expect(last_response.status).to eq(401)
55
- end
56
-
57
- it 'returns an error message' do
58
- expect(subject['error']).to eq('Invalid Authorization header format')
59
- end
60
- end
61
-
62
- context 'when authorization header contains an invalid schema' do
63
- let(:token) { issuer.encode({ iss: 1 }, secret) }
64
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "WrongScheme #{token}" }}
65
-
66
- subject { JSON.parse(last_response.body) }
67
-
68
- before { perform_request }
69
-
70
- it 'returns 401 status code' do
71
- expect(last_response.status).to eq(401)
72
- end
73
-
74
- it 'returns an error message' do
75
- expect(subject['error']).to eq('Invalid Authorization header format')
76
- end
77
- end
78
-
79
- context 'when token signature is invalid' do
80
- let(:token) { issuer.encode({ iss: 1 }, 'invalid secret') }
81
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
82
-
83
- subject { JSON.parse(last_response.body) }
84
-
85
- before { perform_request }
86
-
87
- it 'returns 401 status code' do
88
- expect(last_response.status).to eq(401)
89
- end
90
-
91
- it 'returns an error message' do
92
- expect(subject['error']).to eq('Invalid JWT token')
93
- end
94
- end
95
-
96
- context 'when token signature is invalid and JWT verify option is false' do
97
- let(:app) do
98
- main_app = lambda { |env| [200, env, [body.to_json]] }
99
- Rack::JWT::Auth.new(main_app, { secret: secret, verify: false })
100
- end
101
- let(:token) { issuer.encode({ iss: 1 }, 'invalid secret') }
102
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
103
-
104
- before { perform_request }
105
-
106
- subject { JSON.parse(last_response.body) }
107
-
108
- it 'returns 200 status code' do
109
- expect(last_response.status).to eq(200)
110
- end
111
- end
112
-
113
- context 'when token is valid' do
114
- let(:token) { issuer.encode({ iss: 1 }, secret) }
115
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
116
-
117
- subject { JSON.parse(last_response.body) }
118
-
119
- before { perform_request }
120
-
121
- it 'returns 200 status code' do
122
- expect(last_response.status).to eq(200)
123
- end
124
-
125
- it 'process the request' do
126
- expect(subject).to eq(body)
127
- end
128
-
129
- it 'adds the token payload to the request' do
130
- payload = last_response.header['jwt.payload']
131
- expect(payload['iss']).to eq(1)
132
- end
133
-
134
- it 'adds the token header to the request' do
135
- header = last_response.header['jwt.header']
136
- expect(header['alg']).to eq('HS256')
137
- expect(header['typ']).to eq('JWT')
138
- end
139
- end
140
-
141
- context 'when token is valid but app raises an error unrelated to JWT' do
142
- let(:token) { issuer.encode({ iss: 1 }, secret) }
143
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
144
-
145
- let(:app) do
146
- main_app = lambda { |env| raise 'BOOM!' }
147
- Rack::JWT::Auth.new(main_app, { secret: secret })
148
- end
149
-
150
- it 'bubbles up the exception' do
151
- expect { perform_request }.to raise_error('BOOM!')
152
- end
153
- end
154
-
155
- # Test the pass-through of the options Hash to JWT using Issued At (iat) claim to test..
156
- ###
157
-
158
- context 'when token is valid and an invalid Issued At (iat) claim is provided JWT should ignore bad iat by default' do
159
- let(:token) { issuer.encode({ iss: 1, iat: Time.now.to_i + 1000000 }, secret) }
160
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
161
-
162
- before { perform_request }
163
-
164
- subject { JSON.parse(last_response.body) }
165
-
166
- it 'returns 200 status code' do
167
- expect(last_response.status).to eq(200)
168
- end
169
-
170
- it 'process the request' do
171
- expect(subject).to eq(body)
172
- end
173
-
174
- it 'adds the token payload to the request' do
175
- payload = last_response.header['jwt.payload']
176
- expect(payload['iss']).to eq(1)
177
- end
178
-
179
- it 'adds the token header to the request' do
180
- header = last_response.header['jwt.header']
181
- expect(header['alg']).to eq('HS256')
182
- expect(header['typ']).to eq('JWT')
183
- end
184
- end
185
-
186
- context 'when token is valid and an invalid Issued At (iat) claim is provided and iat verification option is enabled' do
187
- # The token was issued at an insane time in the future.
188
- let(:iat) { Time.now.to_i + 1000000 }
189
- let(:app) do
190
- main_app = lambda { |env| [200, env, [body.to_json]] }
191
- Rack::JWT::Auth.new(main_app, { secret: secret, options: { :verify_iat => true } })
192
- end
193
- let(:token) { issuer.encode({ iss: 1, iat: iat }, secret) }
194
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
195
-
196
- before { perform_request }
197
-
198
- subject { JSON.parse(last_response.body) }
199
-
200
- it 'returns 401 status code' do
201
- expect(last_response.status).to eq(401)
202
- end
203
-
204
- it 'returns an error message' do
205
- expect(subject['error']).to eq('Invalid JWT token')
206
- end
207
- end
208
-
209
- context 'when token is valid and a valid Issued At (iat) claim is provided and iat verification option is enabled' do
210
- # The token was issued at a sane Time.now
211
- let(:iat) { Time.now.to_i }
212
- let(:app) do
213
- main_app = lambda { |env| [200, env, [body.to_json]] }
214
- Rack::JWT::Auth.new(main_app, { secret: secret, options: { :verify_iat => true } })
215
- end
216
- let(:token) { issuer.encode({ iss: 1, iat: iat }, secret) }
217
- let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
218
-
219
- before { perform_request }
220
-
221
- subject { JSON.parse(last_response.body) }
222
-
223
- it 'returns 200 status code' do
224
- expect(last_response.status).to eq(200)
225
- end
226
-
227
- it 'process the request' do
228
- expect(subject).to eq(body)
229
- end
230
-
231
- it 'adds the token payload to the request' do
232
- payload = last_response.header['jwt.payload']
233
- expect(payload['iss']).to eq(1)
234
- end
235
-
236
- it 'adds the token header to the request' do
237
- header = last_response.header['jwt.header']
238
- expect(header['alg']).to eq('HS256')
239
- expect(header['typ']).to eq('JWT')
240
- end
241
- end
242
-
243
- end
data/spec/spec_helper.rb DELETED
@@ -1,2 +0,0 @@
1
- require "rack/test"
2
- require "rack/jwt"