Last active
March 24, 2021 04:51
-
-
Save jturkel/8c796708b76c6385c4ee to your computer and use it in GitHub Desktop.
How to configurate omniauth-saml to work with multiple identity providers based on a dynamic URL path segment. A gem based on this idea has been extracted to https://github.com/salsify/omniauth-multi-provider-saml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'omniauth' | |
require 'omniauth-saml' | |
class MultiProviderSamlHandler | |
UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ | |
attr_reader :path_prefix, :provider_name | |
def initialize(path_prefix: OmniAuth.config.path_prefix, provider_name: 'saml') | |
@path_prefix = path_prefix | |
@provider_name = provider_name | |
# Eagerly compute these since lazy evaluation will not be threadsafe | |
@provider_path_prefix = "#{path_prefix}/#{provider_name}" | |
@saml_path_regex = /^#{@provider_path_prefix}\/(?<identity_provider_id>#{UUID_REGEX})/ | |
@request_path_regex = /#{saml_path_regex}\/?$/ | |
@callback_path_regex = /#{saml_path_regex}\/callback\/?$/ | |
end | |
def provider_options | |
{ | |
path_prefix: path_prefix, | |
name: provider_name, | |
request_path: method(:request_path?), | |
callback_path: method(:callback_path?), | |
setup: method(:setup) | |
} | |
end | |
private | |
attr_reader :provider_path_prefix, :saml_path_regex, :request_path_regex, :callback_path_regex | |
def setup(env) | |
identity_provider_uuid = extract_identity_provider_id(env) | |
if identity_provider_uuid | |
options = env['omniauth.strategy'].options | |
add_path_options(options, identity_provider_uuid) | |
add_identity_provider_options(options, identity_provider_uuid) | |
end | |
end | |
def add_path_options(options, identity_provider_uuid) | |
options.merge!( | |
request_path: "#{provider_path_prefix}/#{identity_provider_uuid}", | |
callback_path: "#{provider_path_prefix}/#{identity_provider_uuid}/callback" | |
) | |
end | |
def add_identity_provider_options(options, identity_provider_uuid) | |
# Make this real based on lookup of IdentityProvider with uuid identity_provider_uuid | |
identity_provider = IdentityProvider.find_by(uuid: identity_provider_uuid) | |
if identity_provider | |
options.merge!(identity_provider.options) | |
else | |
raise OmniAuth::Strategies::SAML::ValidationError.new('Invalid identity provider id') | |
end | |
end | |
def request_path?(env) | |
path = current_path(env) | |
!!request_path_regex.match(path) | |
end | |
def callback_path?(env) | |
path = current_path(env) | |
!!callback_path_regex.match(path) | |
end | |
def current_path(env) | |
env['PATH_INFO'] | |
end | |
def extract_identity_provider_id(env) | |
path = current_path(env) | |
match = saml_path_regex.match(path) | |
match ? match[:identity_provider_id] : nil | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'omniauth' | |
Rails.application.config.middleware.use OmniAuth::Builder do | |
saml_handler = MultiProviderSamlHandler.new(path_prefix: '/users/auth') | |
saml_options = { | |
name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', | |
skip_info: true | |
}.merge(saml_handler.provider_options) | |
provider :saml, saml_options | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MyApplication::Application.routes.draw do | |
match '/users/auth/saml/:saml_provider_id/callback', | |
via: [:get, :post], | |
to: 'omniauth_callbacks#saml', | |
as: 'user_omniauth_callback' | |
match '/users/auth/saml/:saml_provider_id', | |
via: [:get, :post], | |
to: 'omniauth_callbacks#passthru', | |
as: 'user_omniauth_authorize' | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment