Search code examples
wcf.net-3.5wifkerberosadfs2.0

Kerberos authentication with invalid network credentials leads to an unexplained timeout after a second call


I have an issues where calling the WSTrustChannel.Issue using a KerberosWSTrustBinding Binding twice with the same params does not produce the same result. The first call causes the exception "No credentials are available in the security package" but the second one causes a timeout. It looks like the first call did not clean up some resources and left the application in a weird state.

Any idea of what is wrong?

Here is the code:

    using System;
    using System.IdentityModel.Tokens;
    using System.ServiceModel;
    using System.ServiceModel.Security;
    using Microsoft.IdentityModel.Protocols.WSTrust;
    using Microsoft.IdentityModel.Protocols.WSTrust.Bindings;
    using Microsoft.IdentityModel.SecurityTokenService;
    using System.Security.Principal;
    using System.Net;
    using System.ServiceModel.Channels;

    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                GetToken();
                GetToken();
            }

            private static void GetToken()
            {
                try
                {
                    var credentials = CredentialCache.DefaultNetworkCredentials;

                    var binding = new KerberosWSTrustBinding(SecurityMode.TransportWithMessageCredential);
                    binding.CloseTimeout = TimeSpan.FromSeconds(10);
                    binding.OpenTimeout = TimeSpan.FromSeconds(10);
                    binding.ReceiveTimeout = TimeSpan.FromSeconds(10);
                    binding.SendTimeout = TimeSpan.FromSeconds(10);

                    var trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress("https://www.google.com/adfs/services/trust/13/kerberosmixed")); //Changing for a valid ADFS server address does not affect the behavior
                    trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
                    trustChannelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
                    trustChannelFactory.Credentials.Windows.ClientCredential = credentials;
                    trustChannelFactory.ConfigureChannelFactory();

                    var wsTrustChannel = (WSTrustChannel)trustChannelFactory.CreateChannel();
                    var requestSecurityToken = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, KeyTypes.Bearer);
                    requestSecurityToken.AppliesTo = new EndpointAddress(new Uri("https://DontCare"));

                    var requestSecurityTokenResponse = new RequestSecurityTokenResponse();
                    var token = wsTrustChannel.Issue(requestSecurityToken, out requestSecurityTokenResponse);

                    Console.WriteLine("[SUCCESS] Kerberos");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("[FAIL] Kerberos");
                    Console.WriteLine(GetExceptionMessages(ex));
                }
            }
            public static string GetExceptionMessages(Exception exception, string separator = ". ")
            {
                StringBuilder sb = new StringBuilder();

                string previousExceptionMessage = null;

                Exception currentException = exception;

                while (currentException != null)
                {
                    string currentExceptionMessage = currentException.Message;

                    if (currentExceptionMessage != previousExceptionMessage)
                    {
                        if (sb.Length > 0)
                        {
                            sb.Append(separator);
                        }

                        sb.Append(currentExceptionMessage);
                    }

                    previousExceptionMessage = currentExceptionMessage;

                    currentException = currentException.InnerException;
                }

                return sb.ToString();
            }

        }
    }

Solution

  • It's funny I came across the same issue again 6 years after without remembering my initial question...

    UPDATE: Event after 6 years this issue has not resolved by itself using the updated version of wif.

    Since this issue is not resolved, knowing that the second call would fail, I set timeouts to 1 ms and reissue the call right away (surprisingly reissuing the call will work)

                binding.CloseTimeout = TimeSpan.FromMilliseconds(1);
                binding.OpenTimeout = TimeSpan.FromMilliseconds(1);
                binding.ReceiveTimeout = TimeSpan.FromMilliseconds(1);
                binding.SendTimeout = TimeSpan.FromMilliseconds(1);
    

    and then the third call (or let the default values)

                binding.CloseTimeout = TimeSpan.FromMilliseconds(60000);
                binding.OpenTimeout = TimeSpan.FromMilliseconds(60000);
                binding.ReceiveTimeout = TimeSpan.FromMilliseconds(60000);
                binding.SendTimeout = TimeSpan.FromMilliseconds(60000);