Pass SAML token into web api call

2019-07-10 23:06发布

问题:

I have a web application and web api services that authenticate through ADFS. They are contained in the same IIS application, and the web app makes calls back to the web api services without a problem.

I'm now trying to call the same services from a different application, but am having trouble passing the token. I am able to authenticate and retrieve a SAML token with the following code:

var stsEndpoint = "https://MyAdfsServer/adfs/services/trust/13/UsernameMixed";
var reliantPartyUri = "https://MyDomain/AppRoot/";

var factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
            new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
            new EndpointAddress(stsEndpoint));

factory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;

// Username and Password here...
factory.Credentials.UserName.UserName = @"Domain\UserName";
factory.Credentials.UserName.Password = "Password";

var rst = new RequestSecurityToken
    {
        RequestType = RequestTypes.Issue,
        AppliesTo = new EndpointAddress(reliantPartyUri),
        KeyType = KeyTypes.Bearer,
    };

var channel = factory.CreateChannel();
var token = channel.Issue(rst) as GenericXmlSecurityToken;

var saml = token.TokenXml.OuterXml;

However, I'm not sure how to pass the saml in to the web api call. I've tried this:

using (var handler = new HttpClientHandler() 
    { 
        ClientCertificateOptions = ClientCertificateOption.Automatic,
        AllowAutoRedirect = false
    })
{
    using (var client = new HttpClient(handler))
    {
        client.BaseAddress = new Uri("https://MyDomain/AppRoot/api/");

        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SAML", saml);

        HttpResponseMessage response = client.GetAsync("MyService/Get/").Result;

        // Get the results...
        var result = response.Content.ReadAsStringAsync().Result;
        var status = response.StatusCode;
    }
}

This is returning a status code of 302 and trying to redirect me to the ADFS server for authentication. Is there another way to pass the SAML token to the web api service?

回答1:

I have been tackling the same challenge. The heart of the issue is that SAML is a specification that builds on SOAP; it is intended for message protection across multiple SOAP processing nodes, regardless of transport.

On the other hand, REST is all about transport. Thus, the security of REST endpoints rests (no pun intended) on transport security, e.g., SSL.

We have implemented a "hack" where we use a NetworkCredential with a fixed username like "saml_user" and set the password to the SAML token. But this is a short term thing. Do you really want to add 8 to 10 KB on every call?

Simple Web Tokens are much better suited for REST security. In fact, Microsoft's Guide to Claims-based Identity and Access Control (2nd Edition) has an entire chapter devoted to bridging from a SAML token provider to a Simple Web Token. See Accessing REST Services from a Windows Phone Device.



回答2:

(SET)

            string samlString = "blah blah blah";

            byte[] bytes = Encoding.UTF8.GetBytes(samlString);

            string base64SamlString = Convert.ToBase64String(bytes);

            myHttpClient.DefaultRequestHeaders.Add("X-My-Custom-Header", base64SamlString);

(GET)

        IEnumerable<string> headerValues = request.Headers.GetValues("X-My-Custom-Header");

        if (null != headerValues)

        {

            var encoding = Encoding.GetEncoding("iso-8859-1");

            string samlToken = encoding.GetString(Convert.FromBase64String(headerValues.FirstOrDefault()));

        }