Providing JWT SecurityToken to WCF Client

I need to create and issue a token to clients based on username and password authentication. I have tried several approaches to solve this problem, but they all ran into problems.

My first plan was to implement WS-Trust Issue on my WCF endpoint. An example I found that used this:

[OperationContract(Action = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
                   ReplyAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue")]
Message IssueToken(Message rstMessage);

      

However, the changes to WIF in 4.5 to integrate it into the .NET Framework broke the rest of the sample code to convert a Message to a RequestSecurityToken. The WSTrustRequestSerializer seems to do this, but it requires a WSTrustSerializationContext and little information on how to create or configure this context object.

I tried to simply serialize the JWT I want to use for my SecurityToken to a string and return it to the client, but it looks like deserializing it into a SecurityToken that WCF can use would require me to send a JWTSecurityToken and a handler on the client, which I want to avoid ... While the WS-Trust bindings seem to have somehow bypassed this and created a GenericXmlSecurityToken, I can't seem to find how to create one myself.

Any thoughts on how to serialize / deserialize the RequestSecurityToken and RequestSecurityTokenResponse objects in WS-Trust, or how to serialize / deserialize the token outside of the WS-Trust framework? Or other ideas?

+1


source to share


2 answers


What I did was this: I created my own version of the response message, which needed the bits needed to create the GenericXmlSecurityToken. This is what WSTrustChannel usually returned, so it seemed to be correct. Fortunately, most of the parameters for the GenericXmlSecurityToken that wrap the JWT are null; I only need a serialized token, serialized with a WriteToken in the JWTSecurityTokenHandler in the service, and the validFrom and validTo values.

Client code:

XmlElement element = document.CreateElement("wsse", "BinarySecurityToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
element.SetAttribute("ValueType", "urn:ietf:params:oauth:token-type:jwt");
element.SetAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
UTF8Encoding encoding = new UTF8Encoding();
element.InnerText = Convert.ToBase64String(encoding.GetBytes(jwtToken));

GenericXmlSecurityToken token = new GenericXmlSecurityToken(
    element,
    null,
    validFrom,
    validTo,
    null,
    null,
    null);

var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.IssuedTokenType = "urn:ietf:params:oauth:token-type:jwt";

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.DoWork();

      

Relevant bits of web.config:

Binding:

<ws2007FederationHttpBinding>
  <binding>
    <security mode="TransportWithMessageCredential">
      <message issuedKeyType="BearerKey" establishSecurityContext="false" issuedTokenType="urn:ietf:params:oauth:token-type:jwt"/>
    </security>
  </binding>
</ws2007FederationHttpBinding>

      



IdentityModel section:

<system.identityModel>
  <identityConfiguration>
    <audienceUris>
      <add value="--audienceUri--"/>
    </audienceUris>
    <securityTokenHandlers>
      <add type="--namespace--.CustomJWTSecurityTokenHandler, --my dll--" />
      <securityTokenHandlerConfiguration>
        <certificateValidation certificateValidationMode="PeerTrust"/>
      </securityTokenHandlerConfiguration>
    </securityTokenHandlers>
    <issuerNameRegistry>
      <trustedIssuers>
        <add name="--issuer--"  thumbprint="--thumbprint--"/>
      </trustedIssuers>
    </issuerNameRegistry>
  </identityConfiguration>
</system.identityModel>

      

And the CustomJWTSecurityTokenHandler from this question (only the validIssuer part is required for my scenario): How to set up MIcrosoft JWT with symmetric key?

I haven't seen the givenTokenType attribute used elsewhere, but I found it very important to get my code to work. Without it, I got this error: "MessageSecurityException: Cannot find token authenticator for token type" Microsoft.IdentityModel.Tokens.JWT.JWTSecurityToken ". Tokens of this type cannot be accepted according to the current security settings."

This might be overkill as a solution, but I think it minimizes the amount of custom code and centralizes it in places where I feel more comfortable.

Thanks to both user2338856 and least privileged one for getting me out of there!

+3


source


AFAIK, you need a WSFederationBinding for this. Out of the box, this only supports Saml tokens. For it to support Jwt tokens, you need to add a security handler capable of handling it in the WiF pipeline. Unfortunately, an experimental handler from Microsoft does not support this config file, so you need to subclass it so you can specify properties in config. Alternatively, you can use the ThinkTecture Jwt handler, which is also not very easy to customize in configuration. It will take you quite a long time to install all of this, and I don't know of any examples on the Internet that do this.



+1


source







All Articles