Skip to content

Commit

Permalink
Add logout phase
Browse files Browse the repository at this point in the history
  • Loading branch information
yannvery committed Aug 24, 2018
1 parent 23495e0 commit 1c2d23a
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 6 deletions.
45 changes: 40 additions & 5 deletions lib/omniauth/strategies/openid_connect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class OpenIDConnect
authorization_endpoint: '/authorize',
token_endpoint: '/token',
userinfo_endpoint: '/userinfo',
jwks_uri: '/jwk'
jwks_uri: '/jwk',
end_session_endpoint: nil
}
option :issuer
option :discovery, false
Expand All @@ -42,6 +43,7 @@ class OpenIDConnect
option :send_nonce, true
option :send_scope_to_token_endpoint, true
option :client_auth_method
option :post_logout_redirect_uri

uid { user_info.sub }

Expand Down Expand Up @@ -82,8 +84,7 @@ def config
end

def request_phase
options.issuer = issuer if options.issuer.blank?
discover! if options.discovery
discover!
redirect authorize_uri
end

Expand All @@ -96,8 +97,7 @@ def callback_phase
elsif !request.params['code']
return fail!(:missing_code, OmniAuth::OpenIDConnect::MissingCodeError.new(request.params['error']))
else
options.issuer = issuer if options.issuer.blank?
discover! if options.discovery
discover!
client.redirect_uri = redirect_uri
client.authorization_code = authorization_code
access_token
Expand All @@ -111,10 +111,26 @@ def callback_phase
fail!(:failed_to_connect, e)
end

def other_phase
discover!
if logout_path_pattern.match(current_path) && end_session_uri
redirect end_session_uri
else
call_app!
end
end

def authorization_code
request.params['code']
end

def end_session_uri
return unless end_session_endpoint_is_valid?
end_session_uri = URI(client_options.end_session_endpoint)
end_session_uri.query = encoded_post_logout_redirect_uri
end_session_uri.to_s
end

def authorize_uri
client.redirect_uri = redirect_uri
opts = {
Expand Down Expand Up @@ -143,10 +159,13 @@ def issuer
end

def discover!
return unless options.discovery
options.issuer = issuer if options.issuer.blank?
client_options.authorization_endpoint = config.authorization_endpoint
client_options.token_endpoint = config.token_endpoint
client_options.userinfo_endpoint = config.userinfo_endpoint
client_options.jwks_uri = config.jwks_uri
client_options.end_session_endpoint = config.try(:end_session_endpoint)
end

def user_info
Expand Down Expand Up @@ -235,6 +254,22 @@ def redirect_uri
"#{ client_options.redirect_uri }?redirect_uri=#{ CGI.escape(request.params['redirect_uri']) }"
end

def encoded_post_logout_redirect_uri
return unless options.post_logout_redirect_uri
URI.encode_www_form(
post_logout_redirect_uri: options.post_logout_redirect_uri
)
end

def end_session_endpoint_is_valid?
client_options.end_session_endpoint &&
client_options.end_session_endpoint =~ URI::DEFAULT_PARSER.make_regexp
end

def logout_path_pattern
@logout_path_pattern ||= %r{\A#{Regexp.quote(request_path)}(/logout)}
end

class CallbackError < StandardError
attr_accessor :error, :error_reason, :error_uri

Expand Down
2 changes: 1 addition & 1 deletion omniauth_openid_connect.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']

spec.add_dependency 'omniauth', '~> 1.3'
spec.add_dependency 'openid_connect', '~> 0.12.0'
spec.add_dependency 'openid_connect', '~> 1.1.6'
spec.add_dependency 'addressable', '~> 2.5'
spec.add_development_dependency 'bundler', '~> 1.5'
spec.add_development_dependency 'minitest', '~> 5.1'
Expand Down
58 changes: 58 additions & 0 deletions test/lib/omniauth/strategies/openid_connect_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,63 @@ def test_request_phase
strategy.request_phase
end

def test_logout_phase_with_discovery
expected_redirect = %r{^https:\/\/example\.com\/logout$}
strategy.options.client_options.host = 'example.com'
strategy.options.discovery = true

issuer = stub('OpenIDConnect::Discovery::Issuer')
issuer.stubs(:issuer).returns('https://example.com/')
::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer)

config = stub('OpenIDConnect::Discovery::Provder::Config')
config.stubs(:authorization_endpoint).returns('https://example.com/authorization')
config.stubs(:token_endpoint).returns('https://example.com/token')
config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
config.stubs(:jwks_uri).returns('https://example.com/jwks')
config.stubs(:end_session_endpoint).returns('https://example.com/logout')
::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)

request.stubs(:path_info).returns('/auth/openidconnect/logout')

strategy.expects(:redirect).with(regexp_matches(expected_redirect))
strategy.other_phase
end

def test_logout_phase_with_discovery_and_post_logout_redirect_uri
expected_redirect = 'https://example.com/logout?post_logout_redirect_uri=https%3A%2F%2Fmysite.com'
strategy.options.client_options.host = 'example.com'
strategy.options.discovery = true
strategy.options.post_logout_redirect_uri = 'https://mysite.com'

issuer = stub('OpenIDConnect::Discovery::Issuer')
issuer.stubs(:issuer).returns('https://example.com/')
::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer)

config = stub('OpenIDConnect::Discovery::Provder::Config')
config.stubs(:authorization_endpoint).returns('https://example.com/authorization')
config.stubs(:token_endpoint).returns('https://example.com/token')
config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
config.stubs(:jwks_uri).returns('https://example.com/jwks')
config.stubs(:end_session_endpoint).returns('https://example.com/logout')
::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)

request.stubs(:path_info).returns('/auth/openidconnect/logout')

strategy.expects(:redirect).with(expected_redirect)
strategy.other_phase
end

def test_logout_phase
strategy.options.issuer = 'example.com'
strategy.options.client_options.host = 'example.com'

request.stubs(:path_info).returns('/auth/openidconnect/logout')

strategy.expects(:call_app!)
strategy.other_phase
end

def test_request_phase_with_discovery
expected_redirect = /^https:\/\/example\.com\/authorization\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$/
strategy.options.client_options.host = 'example.com'
Expand All @@ -42,6 +99,7 @@ def test_request_phase_with_discovery
assert_equal strategy.options.client_options.token_endpoint, 'https://example.com/token'
assert_equal strategy.options.client_options.userinfo_endpoint, 'https://example.com/userinfo'
assert_equal strategy.options.client_options.jwks_uri, 'https://example.com/jwks'
assert_nil strategy.options.client_options.end_session_endpoint
end

def test_uid
Expand Down

0 comments on commit 1c2d23a

Please sign in to comment.