prx_auth-rails 0.1.0 → 1.3.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 +5 -5
- data/.gitignore +6 -0
- data/Guardfile +8 -0
- data/README.md +44 -2
- data/Rakefile +17 -0
- data/app/controllers/prx_auth/rails/sessions_controller.rb +108 -0
- data/app/views/prx_auth/rails/sessions/auth_error.html.erb +15 -0
- data/app/views/prx_auth/rails/sessions/show.html.erb +38 -0
- data/config/routes.rb +7 -0
- data/lib/prx_auth/rails.rb +10 -2
- data/lib/prx_auth/rails/configuration.rb +28 -0
- data/lib/prx_auth/rails/engine.rb +5 -0
- data/lib/prx_auth/rails/ext/controller.rb +32 -1
- data/lib/prx_auth/rails/railtie.rb +2 -2
- data/lib/prx_auth/rails/token.rb +35 -0
- data/lib/prx_auth/rails/version.rb +1 -1
- data/prx_auth-rails.gemspec +13 -3
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +2 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +8 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/javascript/packs/application.js +15 -0
- data/test/dummy/app/jobs/application_job.rb +7 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/rails +5 -0
- data/test/dummy/bin/rake +5 -0
- data/test/dummy/bin/setup +33 -0
- data/test/dummy/bin/spring +10 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +76 -0
- data/test/dummy/config/environments/production.rb +120 -0
- data/test/dummy/config/environments/test.rb +60 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +12 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +8 -0
- data/test/dummy/config/initializers/content_security_policy.rb +28 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/permissions_policy.rb +11 -0
- data/test/dummy/config/initializers/prx_auth.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +43 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/storage/.keep +0 -0
- data/test/log/development.log +0 -0
- data/test/prx_auth/rails/configuration_test.rb +36 -0
- data/test/prx_auth/rails/sessions_controller_test.rb +94 -0
- data/test/prx_auth/rails/token_test.rb +45 -0
- data/test/test_helper.rb +35 -0
- metadata +243 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3405e4e72d2c39585111bef4fca339a73ce11ec86a2c288f5c82d8934c0620fe
|
4
|
+
data.tar.gz: 3450063f4c4fdae2464f755222fafee08c627eda3aa8fee1d267db1474db58de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edf33f5cf4bc105818bb069554506f0b2d9ef5b8bb18cd1602b2ceb6384991804b5b3268c6bd5c519c08e8503df51c132a9bec045f13995c1d55088194fff489
|
7
|
+
data.tar.gz: 4aa62706ee892c2335756e2c3ae61171b77acc69bfe0ec06433449726f8e884cee4af2031e55e418c6780e9aedc8bfb1c78359c072c089389422a2d4aa15c95b
|
data/.gitignore
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
guard :minitest, all_after_pass: true do
|
2
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb})
|
3
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
4
|
+
watch(%r{^lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
|
5
|
+
watch(%r{^lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
|
6
|
+
watch(%r{^test/.+_test\.rb})
|
7
|
+
watch(%r{^test/test_helper\.rb}) { 'test' }
|
8
|
+
end
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# PrxAuth::Rails
|
2
2
|
|
3
|
-
Rails integration for next generation PRX Authorization system.
|
3
|
+
Rails integration for next generation PRX Authorization system. This
|
4
|
+
provides common OpenId authorization patterns used in PRX apps.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -14,7 +15,48 @@ And then execute:
|
|
14
15
|
|
15
16
|
## Usage
|
16
17
|
|
17
|
-
|
18
|
+
Installing the gem in a Rails project will automatically add the
|
19
|
+
appropriate Rack middleware to your Rails application and add two
|
20
|
+
methods to your controllers. These methods are:
|
21
|
+
|
22
|
+
* `prx_auth_token`: returns a token (similar to PrxAuth::Token) which
|
23
|
+
automatically namespaces queries. The main methods you will be
|
24
|
+
interested in are `authorized?`, `globally_authorized?` and `resources`.
|
25
|
+
More information can be found in PrxAuth.
|
26
|
+
|
27
|
+
* `prx_authenticated?`: returns whether or not this request includes a
|
28
|
+
valid PrxAuth token.
|
29
|
+
|
30
|
+
This will let set up the Rails app to be ready for HTTP requests
|
31
|
+
associated with an OpenId access token.
|
32
|
+
|
33
|
+
### Configuration
|
34
|
+
|
35
|
+
Generally, configuration is not required and the gem aims for great
|
36
|
+
defaults, but you can override some settings if you need to change the
|
37
|
+
default behavior.
|
38
|
+
|
39
|
+
If you're using the Rails server-side session flow, you must supply the
|
40
|
+
client_id via configuration.
|
41
|
+
|
42
|
+
In your rails app, add a file to config/initializers called
|
43
|
+
`prx_auth.rb`:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
PrxAuth::Rails.configure do |config|
|
47
|
+
|
48
|
+
# enables automatic installation of token parser middleware
|
49
|
+
config.install_middleware = false # default: true
|
50
|
+
|
51
|
+
# automatically adds namespace to all scoped queries, e.g. .authorized?(:foo) will be treated
|
52
|
+
# as .authorized?(:my_great_ns, :foo). Has no impact on unscoped queries.
|
53
|
+
config.namespace = :my_great_ns # default: derived from Rails::Application name.
|
54
|
+
# e.g. class Feeder < Rails::Application => :feeder
|
55
|
+
|
56
|
+
# Set up the PRX OpenID client_id if using the backend rails sessions flow.
|
57
|
+
config.client_id = '<some client id>'
|
58
|
+
end
|
59
|
+
```
|
18
60
|
|
19
61
|
## Contributing
|
20
62
|
|
data/Rakefile
CHANGED
@@ -1 +1,18 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
1
8
|
require "bundler/gem_tasks"
|
9
|
+
require 'rake'
|
10
|
+
require "rake/testtask"
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
task default: :test
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module PrxAuth::Rails
|
4
|
+
class SessionsController < ApplicationController
|
5
|
+
include PrxAuth::Rails::Engine.routes.url_helpers
|
6
|
+
|
7
|
+
skip_before_action :authenticate!
|
8
|
+
|
9
|
+
before_action :set_nonce!, only: :show
|
10
|
+
|
11
|
+
ID_NONCE_SESSION_KEY = 'id_prx_openid_nonce'.freeze
|
12
|
+
|
13
|
+
def new
|
14
|
+
set_nonce! unless fetch_nonce.present?
|
15
|
+
|
16
|
+
config = PrxAuth::Rails.configuration
|
17
|
+
|
18
|
+
id_auth_params = {
|
19
|
+
client_id: config.prx_client_id,
|
20
|
+
nonce: fetch_nonce,
|
21
|
+
response_type: 'id_token token',
|
22
|
+
scope: 'openid apps',
|
23
|
+
prompt: 'necessary'
|
24
|
+
}
|
25
|
+
|
26
|
+
redirect_to '//' + config.id_host + '/authorize?' + id_auth_params.to_query
|
27
|
+
end
|
28
|
+
|
29
|
+
def show
|
30
|
+
end
|
31
|
+
|
32
|
+
def auth_error
|
33
|
+
@auth_error_message = params.require(:error)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create
|
37
|
+
jwt_id_claims = id_claims
|
38
|
+
jwt_access_claims = access_claims
|
39
|
+
|
40
|
+
jwt_access_claims['id_token'] = jwt_id_claims.as_json
|
41
|
+
|
42
|
+
result_path = if valid_nonce?(jwt_id_claims['nonce']) &&
|
43
|
+
users_match?(jwt_id_claims, jwt_access_claims)
|
44
|
+
sign_in_user(jwt_access_claims)
|
45
|
+
after_sign_in_path_for(current_user)
|
46
|
+
else
|
47
|
+
auth_error_sessions_path(error: 'verification_failed')
|
48
|
+
end
|
49
|
+
reset_nonce!
|
50
|
+
|
51
|
+
redirect_to result_path
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def after_sign_in_path_for(_)
|
57
|
+
return super if defined?(super)
|
58
|
+
|
59
|
+
"/"
|
60
|
+
end
|
61
|
+
|
62
|
+
def id_claims
|
63
|
+
id_token = params.require('id_token')
|
64
|
+
validate_token(id_token)
|
65
|
+
end
|
66
|
+
|
67
|
+
def access_claims
|
68
|
+
access_token = params.require('access_token')
|
69
|
+
validate_token(access_token)
|
70
|
+
end
|
71
|
+
|
72
|
+
def reset_nonce!
|
73
|
+
session[ID_NONCE_SESSION_KEY] = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_nonce!
|
77
|
+
n = session[ID_NONCE_SESSION_KEY]
|
78
|
+
return n if n.present?
|
79
|
+
|
80
|
+
session[ID_NONCE_SESSION_KEY] = SecureRandom.hex
|
81
|
+
end
|
82
|
+
|
83
|
+
def fetch_nonce
|
84
|
+
session[ID_NONCE_SESSION_KEY]
|
85
|
+
end
|
86
|
+
|
87
|
+
def valid_nonce?(nonce)
|
88
|
+
return false if fetch_nonce.nil?
|
89
|
+
|
90
|
+
fetch_nonce == nonce
|
91
|
+
end
|
92
|
+
|
93
|
+
def users_match?(claims1, claims2)
|
94
|
+
return false if claims1['sub'].nil? || claims2['sub'].nil?
|
95
|
+
|
96
|
+
claims1['sub'] == claims2['sub']
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate_token(token)
|
100
|
+
id_host = PrxAuth::Rails.configuration.id_host
|
101
|
+
prx_auth_cert = Rack::PrxAuth::Certificate.new("https://#{id_host}/api/v1/certs")
|
102
|
+
auth_validator = Rack::PrxAuth::AuthValidator.new(token, prx_auth_cert, id_host)
|
103
|
+
auth_validator.
|
104
|
+
claims.
|
105
|
+
with_indifferent_access
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class='main'>
|
2
|
+
<section>
|
3
|
+
<h3>Not authorized for this application.</h3>
|
4
|
+
|
5
|
+
<p>Message was: <pre><%= @auth_error_message %></pre>
|
6
|
+
<% if @auth_error_message == 'invalid_scope' %>
|
7
|
+
Did you add a row in the account_applications table on id.prx?
|
8
|
+
<% end %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<p>
|
12
|
+
<a href="<%= new_sessions_path %>">Try logging in again</a>
|
13
|
+
</p>
|
14
|
+
</section>
|
15
|
+
</div>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<div style="display:none;">
|
2
|
+
<%= form_for(:sessions, :url => PrxAuth::Rails::Engine.routes.url_helpers.sessions_path) do |f| %>
|
3
|
+
<%= hidden_field_tag :access_token, '', id: 'access-token-field' %>
|
4
|
+
<%= hidden_field_tag :id_token, '', id: 'id-token-field' %>
|
5
|
+
<%= f.submit id: 'sessions-form-submit' %>
|
6
|
+
<% end %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<script type='application/javascript'>
|
10
|
+
|
11
|
+
function parseURLFragment() {
|
12
|
+
let hashParams = {};
|
13
|
+
let e,
|
14
|
+
a = /\+/g, // Regex for replacing addition symbol with a space
|
15
|
+
r = /([^&;=]+)=?([^&;]*)/g,
|
16
|
+
d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
|
17
|
+
q = window.location.hash.substring(1);
|
18
|
+
|
19
|
+
while (e = r.exec(q))
|
20
|
+
hashParams[d(e[1])] = d(e[2]);
|
21
|
+
|
22
|
+
return hashParams;
|
23
|
+
}
|
24
|
+
|
25
|
+
window.addEventListener("load", () => {
|
26
|
+
var idToken = document.querySelector("#id-token-field");
|
27
|
+
var accessToken = document.querySelector("#access-token-field");
|
28
|
+
var submit = document.querySelector("input#sessions-form-submit[type=submit]");
|
29
|
+
|
30
|
+
var hashParams = parseURLFragment();
|
31
|
+
|
32
|
+
accessToken.value = hashParams['access_token'];
|
33
|
+
idToken.value = hashParams['id_token'];
|
34
|
+
|
35
|
+
submit.click();
|
36
|
+
});
|
37
|
+
|
38
|
+
</script>
|
data/config/routes.rb
ADDED
data/lib/prx_auth/rails.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
require "prx_auth/rails/version"
|
2
|
+
require "prx_auth/rails/configuration"
|
2
3
|
require "prx_auth/rails/railtie" if defined?(Rails)
|
4
|
+
require "prx_auth/rails/engine" if defined?(Rails)
|
5
|
+
|
3
6
|
module PrxAuth
|
4
7
|
module Rails
|
5
8
|
class << self
|
6
|
-
attr_accessor :
|
9
|
+
attr_accessor :configuration
|
10
|
+
|
11
|
+
def configure
|
12
|
+
yield configuration
|
13
|
+
end
|
7
14
|
end
|
8
|
-
|
15
|
+
|
16
|
+
self.configuration = Configuration.new
|
9
17
|
end
|
10
18
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class PrxAuth::Rails::Configuration
|
2
|
+
attr_accessor :install_middleware,
|
3
|
+
:namespace,
|
4
|
+
:prx_client_id,
|
5
|
+
:id_host
|
6
|
+
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@install_middleware = true
|
10
|
+
if defined?(::Rails)
|
11
|
+
klass = ::Rails.application.class
|
12
|
+
parent_name = if ::Rails::VERSION::MAJOR >= 6
|
13
|
+
klass.module_parent_name
|
14
|
+
else
|
15
|
+
klass.parent_name
|
16
|
+
end
|
17
|
+
klass_name = if parent_name.present?
|
18
|
+
parent_name
|
19
|
+
else
|
20
|
+
klass.name
|
21
|
+
end
|
22
|
+
|
23
|
+
@namespace = klass_name.underscore.intern
|
24
|
+
@prx_client_id = nil
|
25
|
+
@id_host = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,13 +1,44 @@
|
|
1
|
+
require 'prx_auth/rails/token'
|
2
|
+
|
1
3
|
module PrxAuth
|
2
4
|
module Rails
|
3
5
|
module Controller
|
4
6
|
def prx_auth_token
|
5
|
-
|
7
|
+
rack_auth_token = env_prx_auth_token
|
8
|
+
return rack_auth_token if rack_auth_token.present?
|
9
|
+
|
10
|
+
session['prx.auth'] && Rack::PrxAuth::TokenData.new(session['prx.auth'])
|
6
11
|
end
|
7
12
|
|
8
13
|
def prx_authenticated?
|
9
14
|
!!prx_auth_token
|
10
15
|
end
|
16
|
+
|
17
|
+
def authenticate!
|
18
|
+
return true if current_user.present?
|
19
|
+
|
20
|
+
redirect_to PrxAuth::Rails::Engine.routes.url_helpers.new_sessions_path
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_user
|
24
|
+
return if prx_auth_token.nil?
|
25
|
+
|
26
|
+
PrxAuth::Rails::Token.new(prx_auth_token)
|
27
|
+
end
|
28
|
+
|
29
|
+
def sign_in_user(token)
|
30
|
+
session['prx.auth'] = token
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def env_prx_auth_token
|
36
|
+
if !defined? @_prx_auth_token
|
37
|
+
@_prx_auth_token = request.env['prx.auth'] && PrxAuth::Rails::Token.new(request.env['prx.auth'])
|
38
|
+
else
|
39
|
+
@_prx_auth_token
|
40
|
+
end
|
41
|
+
end
|
11
42
|
end
|
12
43
|
end
|
13
44
|
end
|
@@ -9,8 +9,8 @@ module PrxAuth::Rails
|
|
9
9
|
end
|
10
10
|
|
11
11
|
initializer 'prx_auth.insert_middleware' do |app|
|
12
|
-
if PrxAuth::Rails.
|
13
|
-
app.config.middleware.
|
12
|
+
if PrxAuth::Rails.configuration.install_middleware
|
13
|
+
app.config.middleware.insert_after Rack::Head, Rack::PrxAuth
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rack/prx_auth'
|
2
|
+
|
3
|
+
class PrxAuth::Rails::Token
|
4
|
+
def initialize(token_data)
|
5
|
+
@token_data = token_data
|
6
|
+
@namespace = PrxAuth::Rails.configuration.namespace
|
7
|
+
end
|
8
|
+
|
9
|
+
def authorized?(resource, namespace=nil, scope=nil)
|
10
|
+
namespace, scope = @namespace, namespace if scope.nil? && !namespace.nil?
|
11
|
+
@token_data.authorized?(resource, namespace, scope)
|
12
|
+
end
|
13
|
+
|
14
|
+
def globally_authorized?(namespace, scope=nil)
|
15
|
+
namespace, scope = @namespace, namespace if scope.nil?
|
16
|
+
@token_data.globally_authorized?(namespace, scope)
|
17
|
+
end
|
18
|
+
|
19
|
+
def resources(namespace=nil, scope=nil)
|
20
|
+
namespace, scope = @namespace, namespace if scope.nil? && !namespace.nil?
|
21
|
+
@token_data.resources(namespace, scope)
|
22
|
+
end
|
23
|
+
|
24
|
+
def scopes
|
25
|
+
@token_data.scopes
|
26
|
+
end
|
27
|
+
|
28
|
+
def user_id
|
29
|
+
@token_data.user_id
|
30
|
+
end
|
31
|
+
|
32
|
+
def authorized_account_ids(scope)
|
33
|
+
@token_data.authorized_account_ids(scope)
|
34
|
+
end
|
35
|
+
end
|