【问题标题】:How to get a SAML Response from OKTA for unit/integration testing如何从 OKTA 获取 SAML 响应以进行单元/集成测试
【发布时间】:2017-01-13 16:50:06
【问题描述】:

我正在处理他们通过 OKTA 的 SAML 进行身份验证的项目。我已经成功地将 SAMLResponse 发送(通过 POST 方法)到网站的集成工作。

以真正的 TDD 方式,我从编写一些单元测试开始。我的单元测试采用 SAMLResponse(Base64 编码)。但是,我的所有单元测试都有效,因为 SAMLResponse 只有几分钟的生命周期(过期),所以我的单元测试会在几分钟后中断。

所以我需要定期登录 OKTA,然后使用 Chrome 开发工具捕获发送到我的开发站点的流量。然后我将 SAMLResponse 复制并粘贴到我的单元测试中,然后返回通过单元测试。显然,这不是一个理想的情况。

所以我的问题是如何以自动方式(最好是 C#)登录 Okta 以获得 SAMLResponse?我假设有一些我可以使用用户名和密码发布到的 URL 并取回 SAMLReponse。我所有 Fiddler 试图理解所需沟通的尝试都让我感到沮丧。我正在寻找你可能有的任何指导。提前致谢。

【问题讨论】:

  • 我不熟悉 SAML,但从您的问题来看,我认为您要么在不正确的级别进行测试,要么需要进一步抽象。我会看到,您处理响应的位置与实际消息无关,而对消息的内容更重要,即能够伪造响应应该有效。我永远不会期望单元测试必须调用外部函数,如果它必须登录到外部应用程序,它会响起警报。
  • 同意。这真的正在成为一个集成测试。如果 SAMLResponses 没有过期,它可能真的是一个单元测试,但根据 Okta 的说法,这不是一个选项。鉴于 SAML 集成的复杂性,我们每周依靠测试来查找配置问题。
  • 关于您关于能够“伪造”回复的评论。那是我最初尝试的,但是响应(只是 Base 64 字符串)包含证书签名信息。因此,如果您更改任何响应,验证将失败。我知道我打破了一些单元测试的最佳实践,但我也被 SAML 和 OKTA 逼到了一个角落,但我仍然不会放弃我的集成测试,因为我从中获得了如此多的价值......只是希望它们成为自动化。

标签: c# unit-testing integration-testing saml okta


【解决方案1】:

我想出了一个可行的解决方案,并希望与社区分享。 我不确定根据其他作者 (Joël Franusic) 的有用反馈来回答我自己的问题的协议。如果我违反协议,请告诉我。

感谢 Joël Franusic 的指点。我实现了他的 1.2 解决方案(带有 Okta 客户端的用户代理) 在他的参考资料和 Okta 网站上的其他一些文档之间,我最终能够拼凑出工作代码。

private static async Task<string> GetTestSamlResponse()
    {
        try
        {
            // settings specific to my Okta instance
            string username = "USERNAME GOES HERE";
            string password = "PASSWORD GOES HERE";
            var apiToken = "API TOKEN GOES HERE";

            // this is the unique domain issued to your account.  
            // If you setup a dev account you'll have a domain in the form https://dev-<AccountNumber>.oktapreview.com.
            // account number is a unique number issues by Okta when you sign up for the account
            var baseUrl = "YOUR BASE URL GOES HERE";

            // In Okta Admin UI, click "Applications" in main menu, choose your app, click "Sign On" tab.  Under Sign On Methods, then under SAML 2.0, click "View Setup Instructions"
            // Get the url called "Identity Provider Single Sign-On URL", paste it in th below line
            var ssoUrl = "YOUR SSO URL GOES HERE";

            // construct an Okta settings object
            var settings = new Okta.Core.OktaSettings
            {
                ApiToken = apiToken,
                BaseUri = new Uri(baseUrl)
            };

            // get session token from Okta
            var authClient = new Okta.Core.Clients.AuthClient(settings);
            var authResponse = authClient.Authenticate(username, password);
            var sessionToken = authResponse.SessionToken;

            // start session and get a cookie token
            var sessionsClient = new Okta.Core.Clients.SessionsClient(settings);
            var session = sessionsClient.CreateSession(sessionToken);
            var cookieToken = session.CookieToken;

            // using the cookie token, get the SAMLResponse from Okta via a HTTP GET.
            var httpClient = new System.Net.Http.HttpClient();

            // add User-Agent header, because apparently Okta is expecting this information.  
            // If you don't pass something, the Okta site will return a 500 - Internal Server error
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "UnitTest");

            // add the cookie token to the URL query string
            string url = string.Format("{0}?onetimetoken={1}", ssoUrl, cookieToken);

            // do the HTTP GET
            using (var response = await httpClient.GetAsync(url))
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    // read the HTML returned
                    string html = await response.Content.ReadAsStringAsync();

                    // parse the HTML to get the SAMLResponse (using HtmlAgilityPack from NuGet)
                    HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
                    htmlDoc.LoadHtml(html);
                    // from the input field called SAMLResponse, get the "value" attribute
                    string samlResponse = htmlDoc.DocumentNode.SelectSingleNode("//input[@name='SAMLResponse']").Attributes["value"].Value;
                    return samlResponse;
                }
                else
                    throw new Exception(string.Format("Error getting SAML Response {0}", response.StatusCode));
            }
        }
        catch (Exception ex)
        {
            throw;
        }
    }

【讨论】:

    【解决方案2】:

    有几种方法可以进行您建议的测试,以下是立即想到的:

    1. 使用 RestSharp 或类似的库在 C# 中编写一个简单的 HTTP“用户代理”。
    2. 针对外部模拟 IdP 构建集成测试。
    3. 修改SAMLResponse并用自己的密钥重新签名。

    我将在下面详细介绍每种方法。

    编写一个简单的 HTTP“用户代理”

    我建议这种方法,我建议采取以下两种方法之一:

    1. 编写一个通用的用户代理,它将检测并填写用户名和密码表单字段。我在 Python 中编写了一个名为“saml-messenger”的工具,它采用了这种方法,核心代码在名为messenger.py 的文件中。
    2. 使用 Okta 的 API 获取会话令牌并使用它来获取 SAMLResponseokta-aws-cli-assume-role 项目采用这种方法。 fetching the session tokenexchanging that session token for a SAMLResponse 的代码都可以在 src/main/java/com/okta/tools/awscli.java 文件中找到。

    第一种方法更通用,应该适用于任何具有用户名和密码字段的 IdP。第二种方法可能是您正在寻找的,但特定于 Okta。

    无论哪种情况,我都建议在 Okta 中创建一个特殊用户,该用户被锁定并仅用于测试。

    针对模拟 IdP 构建集成测试

    这种方法会让您设置一个模拟 IdP,它会在没有身份验证的情况下为您提供 SAMLResponse,我之前使用过 saml-idp 项目来执行此操作。

    这种方法的好处是它应该需要更少的 C# 来编写,但代价是承担另一个依赖项。

    修改重签SAMLResponse

    我只是为了完整起见在此处包含此选项,并作为警告您不要尝试此方法的一种方式。学习 SAML 会让你面对疯狂和后悔。我不建议使用这种方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-21
      • 1970-01-01
      • 1970-01-01
      • 2019-12-03
      • 2019-11-08
      • 2016-03-21
      • 1970-01-01
      相关资源
      最近更新 更多