---
title: "Integrate SAML with many IDPs with Devise & OmniAuth"
description: "How to enable SSO using SAML service provider for multiple IDPs"
canonical_url: "https://www.bigbinary.com/blog/saml-integration-with-multiple-idps-using-devise-omniauth"
markdown_url: "https://www.bigbinary.com/blog/saml-integration-with-multiple-idps-using-devise-omniauth.md"
---

# Integrate SAML with many IDPs with Devise & OmniAuth

How to enable SSO using SAML service provider for multiple IDPs

- Author: Vijay Kumar Agrawal
- Published: October 11, 2017
- Categories: Rails

Recently, we integrated our SAML service provider(SP) with multiple identity
providers(IDPs) to facilitate Single sign-on(SSO) using Devise with OmniAuth.

Before we jump into the specifics, here is SAML definition from
[wikipedia](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language).

> Security Assertion Markup Language (SAML, pronounced sam-el) is an open
> standard for exchanging authentication and authorization data between parties,
> in particular, between an identity provider(IDP) and a service provider(SP).

The choice of [Devise](https://github.com/plataformatec/devise) with
[OmniAuth-SAML](https://github.com/omniauth/omniauth-saml) to build SAML SSO
capabilities was natural to us, as we already had dependency on Devise and
OmniAuth nicely integrates with Devise.

[Here](https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview) is the
official overview on how to integrate OmniAuth with Devise.

After following the overview, this is how our `config` and `user.rb` looked
like.

```ruby
# config file
Devise.setup do |config|
  config.omniauth :saml,
    idp_cert_fingerprint: 'fingerprint',
    idp_sso_target_url: 'target_url'
end

#user.rb file
devise :omniauthable, :omniauth_providers => [:saml]
```

The problem with above configuration is that it supports only one SAML IDP.

To have support for multiple IDPs, we re-defined files as below.

```ruby
# config file
Devise.setup do |config|
  config.omniauth :saml_idp1,
    idp_cert_fingerprint: 'fingerprint-1',
    idp_sso_target_url: 'target_url-1'
    strategy_class: ::OmniAuth::Strategies::SAML,
    name: :saml_idp1

  config.omniauth :saml_idp2,
    idp_cert_fingerprint: 'fingerprint-2',
    idp_sso_target_url: 'target_url-2'
    strategy_class: ::OmniAuth::Strategies::SAML,
    name: :saml_idp2
end

#user.rb file
devise :omniauthable, :omniauth_providers => [:saml_idp1, :saml_idp2]
```

Let's go through the changes one by one.

**1. Custom Providers:** Instead of using standard provider `saml`, we
configured custom providers (`saml_idp1`, `saml_idp2`) in the first line of
configuration as well as in `user.rb`

**2. Strategy Class:** In case of the standard provider(`saml`), Devise can
figure out `strategy_class` on its own. For custom providers, we need to
explicitly specify it.

**3. OmniAuth Unique Identifier:** After making the above two changes,
everything worked fine except OmniAuth URLs. For some reason, OmniAuth was still
listening to `saml` scoped path instead of new provider names
`saml_idp1, saml_idp2`.

```ruby
# Actual metadata path used by OmniAuth
/users/auth/saml/metadata

# Expected metadata path
/users/auth/saml_idp1/metadata
/users/auth/saml_idp2/metadata
```

After digging in `Devise` and `OmniAuth` code bases, we
[discovered](https://github.com/omniauth/omniauth/blob/6cce8181b45da49cecb9c0af139cfc62b3b1a25e/lib/omniauth/strategy.rb#L139)
provider `name` configuration. In the absence of this configuration, OmniAuth
falls back to strategy class name to build the path. As we could not find any
code in Devise which defined `name` for `OmniAuth` that explained `saml` scoped
path (we were expecting Devise to pass `name` assigning same value as
`provider`).

After adding `name` configuration, OmnitAuth started listening to the correct
URLs.

**4. Callback Actions:** Lastly, we added both actions in
`OmniauthCallbacksController`:

```ruby
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def saml_idp1
    # Implementation
  end

  def saml_idp2
    # Implementation
  end

  # ...
  # Rest of the actions
end
```

With these changes along with the official guide mentioned above, our SP was
able to authenticate users from multiple IDPs.

## Links

- [Human page](https://www.bigbinary.com/blog/saml-integration-with-multiple-idps-using-devise-omniauth)
