I need to generate and issue a token to clients based on username/password authentication. I\'ve tried several approaches to solving this problem, but all of them have run into
What I did was this: I created my own version of a response message which had the bits I needed to create a GenericXmlSecurityToken. This is what's generally returned from a WSTrustChannel, so it seemed like the right thing to do. Thankfully, most of the parameters for a GenericXmlSecurityToken wrapping a JWT are null; I only needed the serialized token, serialized with WriteToken on 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 the web.config:
The binding:
<ws2007FederationHttpBinding>
<binding>
<security mode="TransportWithMessageCredential">
<message issuedKeyType="BearerKey" establishSecurityContext="false" issuedTokenType="urn:ietf:params:oauth:token-type:jwt"/>
</security>
</binding>
</ws2007FederationHttpBinding>
The 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 was required for my scenario): How to configure MIcrosoft JWT with symmetric key?
I haven't seen the issuedTokenType attribute used elsewhere, but I found it was essential to getting my code to work. Without it, I got this error: "MessageSecurityException: Cannot find a token authenticator for the 'Microsoft.IdentityModel.Tokens.JWT.JWTSecurityToken' token type. Tokens of that type cannot be accepted according to 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 I feel more comfortable with.
Thanks to both user2338856 and leastprivilege for getting me partway there!
AFAIK, you need a WSFederationBinding for this. Out of the box this only supports Saml tokens. To have it support Jwt tokens, you need to add a securitytokenhandler that is capable of handling it in the WiF pipeline. Unfortunately, the experimental handler from Microsoft is not that configuration file friendly so you need to subclass it to allow you to specify the properties in config. Alternatively, you can use the ThinkTecture Jwt handler, which is also not extremely easy to set up in config. Setting all of this up will take you quite some time and I am not aware of any examples on the web that do this.