rack-jwt 0.1.1 → 0.2.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 +4 -4
- data/README.md +17 -3
- data/lib/rack/jwt/auth.rb +7 -5
- data/lib/rack/jwt/token.rb +5 -6
- data/lib/rack/jwt/version.rb +2 -2
- data/rack-jwt.gemspec +2 -2
- data/spec/auth_spec.rb +137 -5
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19267b5f494a49fb28e4a58a34a51aed16313480
|
4
|
+
data.tar.gz: 085968e3d783b4e7e1bcc13cf85e0944da44a97e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95d36e6594b6cae68ec827bc7b53774814ce42a8a1d551ef99d697c7ed28fce6dfb30ff8a62c0f3644f6070d51b6d22ee6dbb09897366323a4059b7dce696a97
|
7
|
+
data.tar.gz: 9a7b0274512ba049c1a76071c7fb9850e4f36ba5b55d4401054a3206ef095b99c22340d0f12530796e8dea0f6c8f57dfa67539f9a81f10426336e9558c7c5128
|
data/README.md
CHANGED
@@ -24,16 +24,30 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
|
27
|
+
`Rack::JWT::Auth` accepts several configuration options:
|
28
|
+
|
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.
|
33
|
+
|
34
|
+
|
35
|
+
### Sinatra
|
36
|
+
|
37
|
+
```
|
38
|
+
use Rack::JWT::Auth, secret: 'you_secret_token_goes_here', verify: true, options: {}, exclude: ['/api/docs']
|
39
|
+
```
|
40
|
+
|
41
|
+
### Cuba
|
28
42
|
|
29
43
|
```
|
30
|
-
use Rack::JWT::Auth secret: 'you_secret_token_goes_here', exclude: ['/api/docs']
|
44
|
+
Cuba.use Rack::JWT::Auth, secret: 'you_secret_token_goes_here', verify: true, options: {}, exclude: ['/api/docs']
|
31
45
|
```
|
32
46
|
|
33
47
|
### Rails
|
34
48
|
|
35
49
|
```
|
36
|
-
Rails.application.config.middleware.use, Rack::JWT::Auth, secret: Rails.application.secrets.secret_key_base, exclude: ['/api/docs']
|
50
|
+
Rails.application.config.middleware.use, Rack::JWT::Auth, secret: Rails.application.secrets.secret_key_base, verify: true, options: {}, exclude: ['/api/docs']
|
37
51
|
```
|
38
52
|
|
39
53
|
## Generating tokens
|
data/lib/rack/jwt/auth.rb
CHANGED
@@ -5,7 +5,9 @@ module Rack
|
|
5
5
|
class Auth
|
6
6
|
def initialize(app, opts = {})
|
7
7
|
@app = app
|
8
|
-
@
|
8
|
+
@jwt_secret = opts.fetch(:secret)
|
9
|
+
@jwt_verify = opts.fetch(:verify, true)
|
10
|
+
@jwt_options = opts.fetch(:options, {})
|
9
11
|
@exclude = opts.fetch(:exclude, [])
|
10
12
|
end
|
11
13
|
|
@@ -23,11 +25,11 @@ module Rack
|
|
23
25
|
if valid_header?(env)
|
24
26
|
begin
|
25
27
|
token = env['HTTP_AUTHORIZATION'].split(' ')[-1]
|
26
|
-
decoded_token = Token.decode(token, @
|
27
|
-
env['jwt.header'] = decoded_token.last
|
28
|
-
env['jwt.payload'] = decoded_token.first
|
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?
|
29
31
|
@app.call(env)
|
30
|
-
rescue
|
32
|
+
rescue ::JWT::DecodeError
|
31
33
|
return_error('Invalid JWT token')
|
32
34
|
end
|
33
35
|
else
|
data/lib/rack/jwt/token.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
module Rack
|
2
2
|
module JWT
|
3
3
|
class Token
|
4
|
+
# TODO : Support all algorithms, not just default 'HS256'
|
5
|
+
# https://github.com/progrium/ruby-jwt#algorithms-and-usage
|
4
6
|
def self.encode(payload, secret)
|
5
|
-
::JWT.encode(payload, secret)
|
7
|
+
::JWT.encode(payload, secret, 'HS256')
|
6
8
|
end
|
7
9
|
|
8
|
-
def self.decode(token, secret)
|
9
|
-
::JWT.decode(token, secret)
|
10
|
-
rescue
|
11
|
-
# It will raise an error if it is not a valid token due to any reason
|
12
|
-
nil
|
10
|
+
def self.decode(token, secret, verify, options)
|
11
|
+
::JWT.decode(token, secret, verify, options)
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
data/lib/rack/jwt/version.rb
CHANGED
data/rack-jwt.gemspec
CHANGED
@@ -5,7 +5,7 @@ require 'rack/jwt/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "rack-jwt"
|
8
|
-
spec.version = Rack::
|
8
|
+
spec.version = Rack::JWT::VERSION
|
9
9
|
spec.authors = ["Mr. Eigenbart"]
|
10
10
|
spec.email = ["eigenbart@gmail.com"]
|
11
11
|
spec.summary = %q{Rack middleware that provides authentication based on JSON Web Tokens.}
|
@@ -25,5 +25,5 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_development_dependency 'rspec', '~> 3.2.0'
|
26
26
|
|
27
27
|
spec.add_runtime_dependency 'rack', '>= 1.6.0'
|
28
|
-
spec.add_runtime_dependency 'jwt', '~> 1.
|
28
|
+
spec.add_runtime_dependency 'jwt', '~> 1.5.0'
|
29
29
|
end
|
data/spec/auth_spec.rb
CHANGED
@@ -3,16 +3,18 @@ require 'spec_helper'
|
|
3
3
|
describe Rack::JWT::Auth do
|
4
4
|
include Rack::Test::Methods
|
5
5
|
|
6
|
-
let(:issuer)
|
7
|
-
let(:secret)
|
8
|
-
let(:
|
6
|
+
let(:issuer) { Rack::JWT::Token }
|
7
|
+
let(:secret) { 'foo' }
|
8
|
+
let(:verify) { true }
|
9
|
+
let(:options) { {} }
|
10
|
+
let(:body) {{ 'foo' => 'bar' }}
|
9
11
|
|
10
12
|
let(:app) do
|
11
13
|
main_app = lambda { |env| [200, env, [body.to_json]] }
|
12
14
|
Rack::JWT::Auth.new(main_app, { secret: secret })
|
13
15
|
end
|
14
16
|
|
15
|
-
|
17
|
+
def perform_request
|
16
18
|
get('/', {}, headers)
|
17
19
|
end
|
18
20
|
|
@@ -29,6 +31,8 @@ describe Rack::JWT::Auth do
|
|
29
31
|
|
30
32
|
subject { JSON.parse(last_response.body) }
|
31
33
|
|
34
|
+
before { perform_request }
|
35
|
+
|
32
36
|
it 'returns 401 status code' do
|
33
37
|
expect(last_response.status).to eq(401)
|
34
38
|
end
|
@@ -38,12 +42,14 @@ describe Rack::JWT::Auth do
|
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
41
|
-
context 'when
|
45
|
+
context 'when authorization header does not contain the schema' do
|
42
46
|
let(:token) { issuer.encode({ iss: 1 }, secret) }
|
43
47
|
let(:headers) {{ 'HTTP_AUTHORIZATION' => token }}
|
44
48
|
|
45
49
|
subject { JSON.parse(last_response.body) }
|
46
50
|
|
51
|
+
before { perform_request }
|
52
|
+
|
47
53
|
it 'returns 401 status code' do
|
48
54
|
expect(last_response.status).to eq(401)
|
49
55
|
end
|
@@ -59,6 +65,8 @@ describe Rack::JWT::Auth do
|
|
59
65
|
|
60
66
|
subject { JSON.parse(last_response.body) }
|
61
67
|
|
68
|
+
before { perform_request }
|
69
|
+
|
62
70
|
it 'returns 401 status code' do
|
63
71
|
expect(last_response.status).to eq(401)
|
64
72
|
end
|
@@ -74,6 +82,8 @@ describe Rack::JWT::Auth do
|
|
74
82
|
|
75
83
|
subject { JSON.parse(last_response.body) }
|
76
84
|
|
85
|
+
before { perform_request }
|
86
|
+
|
77
87
|
it 'returns 401 status code' do
|
78
88
|
expect(last_response.status).to eq(401)
|
79
89
|
end
|
@@ -83,12 +93,31 @@ describe Rack::JWT::Auth do
|
|
83
93
|
end
|
84
94
|
end
|
85
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
|
+
|
86
113
|
context 'when token is valid' do
|
87
114
|
let(:token) { issuer.encode({ iss: 1 }, secret) }
|
88
115
|
let(:headers) {{ 'HTTP_AUTHORIZATION' => "Bearer #{token}" }}
|
89
116
|
|
90
117
|
subject { JSON.parse(last_response.body) }
|
91
118
|
|
119
|
+
before { perform_request }
|
120
|
+
|
92
121
|
it 'returns 200 status code' do
|
93
122
|
expect(last_response.status).to eq(200)
|
94
123
|
end
|
@@ -108,4 +137,107 @@ describe Rack::JWT::Auth do
|
|
108
137
|
expect(header['typ']).to eq('JWT')
|
109
138
|
end
|
110
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
|
+
|
111
243
|
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.
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2015-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 1.
|
89
|
+
version: 1.5.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 1.
|
96
|
+
version: 1.5.0
|
97
97
|
description: Rack middleware that provides authentication based on JSON Web Tokens.
|
98
98
|
email:
|
99
99
|
- eigenbart@gmail.com
|