title: Rails/Devise/SAML Metadata Incorrect (not working with PingFederate)
body: <p>Let me preface this question by saying that I'm new to SAML and barely understand how it works.</p>
  
  <h1>The Setup</h1>
  
  <p>I'm using the <code>devise_saml_authenticatable</code> gem with a Rails 4 app to achieve SSO. The Rails app acts as the service provider (SP). To test my setup, I created a OneLogin developer account and set up a <a href="https://support.onelogin.com/hc/en-us/articles/202673944-How-to-Use-the-OneLogin-SAML-Test-Connector" rel="nofollow noreferrer">SAML Test Connector (IdP w/attr w/ sign response)</a> using the following attributes:</p>
  
  <p><strong>Configuration Tab</strong></p>
  
  <ul>
  <li>Audience: mysubdomain.onelogin.com</li>
  <li>Recipient: <a href="http://mysubdomain.myapp.local:3000/saml/auth" rel="nofollow noreferrer">http://mysubdomain.myapp.local:3000/saml/auth</a></li>
  <li>ACS (Consumer) URL Validator: ^http://mysubdomain.myapp.local:3000/saml/auth$</li>
  <li>ACS (Consumer) URL: <a href="http://mysubdomain.myapp.local:3000/saml/auth" rel="nofollow noreferrer">http://mysubdomain.myapp.local:3000/saml/auth</a></li>
  <li>Single Logout URL: <a href="http://mysubdomain.myapp.local:3000/saml/idp_sign_out" rel="nofollow noreferrer">http://mysubdomain.myapp.local:3000/saml/idp_sign_out</a></li>
  </ul>
  
  <p><strong>SSO Tab</strong></p>
  
  <ul>
  <li>Issuer URL: <a href="https://app.onelogin.com/saml/metadata/589819" rel="nofollow noreferrer">https://app.onelogin.com/saml/metadata/589819</a></li>
  <li>SAML 2.0 Endpoint (HTTP): <a href="https://mysubdomain.onelogin.com/trust/saml2/http-post/sso/589819" rel="nofollow noreferrer">https://mysubdomain.onelogin.com/trust/saml2/http-post/sso/589819</a></li>
  <li>SLO Endpoint (HTTP): <a href="https://mysubdomain.onelogin.com/trust/saml2/http-redirect/slo/589819" rel="nofollow noreferrer">https://mysubdomain.onelogin.com/trust/saml2/http-redirect/slo/589819</a></li>
  <li>SAML Signature Algorithm: SHA-1</li>
  <li>SHA Fingerprint: 60:9D:18:56:B9:80:D4:25:63:C1:CC:57:6D:B9:06:7C:78:BB:2C:F1</li>
  <li><p>X.509 Certificate:</p>
  
  <p>-----BEGIN CERTIFICATE-----
  MIIEFzCCAv+gAwIBAgIUQYRVa1MQpUh0gJaznmXSF/SPqnowDQYJKoZIhvcNAQEF
  BQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCEZpcm1QbGF5MRUwEwYDVQQLDAxP
  bmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgOTI1MzEwHhcN
  MTYwOTIxMTU0NzQwWhcNMjEwOTIyMTU0NzQwWjBYMQswCQYDVQQGEwJVUzERMA8G
  A1UECgwIRmlybVBsYXkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW
  T25lTG9naW4gQWNjb3VudCA5MjUzMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
  AQoCggEBALGVgocBj0ciHM3uKlWIcofPhOtzfJw1XpAdNynAvPtbCl7WE5+sLBoQ
  ZF+oZ7Dl+wRW6DHMJCl9DdKcOaQA6/gr5bwt78IzZ8hWMoKQEPih+E0km6rKLYA8
  M52vxtJxGs8Iqx60QvPEePQFMOA+xg73OExfM7W5LnXwNz/Pxgsr3lBif5oCC76j
  SaTCFroV+TSjfOaYMW/lZrsS79KRIzA9I5XwUBe3bC8bsfQmZXgddCrkQUNSGGaS
  7/jtFUlQ94+lAL+l3yoAiNAE6+mt48qqmyLfkKibXvnZ8dwuO272wpY4fEM+vFRy
  pYrTajqvhY3hYIq8dLw3ominE5VECl8CAwEAAaOB2DCB1TAMBgNVHRMBAf8EAjAA
  MB0GA1UdDgQWBBSxiuvTPxwOhh2pupID+tuyKCeceTCBlQYDVR0jBIGNMIGKgBSx
  iuvTPxwOhh2pupID+tuyKCeceaFcpFowWDELMAkGA1UEBhMCVVMxETAPBgNVBAoM
  CEZpcm1QbGF5MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxv
  Z2luIEFjY291bnQgOTI1MzGCFEGEVWtTEKVIdICWs55l0hf0j6p6MA4GA1UdDwEB
  /wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAYBe+5d3zpLZ7fcf3l3rXYeIxcpN+
  9D2YZCbxsrBhY2Am4YE9nN+RaJXeDqeRBNtpayCZVxfHnXexRo1n7wxwTmosiydi
  9yE7SY2xZf+3feQreF25atnn4tzVhxYONaX1njZMIt/TNa7A9aeDfHSD+vwSuYYB
  hGxKT6HOkEAEBiXCZ/FcVNiB0D8bRwQhiJ3BTzXDfqHrmq8QYdn3Ejlqo62vMl6W
  XeMXUoyv6cUc64Ap6E+XtEQI1E8YB5R8GtTs3Y1Oa2dD6yWyCyVJ20+Hi7IWAqXC
  EfqstqXB7FoQ2rAt39cepnu1SOarvEYDMwYIaVNF3hoyodBybJJsAwAnCQ==
  -----END CERTIFICATE-----</p></li>
  </ul>
  
  <p>In my <code>devise.rb</code> I have the following configuration:</p>
  
  <pre><code>config.saml_create_user = false
  config.saml_update_user = true
  config.saml_default_user_key = :email
  config.saml_session_index_key = :session_index
  config.saml_use_subject = true
  config.idp_settings_adapter = IdPSettingsAdapter
  config.idp_entity_id_reader = DeviseSamlAuthenticatable::DefaultIdpEntityIdReader
  </code></pre>
  
  <p>Here is my <code>IdPSettingsAdapter</code>:</p>
  
  <pre><code>class IdPSettingsAdapter
      def self.settings(idp_entity_id)
          company = Company.find_by(idp_entity_id: idp_entity_id)
  
          if company.present?
              {
                  assertion_consumer_service_url: company.assertion_consumer_service_url,
                  assertion_consumer_service_binding: company.assertion_consumer_service_binding,
                  name_identifier_format: company.name_identifier_format,
                  issuer: company.issuer,
                  idp_entity_id: company.idp_entity_id,
                  authn_context: company.authn_context,
                  idp_slo_target_url: company.idp_slo_target_url,
                  idp_sso_target_url: company.idp_sso_target_url,
                  idp_cert_fingerprint: company.idp_cert_fingerprint
              }
          else
              {}
          end
      end
  end
  </code></pre>
  
  <p>Note that my user model <code>Contact</code> belongs_to <code>Company</code>, and that the SSO settings are stored in the <code>Company</code> model.</p>
  
  <p>Here are my saml routes:</p>
  
  <pre><code>devise_for :contacts, skip: :saml_authenticatable, controllers: {
      registrations: "registrations",
      sessions: "sessions",
      passwords: "passwords",
      confirmations: "confirmations"
  }
  devise_scope :contact do
      get '/sign_in' =&gt; 'sessions#new'
      get '/sign_out' =&gt; 'sessions#destroy'
  
      # SSO Routes
      get 'saml/sign_in' =&gt; 'saml_sessions#new', as: :new_user_sso_session
      post 'saml/auth' =&gt; 'saml_sessions#create', as: :user_sso_session
      get 'saml/sign_out' =&gt; 'saml_sessions#destroy', as: :destroy_user_sso_session
      get 'saml/metadata' =&gt; 'saml_sessions#metadata', as: :metadata_user_sso_session
      match 'saml/idp_sign_out' =&gt; 'saml_sessions#idp_sign_out', via: [:get, :post]
  end
  </code></pre>
  
  <p>Lastly here is my <code>SamlSessionsController</code>:</p>
  
  <pre><code>require "ruby-saml"
  
  class SamlSessionsController &lt; SessionsController
      include DeviseSamlAuthenticatable::SamlConfig
      skip_before_filter :verify_authenticity_token, raise: false
      before_action :authorize_viewer, except: [:metadata]
      protect_from_forgery with: :null_session, except: :create
  
      def new
          idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
          request = OneLogin::RubySaml::Authrequest.new
          action = request.create(saml_config(idp_entity_id))
          redirect_to action
      end
  
      def metadata
          idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
          meta = OneLogin::RubySaml::Metadata.new
          render :xml =&gt; meta.generate(saml_config(idp_entity_id)), content_type: 'application/samlmetadata+xml'
      end
  
      def create
          @idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
          response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_config(@idp_entity_id))
  
          if !response.is_valid?
              puts "SAML FAILED WITH ERROR: "
              puts response.errors
          end
          super
      end
  
      def idp_sign_out
          company = Company.friendly.find(request.subdomain.downcase)
          idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
  
          if params[:SAMLRequest] &amp;&amp; Devise.saml_session_index_key
              saml_config = saml_config(idp_entity_id)
              logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: saml_config(idp_entity_id))
              resource_class.reset_session_key_for(logout_request.name_id)
  
              # binding.pry
              sign_out current_contact if contact_signed_in?
              redirect_to company.after_slo_url.present? ? company.after_slo_url : 'https://' + company.issuer
              # redirect_to generate_idp_logout_response(saml_config(idp_entity_id), logout_request.id)
          elsif params[:SAMLResponse]
              #Currently Devise handles the session invalidation when the request is made.
              #To support a true SP initiated logout response, the request ID would have to be tracked and session invalidated
              #based on that.
              if Devise.saml_sign_out_success_url
                  redirect_to Devise.saml_sign_out_success_url
              else
                  redirect_to action: :new
              end
          else
              head :invalid_request
          end
      end
  
      protected
  
      # Override devise to send user to IdP logout for SLO
      def after_sign_out_path_for(_)
          request = OneLogin::RubySaml::Logoutrequest.new
          request.create(saml_config)
      end
  
      def generate_idp_logout_response(saml_config, logout_request_id)
          OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil)
      end
  end
  </code></pre>
  
  <h1>The Problem</h1>
  
  <p>When I manually save map the settings from my OneLogin adapter to my <code>Company</code> model (see screenshot), I'm able to authenticate as a user of my app using OneLogin as the identity provider (IdP). However now I need to provide a client with the XML metadata representing the app's setup. When I go to <code>/saml/metadata.xml</code>, I get the following configuration, which according to my client, is incorrect. The client didn't offer any further details about what the problem is. They are using PingFederate, if that matters.</p>
  
  <pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
  &lt;md:EntityDescriptor ID='_a3581975-b73d-4784-a106-bafd61e15f87' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata'&gt;
      &lt;md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'&gt;
          &lt;md:NameIDFormat&gt;urn:oasis:names:tc:SAML:2.0:nameid-format:transient&lt;/md:NameIDFormat&gt;
          &lt;md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='https://mysubdomain.myapp.local:3000/saml/auth' index='0' isDefault='true'/&gt;
      &lt;/md:SPSSODescriptor&gt;
  &lt;/md:EntityDescriptor&gt;
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/bBAJ6.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/bBAJ6.png" alt="enter image description here"></a></p>
  
  <p>My question is, what am I doing wrong here and how can I correct it? As I said, I barely understand how SAML works under the hood.</p>