Получение 400 от samltest.id при попытке запускаемого пользователем SP-потока - PullRequest
1 голос
/ 13 февраля 2020

Я использовал библиотеку SAML2 itfoxte c для реализации SP в моем приложении ASP. NET MVC. Я тестирую, используя samltest.id в качестве IdP. Инициированный IdP рабочий процесс работает отлично, но рабочий процесс, инициированный SP, всегда возвращает ошибку 400 из samltest.id. Я попытался просмотреть журнал samltest.id, чтобы увидеть, не была ли там записана ошибка для моего запроса, но я не могу найти там ничего.

Это действие, которое обрабатывает URL, который будет делать его пользователь. go при запуске SSO:

public ActionResult SSOLogin() {
    LogManager logger = new LogManager("SSOLogin");

    string hostname = this.GetHostname();
    SchoolSettings settings = this.GetClientSettings();

    if (settings.UseSAMLSSO) {
        Saml2Configuration samlConfig = null;
        try {
            samlConfig = SamlConfigLoader.GetSaml2Config(HttpContext, settings, this.IsSandbox());
        } catch (Exception e) {
            logger.exception($"loading Saml2Configuration for {hostname}", e);
        }

        if (samlConfig != null) {
            try {
                var binding = new Saml2RedirectBinding();

                binding.SetRelayStateQuery(new Dictionary<string, string> { { "Home/Index", Url.Content("~/") } });

                return binding.Bind(new Saml2AuthnRequest(samlConfig) {

                }).ToActionResult();
            } catch (Exception e) {
                logger.error($"Exception redirecting to IdP. {e.GetType().ToString()}: {e.Message}\n{e.StackTrace}");
                ViewBag.ssoerror = $"Error redirecting to IdP for {hostname}";
            }
        } else {
            logger.critical($"Could not load SAML2 configuration for {hostname}");
            ViewBag.ssoerror = $"Could not load SAML2 configuration for {hostname}";
        }
    } else {
        ViewBag.ssoerror = "SSO is not configured for this client. Please contact Support";
    }

    return Redirect("/Home/SSOError");
}

Метод, который загружает клиентские метаданные c, выглядит следующим образом:

public static Saml2Configuration GetSaml2Config(HttpContextBase context, SchoolSettings forSchool, bool forSandbox) {
    LogManager log = new LogManager("getSaml2Config");

    Saml2Configuration config = new Saml2Configuration();
    if (!forSandbox) {
        config.Issuer = _saml2Issuer;
    } else {
        config.Issuer = _saml2IssuerSandbox;
    }

    config.SignatureAlgorithm = _saml2SignatureAlgo;
    config.CertificateValidationMode = X509CertificateValidationMode.None;
    config.RevocationMode = (X509RevocationMode)Enum.Parse(typeof(X509RevocationMode), ConfigurationManager.AppSettings["Saml2:RevocationMode"]);
    config.AllowedAudienceUris.Add(config.Issuer);

    var entityDescriptor = new EntityDescriptor();
    if (forSchool.SAMLMetadataLocationIsUrl) {
        try {
            entityDescriptor.ReadIdPSsoDescriptorFromUrl(new Uri(forSchool.SAMLMetadataLocation));
        } catch (Exception e) {
            log.error($"Exception caught loading metadata from school {forSchool.Hostname} at URL {forSchool.SAMLMetadataLocation}\n Exception {e.GetType().ToString()}: {e.Message}\n{e.StackTrace}");
            entityDescriptor.IdPSsoDescriptor = null;
        }
    } else {
        var schoolMetadataPath = context.Server.MapPath("~/App_Data/SAMLMetadata/" + forSchool.SAMLMetadataLocation);
        log.info($"Loading metadata for school {forSchool.Hostname} from file {schoolMetadataPath}");
        try {
            entityDescriptor.ReadIdPSsoDescriptorFromFile(schoolMetadataPath);
        } catch (IOException ioe) {
            log.error($"IOException caught loading metadata for school {forSchool.Hostname} from file {schoolMetadataPath}: {ioe.Message}\n{ioe.StackTrace}");
            entityDescriptor.IdPSsoDescriptor = null;
        } catch (Exception e) {
            log.error($"Exception caught loading metadata for school {forSchool.Hostname} from file {schoolMetadataPath}\n Exception {e.GetType().ToString()}: {e.Message}\n{e.StackTrace}");
            entityDescriptor.IdPSsoDescriptor = null;
        }
    }

    if (entityDescriptor.IdPSsoDescriptor != null) {
        if (entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.Count() > 0) {
            config.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
        } else {
            log.error($"WARNING: metadata for {forSchool.Hostname} does not have any SingleSignOnServices that could be parsed.");
        }

        if (entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.Count() > 0) {
            config.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;
        } else {
            log.error($"WARNING: metadata for {forSchool.Hostname} does not have any SingleLogoutServices that could be parsed.");
        }

        if (entityDescriptor.IdPSsoDescriptor.SigningCertificates.Count() > 0) {
            config.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
        } else {
            log.error($"WARNING: metadata for {forSchool.Hostname} does not have any SigningCertificates that could be parsed.");
        }                
    } else {
        throw new Exception("IdPSsoDescriptor not loaded from metadata.");
    }

    return config;
}

Если это поможет прояснить ситуацию Я могу добавить код для действия AssertionConsumerService, который отлично работает в сценарии, инициированном IdP.

Ответы [ 2 ]

1 голос
/ 23 февраля 2020

Я обнаружил проблему. Это сводится к этой строке кода в методе GetSaml2Config:

config.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;

Это наивно берет первый элемент SingleSignOnService в метаданных и решает, что он является правильным для использования, но это не всегда так что это предположение было правдой. Я действительно хотел получить элемент SingleSignOnService для и привязку HTTP-POST:

config.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.Where(s => s.Binding.ToString().IndexOf("HTTP-POST") > 0).FirstOrDefault()?.Location;

Это хорошо работает для всех случаев, которые я обнаружил с тех пор.

0 голосов
/ 17 февраля 2020

Ваш код выглядит правильно.

Вероятно, это проблема интеграции, но очень трудно найти, если IdP не регистрирует сообщение об ошибке.

Какое сообщение об ошибке вы получите вместо успеха, может быть, это что-то вам скажет.

Возможно, IdP не принимает ответ SAML 2.0 Authn, вот что нужно искать:

  • Вероятно, требуется config.SingleSignOnDestination
  • Возможно, IdP требует подписи запроса
  • Можно также добавить другие атрибуты в запрос, сделать Документация IdP описывает какие-либо требования?
...