lobby_boy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ require 'omniauth'
2
+ require 'lobby_boy/omni_auth/failure_endpoint'
3
+
4
+ OmniAuth.config.on_failure = LobbyBoy::OmniAuth::FailureEndpoint
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ LobbyBoy::Engine.routes.draw do
2
+ get 'session/check'
3
+ get 'session/state'
4
+ get 'session/end'
5
+ get 'session/refresh'
6
+ end
data/lib/lobby_boy.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "lobby_boy/engine"
2
+ require "lobby_boy/configuration"
3
+
4
+ module LobbyBoy
5
+ extend Configuration
6
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module LobbyBoy
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :lobby_boy do
3
+ # # Task goes here
4
+ # 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: []