lobby_boy 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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +196 -0
- data/Rakefile +40 -0
- data/app/controllers/lobby_boy/session_controller.rb +93 -0
- data/app/helpers/lobby_boy/session_helper.rb +52 -0
- data/app/views/lobby_boy/_iframes.html.erb +7 -0
- data/app/views/lobby_boy/session/check.html.erb +171 -0
- data/config/initializers/omniauth.rb +4 -0
- data/config/routes.rb +6 -0
- data/lib/lobby_boy.rb +6 -0
- data/lib/lobby_boy/configuration.rb +80 -0
- data/lib/lobby_boy/engine.rb +31 -0
- data/lib/lobby_boy/omni_auth/failure_endpoint.rb +110 -0
- data/lib/lobby_boy/openid_connect/id_token.rb +39 -0
- data/lib/lobby_boy/patches/session_management.rb +45 -0
- data/lib/lobby_boy/util/uri.rb +25 -0
- data/lib/lobby_boy/version.rb +3 -0
- data/lib/tasks/lobby_boy_tasks.rake +4 -0
- metadata +118 -0
data/config/routes.rb
ADDED
data/lib/lobby_boy.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module LobbyBoy
|
2
|
+
module Configuration
|
3
|
+
module HashInit
|
4
|
+
def initialize(attr)
|
5
|
+
attr.each do |name, value|
|
6
|
+
instance_variable_set "@#{name}", value
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Client
|
12
|
+
attr_reader :host, :cookie_domain,
|
13
|
+
:logged_in, :end_session_endpoint,
|
14
|
+
:refresh_offset, :refresh_interval,
|
15
|
+
:on_login_js_partial, :on_logout_js_partial
|
16
|
+
|
17
|
+
include HashInit
|
18
|
+
end
|
19
|
+
|
20
|
+
class Provider
|
21
|
+
attr_reader :name,
|
22
|
+
:client_id,
|
23
|
+
:issuer,
|
24
|
+
:end_session_endpoint,
|
25
|
+
:check_session_iframe
|
26
|
+
|
27
|
+
include HashInit
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_url(host, path)
|
31
|
+
if path =~ /^https?:\/\//
|
32
|
+
path
|
33
|
+
else
|
34
|
+
URI.join(host, path).to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def host_name(host_address)
|
39
|
+
URI.parse(host_address).host
|
40
|
+
end
|
41
|
+
|
42
|
+
def configure_client!(options)
|
43
|
+
opts = options.dup
|
44
|
+
|
45
|
+
opts[:end_session_endpoint] = build_url opts[:host], opts[:end_session_endpoint]
|
46
|
+
opts[:cookie_domain] ||=
|
47
|
+
if opts[:host].is_a? Symbol # e.g. :all to allow all domains
|
48
|
+
opts[:host]
|
49
|
+
else
|
50
|
+
host_name opts[:host]
|
51
|
+
end
|
52
|
+
opts[:refresh_offset] ||= 60.seconds
|
53
|
+
opts[:refresh_interval] ||= 30.seconds
|
54
|
+
opts[:logged_in] ||= lambda { false }
|
55
|
+
|
56
|
+
@client = Client.new opts
|
57
|
+
end
|
58
|
+
|
59
|
+
def configure_provider!(options)
|
60
|
+
opts = options.dup
|
61
|
+
|
62
|
+
opts[:end_session_endpoint] = build_url opts[:issuer], opts[:end_session_endpoint]
|
63
|
+
opts[:check_session_iframe] = build_url opts[:issuer], opts[:check_session_iframe]
|
64
|
+
|
65
|
+
@provider = Provider.new opts
|
66
|
+
end
|
67
|
+
|
68
|
+
def client
|
69
|
+
@client
|
70
|
+
end
|
71
|
+
|
72
|
+
def provider
|
73
|
+
@provider
|
74
|
+
end
|
75
|
+
|
76
|
+
def configured?
|
77
|
+
client && provider
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module LobbyBoy
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace LobbyBoy
|
4
|
+
|
5
|
+
config.assets.precompile += %w( jquery.js js.cookie-1.5.1.min.js )
|
6
|
+
|
7
|
+
config.to_prepare do
|
8
|
+
require 'lobby_boy/patches/session_management'
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Use CORS to allow AJAX re-reauthentication.
|
13
|
+
initializer 'lobby_boy.add_middleware' do |app|
|
14
|
+
require 'rack/cors'
|
15
|
+
|
16
|
+
app.middleware.insert_before 0, 'Rack::Cors' do
|
17
|
+
allow do
|
18
|
+
omniauth = ::OmniAuth.config.path_prefix
|
19
|
+
|
20
|
+
origins '*'
|
21
|
+
resource "#{omniauth}/*", headers: :any, methods: [:get], credentials: true
|
22
|
+
end
|
23
|
+
|
24
|
+
allow do
|
25
|
+
origins '*'
|
26
|
+
resource '/session/*', headers: :any, methods: [:get], credentials: true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module LobbyBoy
|
2
|
+
module OmniAuth
|
3
|
+
# This is a copy of the default OmniAuth FailureEndpoint
|
4
|
+
# minus the raise_out! we don't want and plus the actual error message
|
5
|
+
# as opposed to always just 'missing_code'.
|
6
|
+
#
|
7
|
+
# Also when authentication with prompt=none fails it will retry with prompt=login.
|
8
|
+
class FailureEndpoint
|
9
|
+
attr_reader :env
|
10
|
+
|
11
|
+
def self.call(env)
|
12
|
+
new(env).call
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(env)
|
16
|
+
@env = env
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
if script?
|
21
|
+
redirect_to '/session/state?state=unauthenticated'
|
22
|
+
elsif retry?
|
23
|
+
retry_interactive
|
24
|
+
else
|
25
|
+
redirect_to_failure
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def params
|
30
|
+
request.params
|
31
|
+
end
|
32
|
+
|
33
|
+
def request
|
34
|
+
@request ||= ::Rack::Request.new env
|
35
|
+
end
|
36
|
+
|
37
|
+
def error
|
38
|
+
env['omniauth.error'] || ::OmniAuth::Error.new(env['omniauth.error.type'])
|
39
|
+
end
|
40
|
+
|
41
|
+
def script?
|
42
|
+
origin =~ /#{script_name}\/session\/state/
|
43
|
+
end
|
44
|
+
|
45
|
+
def retry?
|
46
|
+
error.message == 'interaction_required'
|
47
|
+
end
|
48
|
+
|
49
|
+
def retry_interactive
|
50
|
+
url = "#{omniauth_path}/#{strategy.name}?#{origin_query_param}&prompt=login"
|
51
|
+
|
52
|
+
redirect_to url
|
53
|
+
end
|
54
|
+
|
55
|
+
def redirect_to_failure
|
56
|
+
params = [
|
57
|
+
message_query_param(error.message),
|
58
|
+
origin_query_param,
|
59
|
+
strategy_name_query_param
|
60
|
+
]
|
61
|
+
|
62
|
+
url = omniauth_path + '/failure?' + params.compact.join('&')
|
63
|
+
|
64
|
+
redirect_to url
|
65
|
+
end
|
66
|
+
|
67
|
+
def omniauth_path
|
68
|
+
script_name + ::OmniAuth.config.path_prefix
|
69
|
+
end
|
70
|
+
|
71
|
+
def script_name
|
72
|
+
env['SCRIPT_NAME']
|
73
|
+
end
|
74
|
+
|
75
|
+
def message_query_param(message)
|
76
|
+
'message=' + escape(message)
|
77
|
+
end
|
78
|
+
|
79
|
+
def strategy_name_query_param
|
80
|
+
'strategy=' + escape(strategy.name) if strategy
|
81
|
+
end
|
82
|
+
|
83
|
+
def strategy
|
84
|
+
env['omniauth.error.strategy']
|
85
|
+
end
|
86
|
+
|
87
|
+
def origin
|
88
|
+
env['omniauth.origin']
|
89
|
+
end
|
90
|
+
|
91
|
+
def origin_query_param
|
92
|
+
'origin=' + escape(origin) if origin
|
93
|
+
end
|
94
|
+
|
95
|
+
module Functions
|
96
|
+
module_function
|
97
|
+
|
98
|
+
def redirect_to(url)
|
99
|
+
Rack::Response.new(['302 Moved'], 302, 'Location' => url.to_s).finish
|
100
|
+
end
|
101
|
+
|
102
|
+
def escape(string)
|
103
|
+
Rack::Utils.escape(string)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
include Functions
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module LobbyBoy
|
2
|
+
module OpenIDConnect
|
3
|
+
##
|
4
|
+
# Wraps a OpenIDConnect::ResponseObject::IdToken providing some useful
|
5
|
+
# methods for it.
|
6
|
+
class IdToken < SimpleDelegator
|
7
|
+
attr_accessor :jwt_token
|
8
|
+
|
9
|
+
##
|
10
|
+
# Creates a new IdToken by decoding the given JWT token using the given public key.
|
11
|
+
#
|
12
|
+
# @param jwt_token [String] The JWT token received from the OpenID Connect provider.
|
13
|
+
# @param public_key [String] Public key or secret. Only required for signed tokens.
|
14
|
+
def initialize(jwt_token, public_key = nil)
|
15
|
+
@jwt_token = jwt_token
|
16
|
+
id_token = ::OpenIDConnect::ResponseObject::IdToken.decode jwt_token, public_key
|
17
|
+
super id_token
|
18
|
+
end
|
19
|
+
|
20
|
+
def expires_at
|
21
|
+
datetime_from_seconds __getobj__.exp
|
22
|
+
end
|
23
|
+
|
24
|
+
def issued_at
|
25
|
+
datetime_from_seconds __getobj__.iat
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Number of seconds left until this ID token expires.
|
30
|
+
def expires_in
|
31
|
+
[0, __getobj__.exp - Time.now.to_i].max
|
32
|
+
end
|
33
|
+
|
34
|
+
def datetime_from_seconds(seconds)
|
35
|
+
DateTime.strptime seconds.to_s, '%s'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'omniauth/strategies/openid_connect'
|
2
|
+
require 'lobby_boy/openid_connect/id_token'
|
3
|
+
require 'lobby_boy/util/uri'
|
4
|
+
|
5
|
+
module SessionManagement
|
6
|
+
##
|
7
|
+
# Always append 'prompt=none' to every authorization request to make the login
|
8
|
+
# automatic if possible.
|
9
|
+
def authorize_uri
|
10
|
+
LobbyBoy::Util::URI.add_query_params super,
|
11
|
+
prompt: request.params['prompt'] || 'none',
|
12
|
+
id_token_hint: request.params['id_token_hint']
|
13
|
+
end
|
14
|
+
|
15
|
+
def access_token
|
16
|
+
return super unless LobbyBoy.configured?
|
17
|
+
|
18
|
+
at = super
|
19
|
+
|
20
|
+
@id_token ||= begin
|
21
|
+
session_state = request.params['session_state']
|
22
|
+
id_token = ::LobbyBoy::OpenIDConnect::IdToken.new at.id_token
|
23
|
+
env['lobby_boy.id_token'] = id_token
|
24
|
+
|
25
|
+
if session_state
|
26
|
+
cookie = {
|
27
|
+
state: session_state,
|
28
|
+
expires_at: id_token.exp
|
29
|
+
}
|
30
|
+
|
31
|
+
env['lobby_boy.cookie'] = {
|
32
|
+
value: cookie.to_json,
|
33
|
+
expires: id_token.expires_in.seconds.from_now,
|
34
|
+
domain: LobbyBoy.client.cookie_domain
|
35
|
+
}
|
36
|
+
|
37
|
+
id_token
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
at
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
OmniAuth::Strategies::OpenIDConnect.prepend SessionManagement
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module LobbyBoy
|
4
|
+
module Util
|
5
|
+
module URI
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def add_query_params(url, additional_params = {})
|
9
|
+
return nil if url.nil?
|
10
|
+
|
11
|
+
uri = ::URI.parse url.to_s
|
12
|
+
params = ::URI.decode_www_form(uri.query || '')
|
13
|
+
|
14
|
+
additional_params.each do |name, value|
|
15
|
+
if value
|
16
|
+
params << [name, value]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
uri.query = ::URI.encode_www_form params
|
21
|
+
uri.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lobby_boy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Finn GmbH
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.21
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.21
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-cors
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.4.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.4.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: omniauth
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: omniauth-openid-connect
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.2'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- info@finn.de
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- LICENSE
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- app/controllers/lobby_boy/session_controller.rb
|
80
|
+
- app/helpers/lobby_boy/session_helper.rb
|
81
|
+
- app/views/lobby_boy/_iframes.html.erb
|
82
|
+
- app/views/lobby_boy/session/check.html.erb
|
83
|
+
- config/initializers/omniauth.rb
|
84
|
+
- config/routes.rb
|
85
|
+
- lib/lobby_boy.rb
|
86
|
+
- lib/lobby_boy/configuration.rb
|
87
|
+
- lib/lobby_boy/engine.rb
|
88
|
+
- lib/lobby_boy/omni_auth/failure_endpoint.rb
|
89
|
+
- lib/lobby_boy/openid_connect/id_token.rb
|
90
|
+
- lib/lobby_boy/patches/session_management.rb
|
91
|
+
- lib/lobby_boy/util/uri.rb
|
92
|
+
- lib/lobby_boy/version.rb
|
93
|
+
- lib/tasks/lobby_boy_tasks.rake
|
94
|
+
homepage: https://github.com/finnlabs/lobby_boy
|
95
|
+
licenses:
|
96
|
+
- GPLv3
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.4.3
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Rails engine for OpenIDConnect Session Management
|
118
|
+
test_files: []
|