apokalipto / devise_saml_authenticatable

Devise SAML 2.0 authentication strategy
MIT License
296 stars 154 forks source link

Multiple IdPs, not even able to use SSO against one #156

Closed ekujawski closed 4 years ago

ekujawski commented 4 years ago

I have been able to authenticate against a single IdP when the static configuration is included in the Devise.setup initializer. Such as:

  config.saml_configure do |settings|
    settings.assertion_consumer_service_url = "https://tenet.lvh.me/users/saml/auth"
    settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
    settings.issuer = "https://tenet.lvh.me"
    settings.authn_context = ""
    settings.idp_slo_target_url = "https://idp.lvh.me/simplesaml/index.php"
    settings.idp_sso_target_url = "https://idp.lvh.me/simplesaml/saml2/idp/SSOService.php"
    settings.idp_cert_fingerprint = "6C:36:EF:A1:CB:F2:38:A5:4D:2A:4C:13:92:E8:38:01:86:97:11:0E"
    settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
  end

This is working well, thanks for all the great work!

But now I am wanting to use the Multiple IdP feature. I have the config.idp_entity_id_reader assigned to a class that will properly assign an entity_id. And I have a config.idp_settings_adapter assigned to a class with a settings method that returns a hash:

      {
        assertion_consumer_service_url: "https://tenet.lvh.me/users/saml/auth",
        assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
        name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
        issuer: "https://tenet.lvh.me",
        idp_entity_id: "https://tenet.lvh.me",
        authn_context: "",
        idp_slo_target_url: "https://idp.lvh.me/simplesaml/index.php",
        idp_sso_target_url: "https://idp.lvh.me/simplesaml/saml2/idp/SSOService.php",
        idp_cert_fingerprint: "6C:36:EF:A1:CB:F2:38:A5:4D:2A:4C:13:92:E8:38:01:86:97:11:0E",
        idp_cert_fingerprint_algorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
      }

Authentication is completed when the static settings are enabled. But when the idp_settings_adapter is used, and returning what think is the same configuration, the Rails application fails to accept the SAMLResponse and directs the browser to the IdP (with an authenticated user, directs the browser back to the Rails application).

Is there any additional debugging that can help identify why the Rails application is not accepting the SAMLResponse when using idp_settings_adapter?

Is there any additional configurations needed to use the Multiple IdP feature?

ekujawski commented 4 years ago

Still investigating, but I think the problem is that config.idp_entity_id_reader does not always properly assign an entity id. The entity_id is a function of the domain name, and the HTTP headers are not available within the idp_settings_adapter#entity_id method

adamstegman commented 4 years ago

It sounds like you may already be past this step, but do you get a log message about what's wrong with the SAML response? See https://github.com/apokalipto/devise_saml_authenticatable/blob/2105b89e2117ce6e0761c577ea6a810931295b31/lib/devise_saml_authenticatable/strategy.rb#L43. If not, you may be able to write a saml_failed_callback to investigate the response yourself and diagnose what went wrong. See https://github.com/apokalipto/devise_saml_authenticatable/blob/2105b89e2117ce6e0761c577ea6a810931295b31/lib/devise_saml_authenticatable/strategy.rb#L57.

It seems like headers could be added to the get_idp_entity_id calls. I'm most uncertain about the strategy, but some digging could reveal if the headers method defined in Warden returns the request headers.

ekujawski commented 4 years ago

note: I found that adding the domain to the params can be done for a GET request in the before_action of the controller. But a POST request needed to fetch the domain from the SAMLResponse.

response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
response.audiences # list of audience domains for my use

Thanks for the debugging hints. I'm running into a silent failure when using fingerprint (idp_cert_fingerprint) and a OpenSSL::X509::CertificateError in Devise::SamlSessionsController#create when using the certificate (idp_cert). I will investigate with the saml_failed_callback method.

ekujawski commented 4 years ago

Found it, thank for the debugging help! Now I can see a report with the following error using the saml_failed_callback option

Which now I understand, since

idp_entity_id: "https://tenet.lvh.me",

should have been

idp_entity_id: "https://idp.lvh.me/simplesaml/saml2/idp/metadata.php",

Just as the error message said it should have been.

I can report that it is now working property! Now for the next step - multiple identify providers with settings stored in the database