prx_auth-rails 1.7.0 → 1.8.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
  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