【问题标题】:ADFS STS authentication with console application使用控制台应用程序进行 ADFS STS 身份验证
【发布时间】:2016-09-01 01:31:02
【问题描述】:

我有一个网站和 API,由我们的公司 ADFS 支持的令牌服务保护。我需要使用 C# 控制台应用程序访问 API 上的端点。我发现缺乏使用 C# 代码访问 STS 安全网站的资源。它使用 ADFS 3.0。

当我使用HttpClient(或类似的)访问端点时,我会收到一个 HTML 表单作为回报。

我的代码:

Uri baseAddress = new Uri("http://localhost:64022");

using (HttpClient client = new HttpClient() { BaseAddress = baseAddress })
{
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "#");
    HttpResponseMessage response = client.SendAsync(request).Result;

    var encoding = ASCIIEncoding.ASCII;
    using (var reader = new System.IO.StreamReader(response.Content.ReadAsStreamAsync().Result, encoding))
    {
        string responseText = reader.ReadToEnd();
    }
}

我的应用程序的 web.config 文件中的设置是:

<system.identityModel.services>
    <federationConfiguration>
        <cookieHandler requireSsl="false" persistentSessionLifetime="1.0:0:0" />
        <wsFederation persistentCookiesOnPassiveRedirects="true" passiveRedirectEnabled="true" issuer="https://sts.company.com/adfs/ls/" realm="http://myapp.company.com/" requireHttps="false" />
    </federationConfiguration>
</system.identityModel.services>
<system.identityModel>
    <identityConfiguration>
        <audienceUris>
            <add value="http://myapp.company.com/" />
        </audienceUris>
        <issuerNameRegistry>
            <trustedIssuers>
                <add thumbprint="0000000000000000000000000000000000000000" name="https://sts.company.com/adfs/services/trust" />
            </trustedIssuers>
        </issuerNameRegistry>
    </identityConfiguration>
</system.identityModel>

我不确定各种术语是什么。我的远程地址是什么?我的客户编号?什么是指纹?

【问题讨论】:

  • 你让它工作了吗?
  • @snæbjøn 不,我永远无法让它工作。我最终使用 NTLM 身份验证而不是 ADFS 部署了我们 API 的第二个副本。不过,我非常想让这个工作。
  • @Snæbjørn 我已经设法让它工作了。如果您想要我们的示例代码,我可以与您共享一个 pastebin。

标签: c# saml adfs


【解决方案1】:

我想出了如何做到这一点。我不能肯定这是否是最好的实现,但它对我有用。

类 ADFS 令牌提供程序

public class ADFSUsernameMixedTokenProvider
{
    private readonly Uri adfsUserNameMixedEndpoint;

    /// <summary>
    /// Initializes a new instance of the <see cref="ADFSUsernameMixedTokenProvider"/> class
    /// </summary>
    /// <param name="adfsUserNameMixedEndpoint">i.e. https://adfs.mycompany.com/adfs/services/trust/13/usernamemixed </param>
    public ADFSUsernameMixedTokenProvider(Uri adfsUserNameMixedEndpoint)
    {
        this.adfsUserNameMixedEndpoint = adfsUserNameMixedEndpoint;
    }

    /// <summary>
    /// Requests a security token from the ADFS server
    /// </summary>
    /// <param name="username">The username</param>
    /// <param name="password">The password</param>
    /// <param name="endpoint">The ADFS endpoint</param>
    /// <returns></returns>
    public GenericXmlSecurityToken RequestToken(string username, SecureString password, string endpoint)
    {
        WSTrustChannelFactory factory = new WSTrustChannelFactory(
                new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                 new EndpointAddress(adfsUserNameMixedEndpoint));

        factory.TrustVersion = TrustVersion.WSTrust13;

        factory.Credentials.UserName.UserName = username;
        factory.Credentials.UserName.Password = new System.Net.NetworkCredential(string.Empty, password).Password;

        RequestSecurityToken token = new RequestSecurityToken
        {
            RequestType = RequestTypes.Issue,
            AppliesTo = new EndpointReference(endpoint),
            KeyType = KeyTypes.Bearer
        };

        IWSTrustChannelContract channel = factory.CreateChannel();

        return channel.Issue(token) as GenericXmlSecurityToken;
    }
}

类认证

public class Authentication
{
    private GenericXmlSecurityToken token;
    private string site = "https://my.site.com"
    private string appliesTo = "http://my.site.com"
    private string authUsernameEndpoint = "https://sts-prod.site.com/adfs/services/trust/13/usernamemixed";

    public Authentication(PSCredential credential)
    {
        ADFSUsernameMixedTokenProvider tokenProvider = new ADFSUsernameMixedTokenProvider(new Uri(authUsernameEndpoint));
        token = tokenProvider.RequestToken(credential.UserName, credential.Password, appliesTo);
    }

    public CookieContainer GetFedAuthCookies()
    {
        string prepareToken = WrapInSoapMessage(token, appliesTo);
        string samlServer = site.EndsWith("/") ? site : site + "/";
        string stringData = $"wa=wsignin1.0&wresult={HttpUtility.UrlEncode(prepareToken)}&wctx={HttpUtility.UrlEncode("rm=1&id=passive&ru=%2f")}";

        CookieContainer cookies = new CookieContainer();
        HttpWebRequest request = WebRequest.Create(samlServer) as HttpWebRequest;
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.CookieContainer = cookies;
        request.AllowAutoRedirect = false;
        byte[] data = Encoding.UTF8.GetBytes(stringData);
        request.ContentLength = data.Length;

        using (Stream stream = request.GetRequestStream())
        {
            stream.Write(data, 0, data.Length);
        }

        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
        {
            using (Stream stream = response.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    string responseFromServer = reader.ReadToEnd();
                }
            }
        }

        return cookies;
    }

    private string WrapInSoapMessage(GenericXmlSecurityToken token, string site)
    {
        string validFrom = token.ValidFrom.ToString("o");
        string validTo = token.ValidTo.ToString("o");
        string securityToken = token.TokenXml.OuterXml;
        string soapTemplate = @"<t:RequestSecurityTokenResponse xmlns:t=""http://schemas.xmlsoap.org/ws/2005/02/trust""><t:Lifetime><wsu:Created xmlns:wsu=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">{0}</wsu:Created><wsu:Expires xmlns:wsu=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">{1}</wsu:Expires></t:Lifetime><wsp:AppliesTo xmlns:wsp=""http://schemas.xmlsoap.org/ws/2004/09/policy""><wsa:EndpointReference xmlns:wsa=""http://www.w3.org/2005/08/addressing""><wsa:Address>{2}</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><t:RequestedSecurityToken>{3}</t:RequestedSecurityToken><t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType><t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType><t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType></t:RequestSecurityTokenResponse>";

        return string.Format(soapTemplate, validFrom, validTo, site, securityToken);
    }
}

用法

Authentication auth = new Authentication(credential);
CookieContainer container = auth.GetFedAuthCookies();
HttpWebRequest request = WebRequest.Create("https://api.my.site.com/") as HttpWebRequest;

request.Method = method;
request.ContentType = "application/json";
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = false;

using (WebResponse response = request.GetResponse())
{
    using (Stream dataStream = response.GetResponseStream())
    {
        using (StreamReader reader = new StreamReader(dataStream))
        {
            return JsonConvert.DeserializeObject<dynamic>(reader.ReadToEnd());
        }
    }
}

我将它与 PowerShell cmdlet 一起使用,这是 PSCredential 对象的来源。我希望这对想要从 C# 控制台应用程序使用 ADFS 3.0 进行身份验证的人有所帮助 - 我花费的时间比我想承认的要长。

【讨论】:

  • 其实我没有用户名和密码,有没有办法只使用地址邮件?还是别的什么?
  • 很抱歉在 4 年后重振它……但我无法弄清楚您使用了哪些库! VS 并没有真正的帮助。如果你还有的话,你可以发布你的“使用”吗?
  • @bagofmilk 对不起,伙计,我希望我能。很久没有在我写这篇文章的地方工作了:(
【解决方案2】:

您正在处理哪个版本的 ADFS?根据版本,这些是 Web API 支持的最佳选择

希望这会有所帮助。

谢谢//山姆

(推特:@MrADFS)

【讨论】:

  • 感谢您的回复。不幸的是,3.0 的代码示例已经过时,我不知道需要使用哪些新功能。
  • 我使用的是 ADFS v3.0。我不确定我需要提供什么信息或我应该提供什么端点。我无法从 C# 控制台应用程序中找到一个像样的示例。都是 ASP.NET。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-31
  • 2015-12-13
  • 1970-01-01
  • 2013-01-09
  • 1970-01-01
相关资源
最近更新 更多