0

Currently we authenticate WCF calls TO a service via ADFS, using the following procedure:

Firstly, we get a SAML token from ADFS

using (var factory = new WSTrustChannelFactory(
   new UserNameWSTrustBinding(System.ServiceModel.SecurityMode.TransportWithMessageCredential),
   new System.ServiceModel.EndpointAddress(new Uri(_aDFSUsernameMixedUri))))
    {
        System.IdentityModel.Configuration.SecurityTokenServiceConfiguration serviceConfig = new System.IdentityModel.Configuration.SecurityTokenServiceConfiguration();
        factory.Credentials.UserName.UserName = _apiUser;
        factory.Credentials.UserName.Password = _apiPassword;
        factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = serviceConfig.CertificateValidationMode;
        factory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;
        WSTrustChannel channel = null;
        try
        {
            var rst = new RequestSecurityToken
            {
                RequestType = WSTrust13Constants.RequestTypes.Issue,
                AppliesTo = new System.ServiceModel.EndpointAddress(_endpointUri),
                TokenType = "urn:oasis:names:tc:SAML:2.0:assertion",
                //KeyType = KeyTypes.Symmetric
                KeyType = KeyTypes.Bearer

            };

            var token = factory.CreateChannel().Issue(rst) as System.IdentityModel.Tokens.GenericXmlSecurityToken;

We use the /adfs/services/trust/13/usernamemixed endpoint (_aDFSUsernameMixedUri variable). This endpoint is proxy enabled.

Secondly, we use this SAML token to contact our service:

using (HttpClient httpClient = new HttpClient() { BaseAddress = baseAddress })
        {
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SAML", saml);
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            string jsonString = JsonConvert.SerializeObject(model);
            HttpResponseMessage response = await httpClient.PostAsync(Url, new StringContent(jsonString, Encoding.UTF8, "application/json"));
            return response;

        }

It is worth mentioning that the service only accepts authenticated requests, and it will read out the authorization details from the SAML token (which is signed & encrypted). That is how the service is secured.

We now want to protect our ADFS server by using an ADFS Proxy (Web Application Proxy). The call to ADFS for the SAML token still works, and returns a SAML token. However, the second step fails. The HTTPRequest is blocked by the ADFS Proxy server, and redirects the call to the ADFS login page, which is of course not what I want.

I have already tried setting the passiveRedirectEnabled="false", but this does not help. Any idea where I have to put the SAML token in the HTTPRequest to let the proxy know that this is an authenticated request?


UPDATE

The ADFS Proxy uses an EdgeAccessCookie to decide whether or not it redirects the request back to the ADFS server. In case I could add this EdgeAccessCookie to the request, the proxy would probably stop blocking the request. How can I retrieve this EdgeAccessCookie in a WCF request?

  • The proxy has the ability to enable/disable different protocols for which it will proxy. Make sure this is enabled for your endpoint. That being said, I've never done this particular scenario before. – technology_is_overrated Aug 11 '14 at 16:10
  • The /adfs/services/trust/13/usernamemixed endpoint is proxy enabled. – Michael Aug 12 '14 at 6:37
1

The Web Application Proxy uses an authorization token to decide whether or not it sets the EdgeAccessCookie. If you can request the authorization token and put it in the URL of your HTTPRequest, the WAP will let you pass. Code snippets:

        List<KeyValuePair<string, string>> postData = new List<KeyValuePair<string, string>>();
        postData.Add(new KeyValuePair<string, string>("UserName", username));
        postData.Add(new KeyValuePair<string, string>("Password", pw));
        postData.Add(new KeyValuePair<string, string>("AuthMethod", "FormsAuthentication"));
        HttpContent content = new FormUrlEncodedContent(postData);

        WebRequestHandler webRequestHandler = new WebRequestHandler();
        webRequestHandler.AllowAutoRedirect = true;
        webRequestHandler.MaxAutomaticRedirections = 1;

        HttpResponseMessage response;
        using (HttpClient client = new HttpClient(webRequestHandler))
        {
            response = client.PostAsync(https://FQDNADFS/adfs/ls?version=1.0&action=signin&realm=urn:AppProxy:com&appRealm=REALMOFAPI&returnUrl=RETURNURL&RedirectToIdentityProvider=IDENTITYPROVIDER, content).Result;
        }

        authToken = HttpUtility.ParseQueryString(response.Headers.Location.Query)["authToken"];
        return authToken;

The above snippet will give you the authorizationcode, which you should include in the URL of your HTTPRequest:

HttpResponseMessage response = await httpClient.PostAsync(Url + "/?authToken=" + authToken, new StringContent(jsonString, Encoding.UTF8, "application/json"));

In combination with the code from my question (no changes there), the WebAPI is secured by the WAP. Good luck.

0

How exactly is the second step blocked by the ADFS proxy? The proxy should only be blocking ADFS calls, not calls to your service. Since your calls to obtain the token work properly, I'd say the setup works as it should ADFS-wise.

Where things go screwy is in the configuration of the service you're protecting. By any chance did you run your service through one of the fedutil apps in Visual Studio when reconfiguring the ADFS proxy? Did this stick a passive authorization block in the web.config so all requests need to be authorized, instead of letting the service handle it?

What you need to do is trace the origin of the redirect response. Nothing in the service should be forcing it as the service foundation (usually) knows better. Some of the usual culprits are the FormsAuthenticationModule, SessionAuthenticationModule + WSFederationAuthenticationModule, etc.

  • The second step is blocked by the ADFS proxy because the hostname of my service resolves to the proxy (as it should be). The proxy then looks at the request and determines whether or not it is authenticated. I think the root of the problem is in the fact that the proxy cannot 'read' the SAML token I'm sending in my HTTPRequest, and thinks that the request is unauthenticated, which is why the proxy redirects my call to ADFS again. However, to answer your question, I have configured everything manually. I use System.IdentityModel.Tokens.Saml2SecurityTokenHandler to handle the incoming tokens. – Michael Aug 12 '14 at 6:26
  • By the way, everything works if I choose 'pass-through' on the proxy or if I just disable the proxy. – Michael Aug 12 '14 at 6:38
  • Ah so you're using WAP in front of your application instead of using it for a proxy to ADFS, or two separate instances, one for each? – Steve Aug 12 '14 at 18:38
  • I use WAP as a proxy for ADFS, AND as a proxy for my application. If I understand correctly, that's the point of pre-authentication on the WAP: an unauthenticated user will never even reach the server of the application. The WAP will stop him/her before that. – Michael Aug 13 '14 at 15:32
  • That is the gist of how it should work, but AFAIK its designed for passive requests, not active service requests. WAP uses a local cookie to protect the session I think. – Steve Aug 13 '14 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.