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 option
master
Alexander Petrov 2020-12-06 21:13:52 +03:00 committed by GitHub
parent 30b1201646
commit 5e20c44ff6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 12 deletions

View File

@ -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,

View File

@ -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