warden-jwt 0.1.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.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +6 -0
  7. data/Guardfile +16 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +7 -0
  13. data/lib/warden/jwt/config.rb +117 -0
  14. data/lib/warden/jwt/strategy.rb +73 -0
  15. data/lib/warden/jwt/version.rb +5 -0
  16. data/lib/warden/jwt.rb +9 -0
  17. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_audience_check/and_invalid_audience/fails_authentication.yml +55 -0
  18. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_audience_check/and_valid_audience/authenticates_successfully.yml +55 -0
  19. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_invalid_password/fails_authentication.yml +52 -0
  20. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_invalid_username/fails_authentication.yml +52 -0
  21. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_issuer_check/and_invalid_issuer/raises_invalid_issuer_exception.yml +55 -0
  22. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_issuer_check/and_valid_issuer/authenticates_successfully.yml +55 -0
  23. data/spec/fixtures/vcr_cassettes/Warden_JWT_Strategy/_authenticate_/with_valid_parameters/authenticates_successfully.yml +55 -0
  24. data/spec/helpers/request_helper.rb +9 -0
  25. data/spec/spec_helper.rb +18 -0
  26. data/spec/support/vcr.rb +7 -0
  27. data/spec/warden/jwt/config_spec.rb +183 -0
  28. data/spec/warden/jwt/strategy_spec.rb +143 -0
  29. data/spec/warden/jwt_spec.rb +7 -0
  30. data/warden-jwt.gemspec +38 -0
  31. metadata +297 -0
@@ -0,0 +1,55 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://localhost:4000/oauth/token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=password&username=you%40example.com&password=mypassword
9
+ headers:
10
+ Accept:
11
+ - "*/*; q=0.5, application/xml"
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ Content-Length:
15
+ - '66'
16
+ Content-Type:
17
+ - application/x-www-form-urlencoded
18
+ User-Agent:
19
+ - Ruby
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ X-Frame-Options:
26
+ - SAMEORIGIN
27
+ X-Xss-Protection:
28
+ - 1; mode=block
29
+ X-Content-Type-Options:
30
+ - nosniff
31
+ Cache-Control:
32
+ - no-store
33
+ Pragma:
34
+ - no-cache
35
+ Content-Type:
36
+ - application/json; charset=utf-8
37
+ Etag:
38
+ - W/"e660f9f498b0d02ca450145e9ac9a20e"
39
+ Set-Cookie:
40
+ - _oauth-provider-doorkeeper_session=NUR4dnBROHZHTjhiTm5DZmF4LzJUQjY3Y0wvTFliMDA2U1RIQTJ2cFk3VjJpaWFWM3NGYmFmSE5wRm1mbHFQWGViOW9PYlRaenNZR0h2cERCOXMraUE9PS0tT0U3YlJnT0J5bm9sVmpwQnoxNXhkZz09--cca28b97961fe2e163405d48eed26c0f1767398d;
41
+ path=/; HttpOnly
42
+ X-Request-Id:
43
+ - 86aa1ef4-c4cb-446e-b45e-af9b82e6519d
44
+ X-Runtime:
45
+ - '0.134671'
46
+ Connection:
47
+ - close
48
+ Server:
49
+ - thin
50
+ body:
51
+ encoding: UTF-8
52
+ string: '{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOiIyMDE1LTA3LTI4VDA0OjE2OjUxLjAyMSswMDowMCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMCIsImF1ZCI6WyIxZjI0YmY1NDJiNjkyNWZmM2IxODAzMmY5MzExYzEyMmMyMTA2OGNlMGEwOTAzM2IyMDcxMTJmODI2ZGIzZDdmIl0sInVpZCI6OSwidW4iOiJ0ZXN0IiwiZW1haWwiOiJ5b3VAZXhhbXBsZS5jb20iLCJmbiI6IlRlc3QiLCJsbiI6IlRlc3QiLCJuIjoiVGVzdCIsInIiOiJkZWZhdWx0IiwibSI6W3sicCI6ImRlZmF1bHQiLCJvaWQiOiIxZjI0YmY1NDJiNjkyNWZmM2IxODAzMmY5MzExYzEyMmMyMTA2OGNlMGEwOTAzM2IyMDcxMTJmODI2ZGIzZDdmIiwibyI6IkRpZ2lmaW5kIn1dfQ.BjbvUcrR8HKo51wBA3hv3VlQimmS_oMrpSweri57-yk","token_type":"bearer","expires_in":7200,"refresh_token":"c20c932ebd9dcad41b47e7ab6998ad48fe365c8da605de95f7ce0dfd0a97ee1f","scope":"public","created_at":1438057011}'
53
+ http_version:
54
+ recorded_at: Tue, 28 Jul 2015 04:16:51 GMT
55
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,55 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://localhost:4000/oauth/token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=password&username=you%40example.com&password=mypassword
9
+ headers:
10
+ Accept:
11
+ - "*/*; q=0.5, application/xml"
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ Content-Length:
15
+ - '66'
16
+ Content-Type:
17
+ - application/x-www-form-urlencoded
18
+ User-Agent:
19
+ - Ruby
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ X-Frame-Options:
26
+ - SAMEORIGIN
27
+ X-Xss-Protection:
28
+ - 1; mode=block
29
+ X-Content-Type-Options:
30
+ - nosniff
31
+ Cache-Control:
32
+ - no-store
33
+ Pragma:
34
+ - no-cache
35
+ Content-Type:
36
+ - application/json; charset=utf-8
37
+ Etag:
38
+ - W/"745b7d26dc386138c4cac5914250021b"
39
+ Set-Cookie:
40
+ - _oauth-provider-doorkeeper_session=VGxZYkVEUHM2TXhqNkxTb0VqM3ZrMWJ0NEpLWm9iZzgvcWR3a2lxVktGd0lPMFBIbVBBVGE4dVBYSXFmVmxlckhiVjlleElGaEFFOEJzYmRBNUltZWc9PS0tVVNrVVhvL3pzaWs3aW1ZMlY0d0NpQT09--b8cdb35682eda8b627cb0bc52cd2918cec880d7c;
41
+ path=/; HttpOnly
42
+ X-Request-Id:
43
+ - 8c70bde2-a5a6-4341-9c4b-1e399dd750d7
44
+ X-Runtime:
45
+ - '0.379515'
46
+ Connection:
47
+ - close
48
+ Server:
49
+ - thin
50
+ body:
51
+ encoding: UTF-8
52
+ string: '{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOiIyMDE1LTA3LTI4VDA0OjE0OjA5Ljg0NCswMDowMCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMCIsImF1ZCI6WyIxZjI0YmY1NDJiNjkyNWZmM2IxODAzMmY5MzExYzEyMmMyMTA2OGNlMGEwOTAzM2IyMDcxMTJmODI2ZGIzZDdmIl0sInVpZCI6OSwidW4iOiJ0ZXN0IiwiZW1haWwiOiJ5b3VAZXhhbXBsZS5jb20iLCJmbiI6IlRlc3QiLCJsbiI6IlRlc3QiLCJuIjoiVGVzdCIsInIiOiJkZWZhdWx0IiwibSI6W3sicCI6ImRlZmF1bHQiLCJvaWQiOiIxZjI0YmY1NDJiNjkyNWZmM2IxODAzMmY5MzExYzEyMmMyMTA2OGNlMGEwOTAzM2IyMDcxMTJmODI2ZGIzZDdmIiwibyI6IkRpZ2lmaW5kIn1dfQ.DPWNg9ESIDUJRVGhD1ICKs93YryTWRNXyu4Fv7QmSwM","token_type":"bearer","expires_in":7200,"refresh_token":"aa729119459f41d03c7d6076f3115d5162ad00a2890d45ebc570002f3bed2f21","scope":"public","created_at":1438056849}'
53
+ http_version:
54
+ recorded_at: Tue, 28 Jul 2015 04:14:09 GMT
55
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,9 @@
1
+ module Warden::Spec
2
+ module Helpers
3
+ def env_with_params(path = "/", params = {}, env = {})
4
+ method = params.delete(:method) || "GET"
5
+ env = { 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => "#{method}" }.merge(env)
6
+ Rack::MockRequest.env_for("#{path}?#{Rack::Utils.build_query(params)}", env)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'warden/jwt'
6
+ require 'webmock/rspec'
7
+ require 'support/vcr'
8
+ require 'rubygems'
9
+ require 'rack'
10
+
11
+
12
+ Dir[File.join(File.dirname(__FILE__), "helpers", "**/*.rb")].each do |f|
13
+ require f
14
+ end
15
+
16
+ RSpec.configure do |config|
17
+ config.include(Warden::Spec::Helpers)
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'vcr'
2
+
3
+ VCR.configure do |config|
4
+ config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
5
+ config.hook_into :webmock
6
+ config.configure_rspec_metadata!
7
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::JWT::Config do
4
+ let(:warden_scope) { :test_scope }
5
+
6
+ let(:env) do
7
+ { 'warden' => double(:config => warden_config) }
8
+ end
9
+
10
+ let(:warden_config) do
11
+ { :scope_defaults => { warden_scope => { :config => scope_config } } }
12
+ end
13
+
14
+ let(:scope_config) do
15
+ {}
16
+ end
17
+
18
+ let(:request) do
19
+ double(:url => 'http://example.com/the/path', :path => '/the/path')
20
+ end
21
+
22
+ subject(:config) do
23
+ described_class.new(env, warden_scope)
24
+ end
25
+
26
+ before do
27
+ allow(config).to receive_messages(:request => request)
28
+ end
29
+
30
+ def silence_warnings
31
+ old_verbose, $VERBOSE = $VERBOSE, nil
32
+ yield
33
+ ensure
34
+ $VERBOSE = old_verbose
35
+ end
36
+
37
+ describe '#audience' do
38
+ context 'when specified in scope config' do
39
+ it 'returns the audience' do
40
+ scope_config[:audience] = 'foobar'
41
+ expect(config.audience).to eq 'foobar'
42
+ end
43
+ end
44
+
45
+ context 'when specified in ENV' do
46
+ it 'returns the audience' do
47
+ allow(ENV).to receive(:[]).with('IDENTITY_CLIENT_ID').and_return('foobar')
48
+ expect(config.audience).to eq 'foobar'
49
+ end
50
+ end
51
+
52
+ context 'when not specified' do
53
+ it 'raises BadConfig' do
54
+ expect { config.audience }.to raise_error(described_class::BadConfig)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#secret' do
60
+ context 'when specified in scope config' do
61
+ it 'returns the client secret' do
62
+ scope_config[:secret] = 'foobar'
63
+ expect(config.secret).to eq 'foobar'
64
+ end
65
+ end
66
+
67
+ context 'when specified in ENV' do
68
+ it 'returns the secret' do
69
+ allow(ENV).to receive(:[]).with('IDENTITY_SECRET').and_return('foobar')
70
+ silence_warnings do
71
+ expect(config.secret).to eq 'foobar'
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'when not specified' do
77
+ it 'raises BadConfig' do
78
+ expect { config.secret }.to raise_error(described_class::BadConfig)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe '#issuer' do
84
+ context 'when specified in scope config' do
85
+ it 'returns the identity uri' do
86
+ scope_config[:issuer] = 'http://example.com/callback'
87
+ expect(config.issuer).to eq 'http://example.com/callback'
88
+ end
89
+ end
90
+
91
+ context 'when specified in ENV' do
92
+ it 'returns the identity_uri' do
93
+ allow(ENV).to receive(:[]).with('IDENTITY_URL').and_return('http://example.com/callback')
94
+ silence_warnings do
95
+ expect(config.issuer).to eq 'http://example.com/callback'
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'when not specified' do
101
+ it 'raises BadConfig' do
102
+ expect { config.issuer }.to raise_error(described_class::BadConfig)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#username_param' do
108
+ context 'when specified in config' do
109
+ it 'returns the username param' do
110
+ scope_config[:username_param] = 'user'
111
+ expect(config.username_param).to eq 'user'
112
+ end
113
+ end
114
+
115
+ context 'when not specified' do
116
+ it 'returns username' do
117
+ expect(config.username_param).to eq 'username'
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#password_param' do
123
+ context 'when specified in config' do
124
+ it 'returns the password param' do
125
+ scope_config[:password_param] = 'pass'
126
+ expect(config.password_param).to eq 'pass'
127
+ end
128
+ end
129
+
130
+ context 'when not specified' do
131
+ it 'returns password' do
132
+ expect(config.password_param).to eq 'password'
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "#verify_issuer" do
138
+ context 'when specified in config' do
139
+ it 'returns the verify_issuer param' do
140
+ scope_config[:verify_issuer] = false
141
+ expect(config.verify_issuer).to eq false
142
+ end
143
+ end
144
+
145
+ context 'when not specified' do
146
+ it 'returns true' do
147
+ expect(config.verify_issuer).to eq true
148
+ end
149
+ end
150
+ end
151
+
152
+ describe "#verify_audience" do
153
+ context 'when specified in config' do
154
+ it 'returns the verify_audience param' do
155
+ scope_config[:verify_audience] = false
156
+ expect(config.verify_audience).to eq false
157
+ end
158
+ end
159
+
160
+ context 'when not specified' do
161
+ it 'returns true' do
162
+ expect(config.verify_audience).to eq true
163
+ end
164
+ end
165
+ end
166
+
167
+ describe '#to_hash' do
168
+ it 'includes all configs' do
169
+ scope_config.merge!(
170
+ :issuer => '/foo',
171
+ :audience => 'abc',
172
+ :secret => '123',
173
+ :username_param => 'wef',
174
+ :password_param => 'wef',
175
+ :verify_issuer => 'wef',
176
+ :verify_audience => 'wef'
177
+ )
178
+
179
+ expect(config.to_hash.keys).
180
+ to match_array([:issuer, :audience, :secret, :username_param, :password_param, :verify_issuer, :verify_audience])
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+ require 'warden/jwt'
3
+
4
+ describe Warden::JWT::Strategy do
5
+ let(:strategy) { Warden::Strategies[:jwt].new(env_with_params(path, params, env), warden_scope) }
6
+
7
+ let(:warden_scope) { :test_scope }
8
+
9
+ let(:env) do
10
+ { 'warden' => double(:config => warden_config) }
11
+ end
12
+
13
+ let(:warden_config) do
14
+ { :scope_defaults => { warden_scope => { :config => scope_config } } }
15
+ end
16
+
17
+ let(:secret) { '1fcd5a60e0f8f874c9297d7bc9e8af21a1e8be86add775fcaa1ed8a3722089f398a4ef6fa8a0cc115b85557a72920feaf894e4a34dc3f87dbf91d95f398b5c4c' }
18
+ let(:issuer) { 'http://localhost:4000'}
19
+ let(:audience) { '1f24bf542b6925ff3b18032f9311c122c21068ce0a09033b207112f826db3d7f' }
20
+ let(:verify_audience) { false }
21
+ let(:verify_issuer) { false }
22
+
23
+ let(:scope_config) do
24
+ {
25
+ :issuer => issuer,
26
+ :audience => audience,
27
+ :secret => secret,
28
+ :verify_audience => verify_audience,
29
+ :verify_issuer => verify_issuer
30
+ }
31
+ end
32
+
33
+ let(:params) { {} }
34
+
35
+ let(:path) { '/' }
36
+
37
+ before(:each) do
38
+ RAS = Warden::Strategies unless defined?(RAS)
39
+ Warden::Strategies.clear!
40
+ Warden::Strategies.add(:jwt, Warden::JWT::Strategy)
41
+ end
42
+
43
+ describe "#valid?" do
44
+ context "with empty params" do
45
+ let(:params) { {} }
46
+
47
+ it "is false" do
48
+ expect(strategy.valid?).to eq(false)
49
+ end
50
+ end
51
+
52
+ context "with only username param" do
53
+ let(:params) { {'username' => 'user'} }
54
+
55
+ it "is false" do
56
+ expect(strategy.valid?).to eq(false)
57
+ end
58
+ end
59
+
60
+ context "with only password param" do
61
+ let(:params) { {'password' => 'pass'} }
62
+
63
+ it "is false" do
64
+ expect(strategy.valid?).to eq(false)
65
+ end
66
+ end
67
+
68
+ context "with username and password" do
69
+ let(:params) { {'username' => 'user', 'password' => 'pass'} }
70
+
71
+ it "is true" do
72
+ expect(strategy.valid?).to eq(true)
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#decode_token_from_json" do
78
+ let(:respone) { }
79
+ end
80
+
81
+ describe "#authenticate!", :vcr do
82
+ let(:params) { {'username' => 'you@example.com', 'password' => 'mypassword'} }
83
+
84
+ context "with valid parameters" do
85
+ it "authenticates successfully" do
86
+ expect(strategy.authenticate!).to eq(:success)
87
+ end
88
+ end
89
+
90
+ context "with invalid password" do
91
+ let(:params) { {'username' => 'you@example.com', 'password' => 'pass'} }
92
+
93
+ it "fails authentication" do
94
+ expect(strategy.authenticate!).to eq(:failure)
95
+ end
96
+ end
97
+
98
+ context "with invalid username" do
99
+ let(:params) { {'username' => 'me@example.com', 'password' => 'pass'} }
100
+
101
+ it "fails authentication" do
102
+ expect(strategy.authenticate!).to eq(:failure)
103
+ end
104
+ end
105
+
106
+ context "with audience check" do
107
+ let(:verify_audience) { true }
108
+
109
+ context "and valid audience" do
110
+ it "authenticates successfully" do
111
+ expect(strategy.authenticate!).to eq(:success)
112
+ end
113
+ end
114
+
115
+ context "and invalid audience" do
116
+ let(:audience) { 'weiufhwef'}
117
+
118
+ it "fails authentication" do
119
+ expect(strategy.authenticate!).to eq(:failure)
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ context "with issuer check" do
126
+ let(:verify_issuer) { true }
127
+
128
+ context "and valid issuer" do
129
+ it "authenticates successfully" do
130
+ expect(strategy.authenticate!).to eq(:success)
131
+ end
132
+ end
133
+
134
+ context "and invalid issuer" do
135
+ let(:issuer) { 'http://localhost:2000/' }
136
+
137
+ it "raises invalid issuer exception" do
138
+ expect{ strategy.authenticate! }.to raise_error(JWT::InvalidIssuerError)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Warden::JWT do
4
+ it 'has a version number' do
5
+ expect(Warden::JWT::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'warden/jwt/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "warden-jwt"
8
+ spec.version = Warden::JWT::VERSION
9
+ spec.authors = ["Rob Sharp"]
10
+ spec.email = ["rob.sharp@digivizer.com"]
11
+
12
+ spec.summary = %q{A Warden strategy for JWT}
13
+ spec.homepage = "http://github.com/dgvz/warden-jwt"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency 'jwt', '~> 1.5'
23
+ spec.add_runtime_dependency 'rest-client', '~> 1.8'
24
+ spec.add_runtime_dependency 'warden', '~> 1.2'
25
+ spec.add_runtime_dependency 'addressable', '~> 2.3'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.0'
28
+ spec.add_development_dependency 'rake', '~> 10'
29
+ spec.add_development_dependency 'rack', '~> 1.6'
30
+ spec.add_development_dependency 'rspec', '~> 3.3'
31
+ spec.add_development_dependency 'simplecov', '~> 0.10'
32
+ spec.add_development_dependency 'vcr', '~> 2.9'
33
+ spec.add_development_dependency 'webmock', '~> 1.21'
34
+ spec.add_development_dependency 'multi_json', '~> 1.11'
35
+ spec.add_development_dependency 'guard', '~> 2.11'
36
+ spec.add_development_dependency 'guard-rspec', '~> 4.5'
37
+ spec.add_development_dependency 'guard-bundler', '~> 0.1'
38
+ end