devise_g5_authenticatable 0.1.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 647e697e95fa5a4f468529c0c016c5e9a8f2a8bd
4
- data.tar.gz: 5795af637ed1ba2bce1bc36eae8fc765ea9ce88b
3
+ metadata.gz: 7b68a3deae4616dbf188a936d77e442d39dd761d
4
+ data.tar.gz: d615bdc6cbce1fec5ba81cdcd2a3bf5454beda29
5
5
  SHA512:
6
- metadata.gz: 6a17aa3b3d6cdb0a1e3b2c93aa973df48e6b6a13d6ed923cc2da1b1be813c14e110e7dcebc4ffbdc6449c7224b099bd305234a3f1024b16efb3fe79b5ed788a1
7
- data.tar.gz: a59a344ee27a3927e89bae41de06c72a654b9a12acd62e36c7ddfc7e5a05743a07bbeb237e6cab70cf8637c6be1b6ea3bef4b2c95dcef241b72ff05bf6a140bf
6
+ metadata.gz: c3f7e0ca76d585d91942ea3f98b285e153ed6d49e2cb26369112507d28f7e7c562b742408f224c02525c44c519e6c07399bba6b51bbf251380da5831d1729c47
7
+ data.tar.gz: 43b89ca19bc3938df96be0861492767277cc1d96f2ecff341b9e2571e9a0277f8d129c1635fbbb9f0f3c116af1a436837c6a4c58aa2bac4378eb5561e51901ab
@@ -1,3 +1,8 @@
1
+ ## v0.2.0 (2015-01-08)
2
+
3
+ * Add support for strict token validation, disabled by default
4
+ ([#17](https://github.com/G5/devise_g5_authenticatable/pull/17))
5
+
1
6
  ## v0.1.3 (2014-12-19)
2
7
 
3
8
  * Fix sign out when there isn't a locally authenticated user
data/README.md CHANGED
@@ -10,7 +10,7 @@ G5 users.
10
10
 
11
11
  ## Current Version
12
12
 
13
- 0.1.3
13
+ 0.2.0
14
14
 
15
15
  ## Requirements
16
16
 
@@ -66,7 +66,7 @@ environment variables for your client application:
66
66
 
67
67
  ### Configuration
68
68
 
69
- In `config/initializers/devise.rb`, add the following:
69
+ In `config/initializers/devise.rb`, add the following to enable authentication:
70
70
 
71
71
  ```ruby
72
72
  Devise.setup do |config|
@@ -182,6 +182,29 @@ will need to insert the following in your `config/initializers/devise.rb`:
182
182
  require 'devise_g5_authenticatable/models/protected_attributes'
183
183
  ```
184
184
 
185
+ ### Token validation
186
+
187
+ After a user authenticates, their access token will be stored on the G5
188
+ Authenticatable model. By default, this token will not be validated against
189
+ the auth server again until the local session is destroyed, either because
190
+ it times out, or because the user explicitly logs out of the local application.
191
+
192
+ In order to implement single sign-out, you can enable strict token validation.
193
+ This will validate the token against the auth server on every request, and
194
+ automatically destroy the local session if the token is no longer valid.
195
+
196
+ In your `config/initializers/devise.rb':
197
+
198
+ ```ruby
199
+ Devise.setup do |config|
200
+ # ...
201
+ config.g5_strict_token_validation = true
202
+ end
203
+ ```
204
+
205
+ Note that strict token validation incurs a non-trivial amount of performance
206
+ overhead, which is why it is disabled by default.
207
+
185
208
  ## Examples
186
209
 
187
210
  Currently, the best source of example code is in the [test Rails
@@ -9,6 +9,14 @@ require 'devise_g5_authenticatable/controllers/url_helpers'
9
9
 
10
10
  require 'devise_g5_authenticatable/engine'
11
11
 
12
+ module Devise
13
+ # Should devise_g5_authenticatable validate the user's access token
14
+ # against the auth server for every request? Default is false
15
+ @@g5_strict_token_validation = false
16
+
17
+ mattr_accessor :g5_strict_token_validation
18
+ end
19
+
12
20
  Devise.add_module(:g5_authenticatable,
13
21
  strategy: false,
14
22
  route: {session: [nil, :new, :destroy]},
@@ -0,0 +1,16 @@
1
+ Warden::Manager.after_set_user only: :fetch do |record, warden, options|
2
+ if Devise.g5_strict_token_validation
3
+ scope = options[:scope]
4
+
5
+ auth_client = G5AuthenticationClient::Client.new(allow_password_credentials: 'false',
6
+ access_token: record.g5_access_token)
7
+ begin
8
+ auth_client.token_info
9
+ rescue StandardError => error
10
+ proxy = Devise::Hooks::Proxy.new(warden)
11
+ proxy.sign_out(record)
12
+ record.revoke_g5_credentials!
13
+ throw :warden, scope: scope
14
+ end
15
+ end
16
+ end
@@ -1,4 +1,5 @@
1
1
  require 'devise_g5_authenticatable/g5'
2
+ require 'devise_g5_authenticatable/hooks/g5_authenticatable'
2
3
 
3
4
  module Devise
4
5
  module Models
@@ -1,3 +1,3 @@
1
1
  module DeviseG5Authenticatable
2
- VERSION = '0.1.3'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Token validation per request' do
4
+ let(:user) { create(:user) }
5
+ let(:protected_path) { edit_user_registration_path }
6
+ let(:token_info_url) { 'http://auth.g5search.com/oauth/token/info' }
7
+
8
+ before do
9
+ stub_request(:get, token_info_url).
10
+ with(headers: {'Authorization'=>"Bearer #{user.g5_access_token}"}).
11
+ to_return(status: 200, body: '', headers: {})
12
+ end
13
+
14
+ before do
15
+ visit_path_and_login_with(protected_path, user)
16
+
17
+ # Now that we're logged in, any subsequent attempts to
18
+ # authenticate with the auth server will trigger an omniauth
19
+ # failure, which is a condition we can test for
20
+ stub_g5_invalid_credentials
21
+ end
22
+
23
+ context 'when token validation is disabled' do
24
+ before do
25
+ Devise.g5_strict_token_validation = false
26
+ visit protected_path
27
+ end
28
+
29
+ it 'should not valid the token against the auth server' do
30
+ expect(a_request(:get, token_info_url)).to_not have_been_made
31
+ end
32
+
33
+ it 'should allow the user to access the protected page' do
34
+ expect(current_path).to eq(protected_path)
35
+ end
36
+ end
37
+
38
+ context 'when token validation is enabled' do
39
+ before { Devise.g5_strict_token_validation = true }
40
+
41
+ context 'when the access_token is valid' do
42
+ before { visit protected_path }
43
+
44
+ it 'should validate the token against the auth server' do
45
+ expect(a_request(:get, token_info_url).
46
+ with(headers: {'Authorization' => "Bearer #{user.g5_access_token}"})).to have_been_made
47
+ end
48
+
49
+ it 'should allow the user to access the protected page' do
50
+ expect(current_path).to eq(protected_path)
51
+ end
52
+ end
53
+
54
+ context 'when the access_token has been invalidated' do
55
+ before do
56
+ stub_request(:get, token_info_url).
57
+ with(headers: {'Authorization'=>"Bearer #{user.g5_access_token}"}).
58
+ to_return(status: 401,
59
+ headers: {'Content-Type' => 'application/json; charset=utf-8',
60
+ 'Cache-Control' => 'no-cache'},
61
+ body: {'error' => 'invalid_token',
62
+ 'error_description' => 'The access token expired'}.to_json)
63
+ visit protected_path
64
+ end
65
+
66
+ it 'should force the user to re-authenticate' do
67
+ expect(page).to have_content('Invalid credentials')
68
+ end
69
+ end
70
+
71
+ context 'when there is some other error from the auth server' do
72
+ before do
73
+ stub_request(:get, token_info_url).to_raise(StandardError)
74
+ visit protected_path
75
+ end
76
+
77
+ it 'should force the user to re-authenticate' do
78
+ expect(page).to have_content('Invalid credentials')
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,4 @@
1
1
  RSpec.configure do |config|
2
+ config.before(:each) { Devise.g5_strict_token_validation = false }
2
3
  config.include Devise::TestHelpers, type: :controller
3
4
  end
@@ -19,7 +19,10 @@ module UserFeatureMethods
19
19
  end
20
20
 
21
21
  RSpec.configure do |config|
22
- config.before(:each) { OmniAuth.config.test_mode = true }
22
+ config.before(:each) do
23
+ OmniAuth.config.test_mode = true
24
+ OmniAuth.config.mock_auth[:g5] = nil
25
+ end
23
26
  config.after(:each) { OmniAuth.config.test_mode = false }
24
27
 
25
28
  config.include UserFeatureMethods, type: :feature
@@ -6,20 +6,30 @@ describe 'g5:export_users' do
6
6
  let(:user_exporter) { double(:user_exporter, export: nil) }
7
7
  before { allow(G5::UserExporter).to receive(:new).and_return(user_exporter) }
8
8
 
9
- before { ENV['G5_AUTH_CLIENT_ID'] = default_client_id }
9
+ let!(:old_client_id) { ENV['G5_AUTH_CLIENT_ID'] }
10
10
  let(:default_client_id) { 'default_client_id' }
11
+ before { ENV['G5_AUTH_CLIENT_ID'] = default_client_id }
12
+ after { ENV['G5_AUTH_CLIENT_ID'] = old_client_id }
11
13
 
12
- before { ENV['G5_AUTH_CLIENT_SECRET'] = default_client_secret }
14
+ let!(:old_client_secret) { ENV['G5_AUTH_CLIENT_SECRET'] }
13
15
  let(:default_client_secret) { 'default_client_secret' }
16
+ before { ENV['G5_AUTH_CLIENT_SECRET'] = default_client_secret }
17
+ after { ENV['G5_AUTH_CLIENT_SECRET'] = old_client_secret }
14
18
 
15
- before { ENV['G5_AUTH_REDIRECT_URI'] = default_redirect_uri }
19
+ let!(:old_redirect_uri) { ENV['G5_AUTH_REDIRECT_URI'] }
16
20
  let(:default_redirect_uri) { 'http://test.host/default' }
21
+ before { ENV['G5_AUTH_REDIRECT_URI'] = default_redirect_uri }
22
+ after { ENV['G5_AUTH_REDIRECT_URI'] = old_redirect_uri }
17
23
 
18
- before { ENV['G5_AUTH_ENDPOINT'] = default_endpoint }
24
+ let!(:old_endpoint) { ENV['G5_AUTH_ENDPOINT'] }
19
25
  let(:default_endpoint) { 'https://my.g5auth.host' }
26
+ before { ENV['G5_AUTH_ENDPOINT'] = default_endpoint }
27
+ after { ENV['G5_AUTH_ENDPOINT'] = old_endpoint }
20
28
 
21
- before { ENV['G5_AUTH_AUTHORIZATION_CODE'] = default_auth_code }
29
+ let!(:old_auth_code) { ENV['G5_AUTH_AUTHORIZATION_CODE'] }
22
30
  let(:default_auth_code) { 'default_auth_code' }
31
+ before { ENV['G5_AUTH_AUTHORIZATION_CODE'] = default_auth_code }
32
+ after { ENV['G5_AUTH_AUTHORIZATION_CODE'] = old_auth_code }
23
33
 
24
34
  def expect_init_user_exporter_with(option_name, expected_value)
25
35
  expect(G5::UserExporter).to receive(:new) do |args|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_g5_authenticatable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maeve Revels
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-19 00:00:00.000000000 Z
11
+ date: 2015-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: devise
@@ -82,6 +82,7 @@ files:
82
82
  - lib/devise_g5_authenticatable/g5/auth_user_creator.rb
83
83
  - lib/devise_g5_authenticatable/g5/auth_user_updater.rb
84
84
  - lib/devise_g5_authenticatable/g5/user_exporter.rb
85
+ - lib/devise_g5_authenticatable/hooks/g5_authenticatable.rb
85
86
  - lib/devise_g5_authenticatable/models/g5_authenticatable.rb
86
87
  - lib/devise_g5_authenticatable/models/protected_attributes.rb
87
88
  - lib/devise_g5_authenticatable/omniauth.rb
@@ -160,6 +161,7 @@ files:
160
161
  - spec/features/registration_spec.rb
161
162
  - spec/features/sign_in_spec.rb
162
163
  - spec/features/sign_out_spec.rb
164
+ - spec/features/token_validation_spec.rb
163
165
  - spec/g5/auth_password_validator_spec.rb
164
166
  - spec/g5/auth_user_creator_spec.rb
165
167
  - spec/g5/auth_user_updater_spec.rb
@@ -273,6 +275,7 @@ test_files:
273
275
  - spec/features/registration_spec.rb
274
276
  - spec/features/sign_in_spec.rb
275
277
  - spec/features/sign_out_spec.rb
278
+ - spec/features/token_validation_spec.rb
276
279
  - spec/g5/auth_password_validator_spec.rb
277
280
  - spec/g5/auth_user_creator_spec.rb
278
281
  - spec/g5/auth_user_updater_spec.rb