prx_auth-rails 1.7.0 → 1.8.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: 9a692b8cc459acfdf888599245ff2db7d7ed92a3c967fbccacdd871aa4e78cd7
4
- data.tar.gz: 17439da5ec7e7cc7f85d00083a47aaebb6d6bd495db25fa75581bf0758726d6c
3
+ metadata.gz: 5a687c733815e035f361fbfd4981e3673480311c6d760206b9b1edd4903e084b
4
+ data.tar.gz: 5b04a0584489415a7b0223342c85e638c8c3ca01c141e4ff46db9269c192f6be
5
5
  SHA512:
6
- metadata.gz: 2a185d858e8a50a33e1f18c85e7dabf5f01a46fbc068dd74d222bb48ab03732ced79c60affd45f7e70efd628cfaaf259e9d4781c390470de86a06dd46158bf0c
7
- data.tar.gz: d8f2b9c1c1367c2954575950f1ddc731f329f59d111efcb11282d78651a06cf22c74557f834d51ff6fa3ca7360d1cb06ad4736cd2a41d0ff28c5554913fcc193
6
+ metadata.gz: 71573dd4f1303e7e2b5fc4ca908c502c74aa986845ddb685613b6e8422c05e136c1952f5bfc2989a607b1a5020f708a06190ebe084a199a46aeefab1f7168706
7
+ data.tar.gz: dc5685c9e2908ea56c10f577eb2a457167087345f07b09927e8c36c1b0d2df513e2f2367436fe4f637aac648311c75a44ee3f845c63124be7ab4009c45dccb57
@@ -4,13 +4,11 @@ module PrxAuth::Rails
4
4
 
5
5
  skip_before_action :authenticate!
6
6
 
7
- before_action :set_nonce!, only: :show
7
+ before_action :set_nonce!, only: [:new, :show]
8
8
 
9
9
  ID_NONCE_SESSION_KEY = 'id_prx_openid_nonce'.freeze
10
10
 
11
11
  def new
12
- set_nonce! unless fetch_nonce.present?
13
-
14
12
  config = PrxAuth::Rails.configuration
15
13
 
16
14
  id_auth_params = {
@@ -37,30 +35,27 @@ module PrxAuth::Rails
37
35
  end
38
36
 
39
37
  def create
40
- jwt_id_claims = id_claims
41
- jwt_access_claims = access_claims
42
-
43
- jwt_access_claims['id_token'] = jwt_id_claims.as_json
44
-
45
- result_path = if valid_nonce?(jwt_id_claims['nonce']) &&
46
- users_match?(jwt_id_claims, jwt_access_claims)
47
- sign_in_user(jwt_access_claims)
48
- lookup_and_register_accounts_names
49
- after_sign_in_path_for(current_user)
50
- else
51
- auth_error_sessions_path(error: 'verification_failed')
52
- end
53
- reset_nonce!
54
-
55
- redirect_to result_path
38
+ if valid_nonce? && users_match?
39
+ clear_nonce!
40
+ sign_in_user(access_token)
41
+ redirect_to after_sign_in_path_for(current_user)
42
+ else
43
+ clear_nonce!
44
+ redirect_to auth_error_sessions_path(error: 'verification_failed')
45
+ end
56
46
  end
57
47
 
58
48
  private
59
49
 
60
50
  def after_sign_in_path_for(_)
61
- return super if defined?(super)
62
-
63
- "/"
51
+ back_path = after_sign_in_user_redirect
52
+ if back_path.present?
53
+ back_path
54
+ elsif defined?(super)
55
+ super
56
+ else
57
+ '/'
58
+ end
64
59
  end
65
60
 
66
61
  def after_sign_out_path
@@ -69,49 +64,46 @@ module PrxAuth::Rails
69
64
  "https://#{id_host}/session/sign_out"
70
65
  end
71
66
 
67
+ def id_token
68
+ params.require('id_token')
69
+ end
70
+
71
+ def access_token
72
+ params.require('access_token')
73
+ end
74
+
72
75
  def id_claims
73
- id_token = params.require('id_token')
74
- validate_token(id_token)
76
+ @id_claims ||= validate_token(id_token)
75
77
  end
76
78
 
77
79
  def access_claims
78
- access_token = params.require('access_token')
79
- validate_token(access_token)
80
+ @access_claims ||= validate_token(access_token)
80
81
  end
81
82
 
82
- def reset_nonce!
83
- session[ID_NONCE_SESSION_KEY] = nil
83
+ def clear_nonce!
84
+ session.delete(ID_NONCE_SESSION_KEY)
84
85
  end
85
86
 
86
87
  def set_nonce!
87
- n = session[ID_NONCE_SESSION_KEY]
88
- return n if n.present?
89
-
90
- session[ID_NONCE_SESSION_KEY] = SecureRandom.hex
88
+ session[ID_NONCE_SESSION_KEY] ||= SecureRandom.hex
91
89
  end
92
90
 
93
91
  def fetch_nonce
94
92
  session[ID_NONCE_SESSION_KEY]
95
93
  end
96
94
 
97
- def valid_nonce?(nonce)
98
- return false if fetch_nonce.nil?
99
-
100
- fetch_nonce == nonce
95
+ def valid_nonce?
96
+ id_claims['nonce'].present? && id_claims['nonce'] == fetch_nonce
101
97
  end
102
98
 
103
- def users_match?(claims1, claims2)
104
- return false if claims1['sub'].nil? || claims2['sub'].nil?
105
-
106
- claims1['sub'] == claims2['sub']
99
+ def users_match?
100
+ id_claims['sub'].present? && id_claims['sub'] == access_claims['sub']
107
101
  end
108
102
 
109
103
  def validate_token(token)
110
104
  prx_auth_cert = Rack::PrxAuth::Certificate.new("https://#{id_host}/api/v1/certs")
111
105
  auth_validator = Rack::PrxAuth::AuthValidator.new(token, prx_auth_cert, id_host)
112
- auth_validator.
113
- claims.
114
- with_indifferent_access
106
+ auth_validator.claims.with_indifferent_access
115
107
  end
116
108
 
117
109
  def id_host
@@ -2,7 +2,10 @@ module PrxAuth
2
2
  module Rails
3
3
  class Engine < ::Rails::Engine
4
4
  config.to_prepare do
5
- ::ApplicationController.helper_method [:current_user, :account_name_for]
5
+ ::ApplicationController.helper_method [
6
+ :current_user, :prx_jwt,
7
+ :account_name_for, :account_for, :accounts_for,
8
+ ]
6
9
  end
7
10
  end
8
11
  end
@@ -4,15 +4,24 @@ require 'open-uri'
4
4
  module PrxAuth
5
5
  module Rails
6
6
  module Controller
7
+ class SessionTokenExpiredError < RuntimeError; end
7
8
 
8
- PRX_ACCOUNT_NAME_MAPPING_KEY = 'prx.account.name.mapping'.freeze
9
- PRX_TOKEN_SESSION_KEY = 'prx.auth'.freeze
9
+ PRX_AUTH_ENV_KEY = 'prx.auth'.freeze
10
+ PRX_JWT_SESSION_KEY = 'prx.auth.jwt'.freeze
11
+ PRX_JWT_REFRESH_TTL = 300.freeze
12
+ PRX_ACCOUNT_MAPPING_SESSION_KEY = 'prx.auth.account.mapping'.freeze
13
+ PRX_REFRESH_BACK_KEY = 'prx.auth.back'.freeze
10
14
 
11
15
  def prx_auth_token
12
- rack_auth_token = env_prx_auth_token
13
- return rack_auth_token if rack_auth_token.present?
16
+ env_token || session_token
17
+ rescue SessionTokenExpiredError
18
+ reset_session
19
+ session[PRX_REFRESH_BACK_KEY] = request.fullpath
20
+ redirect_to PrxAuth::Rails::Engine.routes.url_helpers.new_sessions_path
21
+ end
14
22
 
15
- session[PRX_TOKEN_SESSION_KEY] && Rack::PrxAuth::TokenData.new(session[PRX_TOKEN_SESSION_KEY])
23
+ def prx_jwt
24
+ session[PRX_JWT_SESSION_KEY]
16
25
  end
17
26
 
18
27
  def prx_authenticated?
@@ -26,51 +35,51 @@ module PrxAuth
26
35
  end
27
36
 
28
37
  def current_user
29
- return if prx_auth_token.nil?
30
-
31
- PrxAuth::Rails::Token.new(prx_auth_token)
38
+ prx_auth_token
32
39
  end
33
40
 
34
- def lookup_and_register_accounts_names
35
- session[PRX_ACCOUNT_NAME_MAPPING_KEY] =
36
- lookup_account_names_mapping
41
+ def sign_in_user(token)
42
+ session[PRX_JWT_SESSION_KEY] = token
43
+ accounts_for(current_user.resources)
37
44
  end
38
45
 
39
- def account_name_for(id)
40
- id = id.to_i
41
-
42
- session[PRX_ACCOUNT_NAME_MAPPING_KEY] ||= {}
43
-
44
- name =
45
- if session[PRX_ACCOUNT_NAME_MAPPING_KEY].has_key?(id)
46
- session[PRX_ACCOUNT_NAME_MAPPING_KEY][id]
47
- else
48
- session[PRX_ACCOUNT_NAME_MAPPING_KEY][id] = lookup_account_name_for(id)
49
- end
46
+ def after_sign_in_user_redirect
47
+ session.delete(PRX_REFRESH_BACK_KEY)
48
+ end
50
49
 
51
- name = "[#{id}] Unknown Account Name" unless name.present?
50
+ def sign_out_user
51
+ reset_session
52
+ end
52
53
 
53
- name
54
+ def account_name_for(account_id)
55
+ account_for(account_id)[:name]
54
56
  end
55
57
 
56
- def sign_in_user(token)
57
- session[PRX_TOKEN_SESSION_KEY] = token
58
+ def account_for(account_id)
59
+ lookup_accounts([account_id]).first
58
60
  end
59
61
 
60
- def sign_out_user
61
- session.delete(PRX_TOKEN_SESSION_KEY)
62
+ def accounts_for(account_ids)
63
+ lookup_accounts(account_ids)
62
64
  end
63
65
 
64
66
  private
65
67
 
66
- def lookup_account_name_for(id)
67
- id = id.to_i
68
+ def lookup_accounts(ids)
69
+ session[PRX_ACCOUNT_MAPPING_SESSION_KEY] ||= {}
70
+
71
+ # fetch any accounts we don't have yet
72
+ missing = ids - session[PRX_ACCOUNT_MAPPING_SESSION_KEY].keys
73
+ if missing.present?
74
+ fetch_accounts(missing).each do |account|
75
+ session[PRX_ACCOUNT_MAPPING_SESSION_KEY][account['id']] = account.with_indifferent_access
76
+ end
77
+ end
68
78
 
69
- res = lookup_account_names_mapping([id])
70
- res[id]
79
+ ids.map { |id| session[PRX_ACCOUNT_MAPPING_SESSION_KEY][id] }
71
80
  end
72
81
 
73
- def lookup_account_names_mapping(ids=current_user.resources)
82
+ def fetch_accounts(ids)
74
83
  id_host = PrxAuth::Rails.configuration.id_host
75
84
  ids_param = ids.map(&:to_s).join(',')
76
85
 
@@ -79,16 +88,31 @@ module PrxAuth
79
88
 
80
89
  accounts = URI.open("https://#{id_host}/api/v1/accounts?account_ids=#{ids_param}", options).read
81
90
 
82
- mapping = JSON.parse(accounts)['accounts'].map { |acct| [acct['id'], acct['display_name']] }.to_h
91
+ JSON.parse(accounts)['accounts']
92
+ end
83
93
 
84
- mapping
94
+ # token from data set by prx_auth rack middleware
95
+ def env_token
96
+ @env_token_data ||= if request.env[PRX_AUTH_ENV_KEY]
97
+ token_data = request.env[PRX_AUTH_ENV_KEY]
98
+ PrxAuth::Rails::Token.new(token_data)
99
+ end
85
100
  end
86
101
 
87
- def env_prx_auth_token
88
- if !defined? @_prx_auth_token
89
- @_prx_auth_token = request.env['prx.auth'] && PrxAuth::Rails::Token.new(request.env['prx.auth'])
90
- else
91
- @_prx_auth_token
102
+ # token from jwt stored in session
103
+ def session_token
104
+ @session_prx_auth_token ||= if prx_jwt
105
+ # NOTE: we already validated this jwt - so just decode it
106
+ validator = Rack::PrxAuth::AuthValidator.new(prx_jwt)
107
+
108
+ # try to refresh auth session on GET requests
109
+ if request.get? && validator.time_to_live < PRX_JWT_REFRESH_TTL
110
+ raise SessionTokenExpiredError.new
111
+ end
112
+
113
+ # create new data/token from access claims
114
+ token_data = Rack::PrxAuth::TokenData.new(validator.claims)
115
+ PrxAuth::Rails::Token.new(token_data)
92
116
  end
93
117
  end
94
118
  end
@@ -1,5 +1,5 @@
1
1
  module PrxAuth
2
2
  module Rails
3
- VERSION = "1.7.0"
3
+ VERSION = "1.8.0"
4
4
  end
5
5
  end
@@ -8,10 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = PrxAuth::Rails::VERSION
9
9
  spec.authors = ["Chris Rhoden"]
10
10
  spec.email = ["carhoden@gmail.com"]
11
- spec.description = %q{Rails integration for next generation PRX Authorization system.
12
- }
13
- spec.summary = %q{Rails integration for next generation PRX Authorization system.
14
- }
11
+ spec.description = "Rails integration for next generation PRX Authorization system."
12
+ spec.summary = "Rails integration for next generation PRX Authorization system."
15
13
  spec.homepage = "https://github.com/PRX/prx_auth-rails"
16
14
  spec.license = "MIT"
17
15
 
@@ -33,7 +31,5 @@ Gem::Specification.new do |spec|
33
31
  spec.add_development_dependency 'pry'
34
32
  spec.add_development_dependency 'sqlite3'
35
33
 
36
-
37
-
38
- spec.add_runtime_dependency 'prx_auth', "~> 1.2"
34
+ spec.add_runtime_dependency 'prx_auth', ">= 1.7.0"
39
35
  end
@@ -2,6 +2,8 @@ class ApplicationController < ActionController::Base
2
2
 
3
3
  before_action :authenticate!
4
4
 
5
+ def index; end
6
+
5
7
  def after_sign_in_path_for(_resource)
6
8
  '/after-sign-in-path'
7
9
  end
@@ -0,0 +1 @@
1
+ the controller index!
@@ -1,3 +1,5 @@
1
1
  Rails.application.routes.draw do
2
+ get 'index', to: 'application#index'
3
+ put 'index', to: 'application#index'
2
4
  mount PrxAuth::Rails::Engine => "/prx_auth-rails"
3
5
  end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ module PrxAuth::Rails::Ext
4
+ class ControllerTest < ActionController::TestCase
5
+
6
+ setup do
7
+ @controller = ApplicationController.new
8
+ @jwt_session_key = ApplicationController::PRX_JWT_SESSION_KEY
9
+ @stub_claims = {'iat' => Time.now.to_i, 'exp' => Time.now.to_i + 3600}
10
+ end
11
+
12
+ test 'redirects unless you are authenticated' do
13
+ get :index
14
+ assert_equal response.code, '302'
15
+ assert response.headers['Location'].ends_with?('/sessions/new')
16
+ end
17
+
18
+ test 'uses a valid session token' do
19
+ session[@jwt_session_key] = 'some-jwt'
20
+ JSON::JWT.stub(:decode, @stub_claims) do
21
+ get :index
22
+ assert_equal response.code, '200'
23
+ assert response.body.include?('the controller index!')
24
+ assert @controller.current_user.is_a?(PrxAuth::Rails::Token)
25
+ end
26
+ end
27
+
28
+ test 'redirects if your token is nearing expiration' do
29
+ session[@jwt_session_key] = 'some-jwt'
30
+ @stub_claims['exp'] = Time.now.to_i + 10
31
+ JSON::JWT.stub(:decode, @stub_claims) do
32
+ get :index
33
+ assert_equal response.code, '302'
34
+ assert response.headers['Location'].ends_with?('/sessions/new')
35
+ end
36
+ end
37
+
38
+ test 'does not redirect if your token has expired on a non-GET request' do
39
+ session[@jwt_session_key] = 'some-jwt'
40
+ @stub_claims['exp'] = Time.now.to_i + 10
41
+ JSON::JWT.stub(:decode, @stub_claims) do
42
+ put :index
43
+ assert_equal response.code, '200'
44
+ assert response.body.include?('the controller index!')
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -6,8 +6,10 @@ module PrxAuth::Rails
6
6
  setup do
7
7
  @routes = PrxAuth::Rails::Engine.routes
8
8
  @nonce_session_key = SessionsController::ID_NONCE_SESSION_KEY
9
- @token_params = {id_token: 'sometok', access_token: 'othertok'}
9
+ @refresh_back_key = SessionsController::PRX_REFRESH_BACK_KEY
10
+ @token_params = {id_token: 'idtok', access_token: 'accesstok'}
10
11
  @stub_claims = {'nonce' => '123', 'sub' => '1'}
12
+ @stub_token = PrxAuth::Rails::Token.new(Rack::PrxAuth::TokenData.new())
11
13
  end
12
14
 
13
15
  test "new creates nonce" do
@@ -31,11 +33,12 @@ module PrxAuth::Rails
31
33
  end
32
34
 
33
35
  test 'create should validate a token and set the session variable' do
36
+ session[SessionsController::PRX_JWT_SESSION_KEY] = nil
34
37
  @controller.stub(:validate_token, @stub_claims) do
35
- @controller.stub(:lookup_and_register_accounts_names, nil) do
38
+ @controller.stub(:session_token, @stub_token) do
36
39
  session[@nonce_session_key] = '123'
37
40
  post :create, params: @token_params, format: :json
38
- assert session['prx.auth']['id_token']['nonce'] == '123'
41
+ assert session[SessionsController::PRX_JWT_SESSION_KEY] == 'accesstok'
39
42
  end
40
43
  end
41
44
  end
@@ -50,7 +53,7 @@ module PrxAuth::Rails
50
53
 
51
54
  test 'create should reset the nonce after consumed' do
52
55
  @controller.stub(:validate_token, @stub_claims) do
53
- @controller.stub(:lookup_and_register_accounts_names, nil) do
56
+ @controller.stub(:session_token, @stub_token) do
54
57
  session[@nonce_session_key] = '123'
55
58
  post :create, params: @token_params, format: :json
56
59
 
@@ -61,6 +64,20 @@ module PrxAuth::Rails
61
64
  end
62
65
  end
63
66
 
67
+ test 'redirects to a back-path after refresh' do
68
+ @controller.stub(:validate_token, @stub_claims) do
69
+ @controller.stub(:session_token, @stub_token) do
70
+ session[@nonce_session_key] = '123'
71
+ session[@refresh_back_key] = '/lets/go/here?okay'
72
+ post :create, params: @token_params, format: :json
73
+
74
+ assert session[@refresh_back_key] == nil
75
+ assert response.code == '302'
76
+ assert response.headers['Location'].ends_with?('/lets/go/here?okay')
77
+ end
78
+ end
79
+ end
80
+
64
81
  test 'should respond with redirect to the auth error page / code if the nonce does not match' do
65
82
  @controller.stub(:validate_token, @stub_claims) do
66
83
  session[@nonce_session_key] = 'nonce-does-not-match'
@@ -96,9 +113,9 @@ module PrxAuth::Rails
96
113
  end
97
114
 
98
115
  test 'should clear the user token on sign out' do
99
- session[PrxAuth::Rails::Controller::PRX_TOKEN_SESSION_KEY] = 'some-token'
116
+ session[SessionsController::PRX_JWT_SESSION_KEY] = 'some-token'
100
117
  post :destroy
101
- assert session[PrxAuth::Rails::Controller::PRX_TOKEN_SESSION_KEY] == nil
118
+ assert session[SessionsController::PRX_JWT_SESSION_KEY] == nil
102
119
  end
103
120
  end
104
121
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prx_auth-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Rhoden
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-01 00:00:00.000000000 Z
11
+ date: 2021-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -140,19 +140,17 @@ dependencies:
140
140
  name: prx_auth
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - "~>"
143
+ - - ">="
144
144
  - !ruby/object:Gem::Version
145
- version: '1.2'
145
+ version: 1.7.0
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - "~>"
150
+ - - ">="
151
151
  - !ruby/object:Gem::Version
152
- version: '1.2'
153
- description: 'Rails integration for next generation PRX Authorization system.
154
-
155
- '
152
+ version: 1.7.0
153
+ description: Rails integration for next generation PRX Authorization system.
156
154
  email:
157
155
  - carhoden@gmail.com
158
156
  executables: []
@@ -196,6 +194,7 @@ files:
196
194
  - test/dummy/app/mailers/application_mailer.rb
197
195
  - test/dummy/app/models/application_record.rb
198
196
  - test/dummy/app/models/concerns/.keep
197
+ - test/dummy/app/views/application/index.html.erb
199
198
  - test/dummy/app/views/layouts/application.html.erb
200
199
  - test/dummy/app/views/layouts/mailer.html.erb
201
200
  - test/dummy/app/views/layouts/mailer.text.erb
@@ -239,6 +238,7 @@ files:
239
238
  - test/dummy/storage/.keep
240
239
  - test/log/development.log
241
240
  - test/prx_auth/rails/configuration_test.rb
241
+ - test/prx_auth/rails/ext/controller_test.rb
242
242
  - test/prx_auth/rails/sessions_controller_test.rb
243
243
  - test/prx_auth/rails/token_test.rb
244
244
  - test/test_helper.rb
@@ -246,7 +246,7 @@ homepage: https://github.com/PRX/prx_auth-rails
246
246
  licenses:
247
247
  - MIT
248
248
  metadata: {}
249
- post_install_message:
249
+ post_install_message:
250
250
  rdoc_options: []
251
251
  require_paths:
252
252
  - lib
@@ -261,9 +261,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
261
  - !ruby/object:Gem::Version
262
262
  version: '0'
263
263
  requirements: []
264
- rubyforge_project:
265
- rubygems_version: 2.7.6.2
266
- signing_key:
264
+ rubygems_version: 3.0.3
265
+ signing_key:
267
266
  specification_version: 4
268
267
  summary: Rails integration for next generation PRX Authorization system.
269
268
  test_files:
@@ -281,6 +280,7 @@ test_files:
281
280
  - test/dummy/app/mailers/application_mailer.rb
282
281
  - test/dummy/app/models/application_record.rb
283
282
  - test/dummy/app/models/concerns/.keep
283
+ - test/dummy/app/views/application/index.html.erb
284
284
  - test/dummy/app/views/layouts/application.html.erb
285
285
  - test/dummy/app/views/layouts/mailer.html.erb
286
286
  - test/dummy/app/views/layouts/mailer.text.erb
@@ -324,6 +324,7 @@ test_files:
324
324
  - test/dummy/storage/.keep
325
325
  - test/log/development.log
326
326
  - test/prx_auth/rails/configuration_test.rb
327
+ - test/prx_auth/rails/ext/controller_test.rb
327
328
  - test/prx_auth/rails/sessions_controller_test.rb
328
329
  - test/prx_auth/rails/token_test.rb
329
330
  - test/test_helper.rb