Team-Avatar-Neil-Marion-dela-Cruz
Neil Marion dela Cruz May 26, 2025

IN A NUTSHELL

  • This article provides a crucial solution for Rails SaaS developers needing to support multiple, distinct SAML identity providers, which is essential for attracting and retaining enterprise customers today.

  • Discover how to dynamically configure SSO for various client organisations using the omniauth-multi-provider gem, simplifying an otherwise complex and labour-intensive integration process.

  • Learn the foundational middleware setup and critical SAML parameters necessary to implement robust, secure, and scalable SSO authentication in your multi-tenant Rails applications.


 

In many modern SaaS platforms, customers want their users to log in using Single Sign-On (SSO)—a method that allows users to authenticate through their organisation’s identity system (such as Google Workspace, Azure AD, or Okta) instead of creating separate credentials just for your app. SSO simplifies access by enabling users to sign in once and access multiple services seamlessly.

Ruby on Rails supports SSO integration through gems like omniauth-saml, which provides SAML-based authentication out of the box. However, omniauth-saml assumes a single identity provider (IdP) configuration per application. This limitation becomes a blocker in multi-tenant SaaS applications where each tenant may require their own SAML-based IdP.

To solve this, the omniauth-multi-provider gem acts as a dynamic wrapper around OmniAuth. It enables your application to load the correct IdP configuration at runtime—based on the tenant—allowing for flexible and secure SSO authentication across multiple organisations.


Middleware Setup

We start by creating a custom middleware that initialises OmniAuth::MultiProvider with a dynamic configuration:


# lib/saml/saml_sso_registration.rb
module Saml
  class SamlSsoRegistration < OmniAuth::Builder
    def initialize(app, _options = {})
      super(app)

      OmniAuth::MultiProvider.register(
        self,
        provider_name: :saml,
        identity_provider_id_regex: /[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/,
        path_prefix: "/auth/saml",
        callback_suffix: "callback"
      ) do |idp_uuid, rack_env|
        request = Rack::Request.new(rack_env)

        # Find the specific identity provider using its UUID
        idp = IdentityProvider.find_by_uuid(idp_uuid)

        {
          assertion_consumer_service_url: "#{request.url.chomp('/callback')}/callback",
          issuer: idp.issuer,
          idp_sso_target_url: idp.idp_sso_target_url,
          idp_cert: idp.idp_cert
        }
      end
    end
  end
end

Each Identity Provider is identified by a universally unique identifier (UUID), which allows us to reference the corresponding IdentityProvider record stored in our database. When a login request is made, the OmniAuth::MultiProvider.register block uses this UUID to dynamically return a configuration hash tailored to that specific Identity Provider.

Let’s take a closer look at the configuration options returned in the OmniAuth::MultiProvider.register block. These values are critical for establishing a secure and functional SAML authentication flow:

  • assertion_consumer_service_url
    This is the URL in your application where the Identity Provider (IdP) sends its SAML response after a successful login. It’s often referred to as the “callback” URL. This endpoint must be registered in the IdP’s configuration. In most cases, this will look like /auth/saml/callback, and it must match the URL your app is listening on to receive and process SAML responses.

  • issuer
    This is a unique identifier that represents your Rails application as the “Service Provider” (SP) in the SAML protocol. It is used by the Identity Provider to verify the origin of the SAML authentication request. The issuer should be pre-configured in the IdP to ensure requests from your app are trusted. It can be a URL, a URN, or any string identifier, as long as it matches on both sides.

  • idp_sso_target_url
    This is the SAML login endpoint provided by the Identity Provider. When a user initiates login, your app redirects them to this URL to start the SSO process. Each Identity Provider has a specific URL for this purpose—often provided when you configure your application within the IdP’s admin dashboard.

  • idp_cert
    This is the public X.509 certificate issued by the Identity Provider. It’s used by your application to verify the digital signature included in the SAML response. This ensures that the response hasn’t been tampered with and is genuinely from the IdP. You can usually download this certificate from the IdP’s configuration page and store it securely in your database.

Each of these values plays a vital role in completing a secure SAML authentication handshake between your Rails app (the Service Provider) and the Identity Provider.

This post introduces how to use omniauth-multi-provider to support multiple SAML IdPs. In a future post, I’ll walk through a more detailed implementation in a Rails app.

 

Ps. if you have any questions

Ask here