I'm using ACS/Service Identities as a temporary STS while I get things into place. Unfortunately, while I appear to be able to get a SAML 1.1 token fine from ACS, the second I try to pass it into my WCF service things go crazy. As far as I can tell, the token isn't expired (it's being used promptly), I'm not sure how it could be invalid, and nothing I've done with logging has displayed to me any detail on what exactly could be wrong. I'm tempted to assign blame to the binding, because I've never done a formal WCF/WIF binding before. Can anyone see anything wrong with the client/server bindings I'm using (the client was generated via service reference), or suggest an alterative avenue of investigation?
BTW, both the server and client are running on the same development machine.
Web.config:
<configuration>
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
<add key="ida:FederationMetadataLocation" value="--omitted--" />
<add key="ida:ProviderSelection" value="productionSTS" />
</appSettings>
<location path="FederationMetadata">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
<system.web>
<compilation debug="true" targetFramework="4.5">
<assemblies>
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
<httpRuntime targetFramework="4.5" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials useIdentityConfiguration="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add scheme="https" binding="ws2007FederationHttpBinding" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<bindings>
<ws2007FederationHttpBinding>
<binding name="">
<security mode="TransportWithMessageCredential">
<message issuedKeyType="BearerKey" issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"/>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>
<system.identityModel>
<identityConfiguration>
<audienceUris>
<add value="https://localhost:44300/Service1.svc" />
</audienceUris>
<issuerNameRegistry>
<trustedIssuers>
<add name="--omitted--" thumbprint="--omitted--"/>
</trustedIssuers>
</issuerNameRegistry>
<certificateValidation certificateValidationMode="None"/>
</identityConfiguration>
</system.identityModel>
</configuration>
App.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" />
</basicHttpBinding>
<ws2007FederationHttpBinding>
<binding name="WS2007FederationHttpBinding_IService1">
<security mode="TransportWithMessageCredential">
<message issuedKeyType="BearerKey" issuedTokenType="">
<tokenRequestParameters>
<trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<trust:TokenType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</trust:TokenType>
<trust:KeyType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
<trust:CanonicalizationAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm>
<trust:EncryptionAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm>
</trust:SecondaryParameters>
</tokenRequestParameters>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:51853/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"
name="BasicHttpBinding_IService1" />
<endpoint address="https://localhost:44300/Service1.svc" binding="ws2007FederationHttpBinding"
bindingConfiguration="WS2007FederationHttpBinding_IService1"
contract="ServiceReference1.IService1" name="WS2007FederationHttpBinding_IService1" />
</client>
</system.serviceModel>
Client code:
static void Main(string[] args)
{
var factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress("--Azure ACS URL omitted--"));
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = "--omitted--";
factory.Credentials.UserName.Password = "--omitted--";
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference("https://localhost:44300/Service1.svc")
};
SecurityToken token = factory.CreateChannel().Issue(rst);
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
binding.Security.Message.EstablishSecurityContext = false;
var factory2 = new ChannelFactory<IService1>(binding, new EndpointAddress("https://localhost:44300/Service1.svc"));
factory2.Credentials.SupportInteractive = false;
factory2.Credentials.UseIdentityConfiguration = true;
var proxy = factory2.CreateChannelWithIssuedToken(token);
var info = proxy.GetData("testing"); // Exception thrown here
}
Exception:
System.ServiceModel.Security.MessageSecurityException was unhandled
HResult=-2146233087
Message=An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
Source=mscorlib
StackTrace:
Server stack trace:
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, 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 TestWCFClient.ServiceReference1.IService1.GetData(String value)
at TestWCFClient.Program.Main(String[] args) in c:\Users\nicole\Documents\Visual Studio 2012\Projects\TestWCFClient\Program.cs:line 43
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.ServiceModel.FaultException
HResult=-2146233087
Message=The message could not be processed. This is most likely because the action 'http://tempuri.org/IService1/GetData' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.
InnerException:
ETA: In addition, I've tried: switching to SAML 2.0, switching to JWT with the preview JWT token handler, changing the receive timeout, switching the host machine time to UTC, syncing the host with the Windows Time Service explicitly, and waiting for five minutes after the token is issued before using it.