kerberos delegation + FIM 2010 workflow approval programatically

Jul 25, 2012 at 9:36 PM

I am trying to do a FIM approval workflow programatically. If i do not apply kerberos token, the approval goes through without a problem. Once i apply kerberos credentials to the web service i get the following error message:
System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---> System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed. at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target) at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState) --- End of inner exception stack trace --- Server stack trace: at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout) at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout) at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade) at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at Microsoft.ResourceManagement.Client.WsTransfer.IResourceFactory.Create(Message request) at Microsoft.ResourceManagement.Client.WsTransfer.WsTransferFactoryClient.<>c__DisplayClass1.<create></create>b__0(IResourceFactory channel) at Microsoft.ResourceManagement.Client.ClientBaseExtension.CallChannelMethod[TChannel](ClientBase`1 client, Func`2 method) at Microsoft.ResourceManagement.Client.WsTransfer.WsTransferFactoryClient.Create(Message request) at Microsoft.ResourceManagement.Client.WsTransfer.WsTransferFactoryClient.Approve(RmApproval approval, Boolean isApproved) at Microsoft.ResourceManagement.Client.DefaultClient.Approve(RmApproval approval, Boolean isApproved, String approvalConfiguration) at Microsoft.ResourceManagement.Client.DefaultClient.Approve(RmApproval approval, Boolean isApproved) at Extranet.UserManagement.umUser.setDualAuth(String messageGuid, Boolean dualAuth, String reason) 

Please Help?

Jul 25, 2012 at 11:48 PM

tbagga,

There are a few considerations to think about when attempting to send an ApprovalResponse. If the client needing to connect to the web service is executing on the same box, you are better off just using windows authentication - this is the default. If your client is executing on a remote machine, you can use Kerberos delegation for the connection but you will probably need to impersonate an account matching a valid MPR to perform the update action. If your application is running under FIM Admin or Service credentials on the domain, this will occur and you will only need to apply the context token to the message envelope.

You'll need to use Kerberos only if you are executing under credentials that are not managed under FIM. Optionally, you could use windows impersonation of the approver's credentials to establish the connection... I think identitynotes, ptedesco and I had a few posts in the discussion on how to do this a while back with some code samples. To establish a connection using Kerberos delegation and the FIM Client, you'll need to add in the constructors and the methods that I posted a few weeks ago in the discussion on CodePlex, to your local default client class. You'll need to configure your app.config or pass in all of the necessary endpoint parameters to the constructor. Your UPN or SPN will need to match the delegation you have configured in your AD for FIMService delegation connectivity to negotiate properly.

Note: Configure the Delegation for the Service Account to “Trust this user for delegation to the specified services only” and “Use any authentication protocol” to allow the protocol transition for the authentication. Setting your delegation any other way will cause the connection to fail.

If all else fails, set up a trace on your FIM Service and post the failure including the negotiation type that FIM is seeing.

Hope this helps point you in the right direction!

Jul 27, 2012 at 3:55 PM
Edited Jul 27, 2012 at 6:49 PM

mgercevich,

Thank you for your prompt response and detail about kerberos. I have configured kerberos to the extent that i am able to pass kerberos credentials for enumeration action and everything is working fine for that. 

However this error is happening when i try to pass kerberos during approval response action. You mentioned that i need to pass in necessary endpoint parameters. I have also added them to the app.config file and it gives me a different error.

 Here is my code that i am using with kerberos delegation for approval:

 

  using (winId.Impersonate())     

      {   

            using (DefaultClient client = new DefaultClient())         

       {                    // set credentials and refresh schema           

         client.ClientCredential = credential;               

     client.RefreshSchema();

    foreach (RmResource resource in client.Enumerate(string.Format("/Approval[ApprovalStatus='Pending' and WorkflowInstance='" + messageGuid + "']")))                        {

 {

 RmApproval approval = resource as RmApproval;                     

                if (approval != null)

                                    {                                                                                          

                                                        if (dualAuth)    

       {                                           

    client.Approve(approval, true,"ServiceMultipleTokenBinding_WorkflowManager");         

                                            decision = true;                     

                      }       

                                    else           

                                {               

                                client.Approve(approval, false, "ServiceMultipleTokenBinding_WorkflowManager");     

                                          decision = false;           

                                }                           

                                }

                                    }

  }

}

 

My app.config updates to exisiting endpoint config

 

<binding name="ServiceMultipleTokenBinding_WorkflowManager" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="2048576" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false" contextProtectionLevel="Sign">          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>          <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>          <security mode="Message">            <transport clientCredentialType="Windows" proxyCredentialType="Windows" realm=""/>            <message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true"/>          </security>        </binding>

 

  <endpoint address="http://test.com:5725/ResourceManagementService/Alternate" binding="wsHttpContextBinding" bindingConfiguration="ServiceMultipleTokenBinding_Alternate" contract="Alternate" name="ServiceMultipleTokenBinding_Alternate"/>   

  <endpoint address="http://test:5725/ResourceManagementService/MEX" binding="wsHttpBinding" bindingConfiguration="MetadataExchangeHttpBinding_IMetadataExchange" contract="IMEX" name="MetadataExchangeHttpBinding_IMetadataExchange"/>     

<endpoint address="http://test.com:5726/ResourceManagementService/SecurityTokenService/Registration" binding="wsHttpContextBinding" bindingConfiguration="ServiceMultipleTokenBinding_SecurityTokenService" contract="SecurityTokenService" name="ServiceMultipleTokenBinding_SecurityTokenService"/>       

<endpoint address="http://fimservice.extranetdev.ad.central1.com:5726/ResourceManagementService/WorkflowManager" binding="wsHttpContextBinding" bindingConfiguration="ServiceMultipleTokenBinding_WorkflowManager" contract="WorkflowManager" name="ServiceMultipleTokenBinding_WorkflowManager">          <identity>          <servicePrincipalName value="FIMService/fimservice.extranetdev.ad.central1.com"/>        </identity>      </endpoint>

 

I get the following error when i run the application

 

System.InvalidOperationException: Could not find endpoint element with name 'ServiceMultipleTokenBinding_WorkflowManager' and contract 'ResourceFactory' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element. at System.ServiceModel.Description.ConfigLoader.LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, String configurationName) at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address) at System.ServiceModel.ChannelFactory`1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress) at System.ServiceModel.EndpointTrait`1.CreateSimplexFactory() at System.ServiceModel.ClientBase`1.CreateChannelFactoryRef(EndpointTrait`1 endpointTrait) at System.ServiceModel.ClientBase`1.InitializeChannelFactoryRef() at Microsoft.ResourceManagement.Client.WsTransfer.WsTransferFactoryClient..ctor(String endpointConfigurationName, String remoteAddress) at Microsoft.ResourceManagement.Client.DefaultClient.Approve(RmApproval approval, Boolean isApproved, String approvalConfiguration) at Extranet.UserManagement.umUser.setDualAuth(String messageGuid, Boolean dualAuth, String reason) 

 

Does the endpoint configuration differ for approval response when kerberos is applied?

Jul 27, 2012 at 6:56 PM

Do you get a different error if you change your app.config endpoint

from:

<endpoint address="http://fimservice.extranetdev.ad.central1.com:5726/ResourceManagementService/WorkflowManager" binding="wsHttpContextBinding" bindingConfiguration="ServiceMultipleTokenBinding_WorkflowManager" contract="WorkflowManager" name="ServiceMultipleTokenBinding_WorkflowManager">

to:

<endpoint address="http://fimservice.extranetdev.ad.central1.com:5726/ResourceManagementService/WorkflowManager" binding="wsHttpContextBinding" bindingConfiguration="ServiceMultipleTokenBinding_WorkflowManager" contract="ResourceFactory" name="ServiceMultipleTokenBinding_WorkflowManager">

The Approve method is creating a  WSTransferFactoryClient underneath. You error seems to suggest that this client must use the specific contract. Sorry I don't have a better answer, I have not done much work in the Approval sections either.

Jul 27, 2012 at 9:47 PM

Thanks for your response,

I get this error after i make the change:

An endpoint configuration section for contract 'ResourceFactory' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name. 

An endpoint configuration section for contract 'ResourceFactory' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name. 

 

Stack trace

 

InvalidOperationException: An endpoint configuration section for contract 'ResourceFactory' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name.]   System.ServiceModel.Description.ConfigLoader.LookupChannel(ContextInformation configurationContext, String configurationName, ContractDescription contract, EndpointAddress address, Boolean wildcard, Boolean useChannelElementKind, ServiceEndpoint& serviceEndpoint) +642   System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address) +133   System.ServiceModel.ChannelFactory`1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress) +410   System.ServiceModel.EndpointTrait`1.CreateSimplexFactory() +16424784   System.ServiceModel.ClientBase`1.CreateChannelFactoryRef(EndpointTrait`1 endpointTrait) +129   System.ServiceModel.ClientBase`1.InitializeChannelFactoryRef() +386   Microsoft.ResourceManagement.Client.WsTransfer.WsTransferFactoryClient..ctor() +40   Extranet.UserManagement.umUser..ctor(NetworkCredential loggedInAccount) +2246   Extranet.Global.Session_Start(Object sender, EventArgs e) +1303   System.Web.SessionState.SessionStateModule.CompleteAcquireState() +409   System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData) +1296   System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +115   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +375

Jul 28, 2012 at 4:10 PM

The easiest method may be to the default bindings and impersonate the user prior to instantiating the DefaultClient class, then just call the client.Approve(approval, true) method, once you enumerate the target RmApproval resources.

Sorry I don't have a better answer but it's very difficult to troubleshoot wcf errors without having access to the client and server side trace logs. Hopefully, someone else jumps on this thread and offers a better solution that meets your needs.

Dec 4, 2012 at 2:58 PM

tbagga, did you finally resolve this issue. I am having this exact error in one of my environments, and haven't found an answer.

Dec 4, 2012 at 3:19 PM

Hey shaunmc99,

I have opened ticket with Microsoft premier support for this and even they cannot figure it out. They have said the issue is on Microsoft’s end but no solution as of yet. I will let you know if I hear any good news from them.

Regards

Tarun Bagga

From: shaunmc99 [email removed]
Sent: Tuesday, December 04, 2012 10:58 AM
To: Tarun Bagga
Subject: Re: kerberos delegation + FIM 2010 workflow approval programatically [fim2010client:389048]

From: shaunmc99

tbagga, did you finally resolve this issue. I am having this exact error in one of my environments, and haven't found an answer.

Dec 4, 2012 at 4:32 PM

Tarun,

thanks for your response. Did you find a workaround for the approvals?

The situation I have, is that in my development environment, the approvals work and this environment is quite open and not controlled by policies etc

I do have SPN's and kerberos delegation running and it work in that environment.

The environment I cannot get approvals working is an controlled test environment that is supposed to be an environment that is as close to the production environment as possible. This test environment uses the ADFS service and the claims to tokens service for the windows identities used for the FIM connections with group policies that are not used in the development environment.

All activities relating to enumerating objects, reading object , creating and modifying objects work perfectly, it is just the approvals that have the issues. The endpoint addresses for the approvals all use port 5726 and the ResourceManagementService/workflowmanager in the URI.

I think I may have to get a support ticket raised, is your issue very similar to mine? if so would it be useful for me to quote your ticket so that they know there is more than one instance of this problem?

 

Shaun