Raise errors on setup failure and logging with OmniAuth::Strategy::log method (#10)
* Raise errors on setup failure * Use URI join and path methods to remove sensivity to final slash in site optionmaster
parent
30b1201646
commit
5e20c44ff6
|
@ -1,44 +1,84 @@
|
||||||
require 'omniauth'
|
require 'omniauth'
|
||||||
require 'omniauth-oauth2'
|
require 'omniauth-oauth2'
|
||||||
require 'json/jwt'
|
require 'json/jwt'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
module OmniAuth
|
module OmniAuth
|
||||||
module Strategies
|
module Strategies
|
||||||
class KeycloakOpenId < OmniAuth::Strategies::OAuth2
|
class KeycloakOpenId < OmniAuth::Strategies::OAuth2
|
||||||
|
|
||||||
|
class Error < RuntimeError; end
|
||||||
|
class ConfigurationError < Error; end
|
||||||
|
class IntegrationError < Error; end
|
||||||
|
|
||||||
attr_reader :authorize_url
|
attr_reader :authorize_url
|
||||||
attr_reader :token_url
|
attr_reader :token_url
|
||||||
attr_reader :cert
|
attr_reader :cert
|
||||||
|
|
||||||
def setup_phase
|
def setup_phase
|
||||||
if @authorize_url.nil? || @token_url.nil?
|
if @authorize_url.nil? || @token_url.nil?
|
||||||
|
prevent_site_option_mistake
|
||||||
|
|
||||||
realm = options.client_options[:realm].nil? ? options.client_id : options.client_options[:realm]
|
realm = options.client_options[:realm].nil? ? options.client_id : options.client_options[:realm]
|
||||||
site = options.client_options[:site]
|
site = options.client_options[:site]
|
||||||
response = Faraday.get "#{options.client_options[:site]}/auth/realms/#{realm}/.well-known/openid-configuration"
|
|
||||||
|
raise_on_failure = options.client_options.fetch(:raise_on_failure, false)
|
||||||
|
|
||||||
|
config_url = URI.join(site, "/auth/realms/#{realm}/.well-known/openid-configuration")
|
||||||
|
|
||||||
|
log :debug, "Going to get Keycloak configuration. URL: #{config_url}"
|
||||||
|
response = Faraday.get config_url
|
||||||
if (response.status == 200)
|
if (response.status == 200)
|
||||||
json = MultiJson.load(response.body)
|
json = MultiJson.load(response.body)
|
||||||
|
|
||||||
@certs_endpoint = json["jwks_uri"]
|
@certs_endpoint = json["jwks_uri"]
|
||||||
@userinfo_endpoint = json["userinfo_endpoint"]
|
@userinfo_endpoint = json["userinfo_endpoint"]
|
||||||
@authorize_url = json["authorization_endpoint"].gsub(site, "")
|
@authorize_url = URI(json["authorization_endpoint"]).path
|
||||||
@token_url = json["token_endpoint"].gsub(site, "")
|
@token_url = URI(json["token_endpoint"]).path
|
||||||
|
|
||||||
|
log_config(json)
|
||||||
|
|
||||||
options.client_options.merge!({
|
options.client_options.merge!({
|
||||||
authorize_url: @authorize_url,
|
authorize_url: @authorize_url,
|
||||||
token_url: @token_url
|
token_url: @token_url
|
||||||
})
|
})
|
||||||
|
log :debug, "Going to get certificates. URL: #{@certs_endpoint}"
|
||||||
certs = Faraday.get @certs_endpoint
|
certs = Faraday.get @certs_endpoint
|
||||||
if (certs.status == 200)
|
if (certs.status == 200)
|
||||||
json = MultiJson.load(certs.body)
|
json = MultiJson.load(certs.body)
|
||||||
@cert = json["keys"][0]
|
@cert = json["keys"][0]
|
||||||
|
log :debug, "Successfully got certificate. Certificate length: #{@cert.length}"
|
||||||
else
|
else
|
||||||
#TODO: Throw Error
|
message = "Coundn't get certificate. URL: #{@certs_endpoint}"
|
||||||
puts "Couldn't get Cert"
|
log :error, message
|
||||||
|
raise IntegrationError, message if raise_on_failure
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
#TODO: Throw Error
|
message = "Keycloak configuration request failed with status: #{response.status}. " \
|
||||||
puts response.status
|
"URL: #{config_url}"
|
||||||
|
log :error, message
|
||||||
|
raise IntegrationError, message if raise_on_failure
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def prevent_site_option_mistake
|
||||||
|
site = options.client_options[:site]
|
||||||
|
return unless site =~ /\/auth$/
|
||||||
|
|
||||||
|
raise ConfigurationError, "Keycloak site parameter should not include /auth part, only domain. Current value: #{site}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_config(config_json)
|
||||||
|
log_keycloak_config = options.client_options.fetch(:log_keycloak_config, false)
|
||||||
|
log :debug, "Successfully got Keycloak config"
|
||||||
|
log :debug, "Keycloak config: #{config_json}" if log_keycloak_config
|
||||||
|
log :debug, "Certs endpoint: #{@certs_endpoint}"
|
||||||
|
log :debug, "Userinfo endpoint: #{@userinfo_endpoint}"
|
||||||
|
log :debug, "Authorize url: #{@authorize_url}"
|
||||||
|
log :debug, "Token url: #{@token_url}"
|
||||||
|
end
|
||||||
|
|
||||||
def build_access_token
|
def build_access_token
|
||||||
verifier = request.params["code"]
|
verifier = request.params["code"]
|
||||||
client.auth_code.get_token(verifier,
|
client.auth_code.get_token(verifier,
|
||||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe OmniAuth::Strategies::KeycloakOpenId do
|
||||||
stub_request(:get, "http://localhost:8080/auth/realms/example-realm/protocol/openid-connect/certs")
|
stub_request(:get, "http://localhost:8080/auth/realms/example-realm/protocol/openid-connect/certs")
|
||||||
.to_return(status: 404, body: "", headers: {})
|
.to_return(status: 404, body: "", headers: {})
|
||||||
OmniAuth::Strategies::KeycloakOpenId.new('keycloak-openid', 'Example-Client', 'b53c572b-9f3b-4e79-bf8b-f03c799ba6ec',
|
OmniAuth::Strategies::KeycloakOpenId.new('keycloak-openid', 'Example-Client', 'b53c572b-9f3b-4e79-bf8b-f03c799ba6ec',
|
||||||
client_options: {site: 'http://localhost:8080', realm: 'example-realm'})
|
client_options: {site: 'http://localhost:8080/', realm: 'example-realm'})
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should have the correct keycloak token url' do
|
it 'should have the correct keycloak token url' do
|
||||||
|
@ -46,4 +46,50 @@ RSpec.describe OmniAuth::Strategies::KeycloakOpenId do
|
||||||
expect(subject.authorize_url).to eq('/auth/realms/example-realm/protocol/openid-connect/auth')
|
expect(subject.authorize_url).to eq('/auth/realms/example-realm/protocol/openid-connect/auth')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'errors processing' do
|
||||||
|
context 'when site contains /auth part' do
|
||||||
|
subject do
|
||||||
|
OmniAuth::Strategies::KeycloakOpenId.new('keycloak-openid', 'Example-Client', 'b53c572b-9f3b-4e79-bf8b-f03c799ba6ec',
|
||||||
|
client_options: {site: 'http://localhost:8080/auth', realm: 'example-realm', raise_on_failure: true})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises Configuration Error' do
|
||||||
|
expect{ subject.setup_phase }
|
||||||
|
.to raise_error(OmniAuth::Strategies::KeycloakOpenId::ConfigurationError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when raise_on_failure option is true' do
|
||||||
|
context 'when openid configuration endpoint returns error response' do
|
||||||
|
subject do
|
||||||
|
stub_request(:get, "http://localhost:8080/auth/realms/example-realm/.well-known/openid-configuration")
|
||||||
|
.to_return(status: 404, body: "", headers: {})
|
||||||
|
OmniAuth::Strategies::KeycloakOpenId.new('keycloak-openid', 'Example-Client', 'b53c572b-9f3b-4e79-bf8b-f03c799ba6ec',
|
||||||
|
client_options: {site: 'http://localhost:8080', realm: 'example-realm', raise_on_failure: true})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises Integration Error' do
|
||||||
|
expect{ subject.setup_phase }
|
||||||
|
.to raise_error(OmniAuth::Strategies::KeycloakOpenId::IntegrationError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when certificates endpoint returns error response' do
|
||||||
|
subject do
|
||||||
|
stub_request(:get, "http://localhost:8080/auth/realms/example-realm/.well-known/openid-configuration")
|
||||||
|
.to_return(status: 200, body: body, headers: {})
|
||||||
|
stub_request(:get, "http://localhost:8080/auth/realms/example-realm/protocol/openid-connect/certs")
|
||||||
|
.to_return(status: 404, body: "", headers: {})
|
||||||
|
OmniAuth::Strategies::KeycloakOpenId.new('keycloak-openid', 'Example-Client', 'b53c572b-9f3b-4e79-bf8b-f03c799ba6ec',
|
||||||
|
client_options: {site: 'http://localhost:8080', realm: 'example-realm', raise_on_failure: true})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises Integration Error' do
|
||||||
|
expect{ subject.setup_phase }
|
||||||
|
.to raise_error(OmniAuth::Strategies::KeycloakOpenId::IntegrationError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue