General notes
We use IdentityServer3 and are still very pleased with it.
We very easily helped MVC and ASP.NET Web API applications with MS and Thinktecture OWIN middlewares.
The client we work for still has many WCF SOAP services, and this is where we get stuck.
Customization
I will not lie, I am far from experience with WCF, I used it only for the simplest scenarios - I understand basicHttpBinding, lack of transport and message security.
This is what I want to achieve:
- Client receives JWT access token from IdentityServer
- Somehow the token gets into the SOAP message headers
- WCF reads and validates the token
- WCF checks claims and authorizes based on some criteria
I can not get the third step to work.
Server Tuning
- I am using
ws2007FederationHttpBinding security ws2007FederationHttpBinding with TransportWithMessageCredential . The message contains BearerKey , and the token is of type urn:ietf:params:oauth:token-type:jwt - The service uses the WIF authentication pipeline to which I added the
JwtSecurityTokenHandler from the System.IdentityModel.Tokens.Jwt NuGet package
Client setup
- The JWT icon issued by STS is wrapped in the
BinarySecurityToken XML element itself wrapped in a GenericXmlSecurityElement - This token is used as the
CreateChannelWithIssuedToken ChannelFactory parameter ChannelFactory
What will happen
The token is found in the SOAP header and passed to the JwtSecurityTokenHandler .
But then an exception is thrown:
System.ServiceModel.Security.MessageSecurityException: Message security verification failed. ---> System.IndexOutOfRangeException: Index was outside the bounds of the array. at System.Xml.XmlBufferReader.GetChars(Int32 offset, Int32 length, Char[] chars) at System.Xml.XmlBufferReader.GetString(Int32 offset, Int32 length) at System.Xml.StringHandle.GetString() at System.Xml.XmlBaseReader.ReadEndElement() at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader) at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy) at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(Message& message, TimeSpan timeout) at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout) --- End of inner exception stack trace ---
After JustDecompiling, it looks like an error occurs when further reading XML elements in the SOAP header. What is strange is that the token is the last element. Here is what this post looks like:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IService/GetListOfStrings</a:Action> <a:MessageID>urn:uuid:5c22d4e2-f9b8-451a-b4ca-a844f41f7231</a:MessageID> <ActivityId CorrelationId="554fc496-7c47-4063-9539-d25606f186b0" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">1213dcd7-55b7-4153-8a6d-92e0922f76dd</ActivityId> <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> </a:ReplyTo> <VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo90CpMlUwLBOmEPkZ5C8fRQAAAAAVWkkf2rJS0qImBv+Yx1recUXdbBLjThDkAMkwfW3/2AACQAA</VsDebuggerCausalityData> <a:To s:mustUnderstand="1">https://localhost.fiddler:44322/Service.svc</a:To> <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <u:Timestamp u:Id="_0"> <u:Created>2015-05-21T06:41:45.362Z</u:Created> <u:Expires>2015-05-21T06:46:45.362Z</u:Expires> </u:Timestamp> <wsse:BinarySecurityToken ValueType="urn:ietf:params:oauth:token-type:jwt" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"></wsse:BinarySecurityToken> </o:Security> </s:Header> <s:Body> <GetListOfStrings xmlns="http://tempuri.org/" /> </s:Body> </s:Envelope> uIDPo90CpMlUwLBOmEPkZ5C8fRQAAAAAVWkkf2rJS0qImBv + Yx1recUXdbBLjThDkAMkwfW3 / 2AACQAA </ VsDebuggerCausalityData> <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IService/GetListOfStrings</a:Action> <a:MessageID>urn:uuid:5c22d4e2-f9b8-451a-b4ca-a844f41f7231</a:MessageID> <ActivityId CorrelationId="554fc496-7c47-4063-9539-d25606f186b0" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">1213dcd7-55b7-4153-8a6d-92e0922f76dd</ActivityId> <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> </a:ReplyTo> <VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo90CpMlUwLBOmEPkZ5C8fRQAAAAAVWkkf2rJS0qImBv+Yx1recUXdbBLjThDkAMkwfW3/2AACQAA</VsDebuggerCausalityData> <a:To s:mustUnderstand="1">https://localhost.fiddler:44322/Service.svc</a:To> <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <u:Timestamp u:Id="_0"> <u:Created>2015-05-21T06:41:45.362Z</u:Created> <u:Expires>2015-05-21T06:46:45.362Z</u:Expires> </u:Timestamp> <wsse:BinarySecurityToken ValueType="urn:ietf:params:oauth:token-type:jwt" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"></wsse:BinarySecurityToken> </o:Security> </s:Header> <s:Body> <GetListOfStrings xmlns="http://tempuri.org/" /> </s:Body> </s:Envelope>
It does not look like something is distorted or something else. An exception should be thrown from the stack trace when reading the final element </o:Security> , since the token was correctly read and processed.
Repro
I was looking for repo patterns so you can see if you feel it. Here are the relevant projects:
SelfHost (Minimal) in the sources folder. This is STS- In the
Clients solution, the WCF service is located in the APIs folder - In the
Clients solution, the WCF client is a Console Client Credentials With Wcf
The best way to start it is to start STS first, then Right click -> Debug -> Start new instance in the WCF service, same thing in the WCF client.
Thanks in advance!