Files
nairobi/app/lib/omniauth_tenant_setup.rb
Javi Martín 4332637c0f Only access SAML single sign-on URL when necessary
We were calling `parse_remote_to_hash` in the Devise initializer, which
runs when the application starts.

That meant that, if we got an exception when calling that method, the
application wouldn't start. We got exceptions if the single sign-on
(SSO) URL isn't available or we aren't providing the right credentials.

So we're moving the call to `parse_remote_to_hash` to
`OmniauthTenantSetup`, which is only called when actually trying to sign
in with SAML.

Since we're moving the code there, we're also unifying the code so SAML
settings are configured the same way for the main tenant and other
tenants, like we did for OpenID Connect in commit c3b523290.

In order to keep the existing behavior, we're caching the result of
`parse_remote_to_hash` in an instance variable. Not sure about the
advantages and disadvantages of doing so over parsing the remote URL
metadata on every SAML-related request.

Note that the SAML tests in `OmniauthTenantSetup` use the `stub_secrets`
method. But this method is called after the application has started,
meaning it doesn't stub calls to `Rails.application.secrets` in
`config/initializers/`. So, before this commit, the code that parsed the
IDP metadata URL wasn't executed in the tests. Since now we've moved the
code but we don't want to depend on external URLs when running the
tests, we need to stub the call to the external URL. Since we're now
stubbing the call, we're adding expectations in the tests to check that
we correctly use the settings returned in that call.
2025-10-22 12:25:43 +02:00

88 lines
2.8 KiB
Ruby

module OmniauthTenantSetup
class << self
def twitter(env)
oauth(env, secrets.twitter_key, secrets.twitter_secret)
end
def facebook(env)
oauth2(env, secrets.facebook_key, secrets.facebook_secret)
end
def google_oauth2(env)
oauth2(env, secrets.google_oauth2_key, secrets.google_oauth2_secret)
end
def wordpress_oauth2(env)
oauth2(env, secrets.wordpress_oauth2_key, secrets.wordpress_oauth2_secret)
end
def saml(env)
saml_auth(env, secrets.saml_sp_entity_id,
secrets.saml_idp_metadata_url, secrets.saml_idp_sso_service_url)
end
def oidc(env)
oidc_auth(env, secrets.oidc_client_id, secrets.oidc_client_secret, secrets.oidc_issuer)
end
private
def oauth(env, key, secret)
unless Tenant.default?
env["omniauth.strategy"].options[:consumer_key] = key
env["omniauth.strategy"].options[:consumer_secret] = secret
end
end
def oauth2(env, key, secret)
unless Tenant.default?
env["omniauth.strategy"].options[:client_id] = key
env["omniauth.strategy"].options[:client_secret] = secret
end
end
def saml_auth(env, sp_entity_id, idp_metadata_url, idp_sso_service_url)
env["omniauth.strategy"].options.merge!(
saml_settings(sp_entity_id, idp_metadata_url, idp_sso_service_url)
)
end
def saml_settings(sp_entity_id, idp_metadata_url, idp_sso_service_url)
remote_saml_settings(idp_metadata_url).tap do |settings|
settings[:sp_entity_id] = sp_entity_id if sp_entity_id.present?
settings[:idp_sso_service_url] = idp_sso_service_url if idp_sso_service_url.present?
settings[:allowed_clock_drift] = 1.minute
end
end
def remote_saml_settings(idp_metadata_url)
return {} if idp_metadata_url.blank?
@remote_saml_settings ||= {}
@remote_saml_settings[idp_metadata_url] ||= parsed_saml_metadata(idp_metadata_url)
end
def parsed_saml_metadata(idp_metadata_url)
OneLogin::RubySaml::IdpMetadataParser.new.parse_remote_to_hash(idp_metadata_url)
end
def oidc_auth(env, client_id, client_secret, issuer)
strategy = env["omniauth.strategy"]
strategy.options[:issuer] = issuer if issuer.present?
strategy.options[:client_options] ||= {}
strategy.options[:client_options][:identifier] = client_id if client_id.present?
strategy.options[:client_options][:secret] = client_secret if client_secret.present?
strategy.options[:client_options][:redirect_uri] = oidc_redirect_uri if oidc_redirect_uri.present?
end
def oidc_redirect_uri
Rails.application.routes.url_helpers.user_oidc_omniauth_callback_url(Tenant.current_url_options)
end
def secrets
Tenant.current_secrets
end
end
end