How to use JWT tokens with WCF and WIF?

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"><!-- Removed --></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"><!-- Removed --></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!

+6
source share
1 answer

I could not solve this problem, but Dominick Baier, one of the developers of IdentityServer, found a workaround.
He believes the exception comes from a WCF error or incompatibility between WCF and JwtSecurityTokenHandler . Since he believes the WCF is done, he does not expect anyone to look at him.

His solution is to wrap the JWT token in a SAML token. Then, by subclassing SamlSecurityTokenHandler , return it and check it for an instance of JwtSecurityTokenHandler .

Here are the links:

Everyone has fun, now :-)

+6
source

Source: https://habr.com/ru/post/987643/


All Articles