Query WS-Trust 1.4 STS with .NET / WIF / WCF
Question
I need to query a WS Trust 1.4 service using .NET to enable a SAML 2.0 Authentication scenario.
EDIT: To be more precise I need to support an User Interaction Challenge on the client side which is defined in WS Trust 1.4.
I looked into WIF which provides direct access to WS Trust via the WSTrustChannelFactory (see trustChannelFactory.TrustVersion in the codesnippet...) but it seems that there is only support for WS-Trust 1.3 and Feb2005?
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(getBinding(), "http:/localhost...");
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
RequestSecurityToken rst = new RequestSecurityToken();
RequestSecurityTokenResponse rstr = null;
SecurityToken token = channel.Issue(rst, out rstr);
Does anybody know how to implement such a direct WS-Trust Query using .NET?
I cannot use a WSHttpFederation Binding since we need to work with SAML 2.0 and have to retrieve the SAMl 2.0 Authentication Requests from the Application Server before passing them to the IdP.
I could of course roll my own client-side WS-Trust 1.4. implementation, but perhaps there is an easier way...
Solution
I extended the WIF WS Trust implementation using .NET Extension methods. Here you can see the first part (Issue Request with RST and SAML Authn Request) as an example on how to reuse the things which are already defined in WIF. I used a IL Disassembler to see how things are done inside WIF which was pretty helpful on the way...
internal static RequestSecurityTokenResponseWithSAML2Assertion Issue(this WSTrustChannel pThis,
string pSAML2AuthnRequest,
Func<ProfileSelectionChallengeType, wsTrust14.ChoiceSelectedType> pProfileSelectionCallback)
{
if (pThis != null)
{
if (pThis.ChannelFactory != null &&
pThis.ChannelFactory.Endpoint != null &&
pThis.ChannelFactory.Endpoint.Binding != null)
{
// Create RST Request
RequestSecurityToken rst = new RequestSecurityToken("http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue");
rst.TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
// we use WS Trust 1.4 but .NET WIF only provides support for WS Trust 1.3
// so we add the needed Challenge support and reuse most of the WIF stuff
if (pThis.TrustVersion != System.ServiceModel.Security.TrustVersion.WSTrust13)
{
throw new Exception("Given WS Trust Version not supported!");
}
// create a WS Trust 1.3 SOAP Message
Message issueRequest = Message.CreateMessage(pThis.ChannelFactory.Endpoint.Binding.MessageVersion,
"http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
new WSTrustRequestBodyWriter(rst,
pThis.WSTrustRequestSerializer,
pThis.WSTrustSerializationContext));
// add SAML Authn Request to the WS Trust request
XmlDocument messageAsXml = issueRequest.serializeToXml();
messageAsXml = SAMLSupport.addSAMLAuthenticationRequest(messageAsXml, pSAML2AuthnRequest);
issueRequest = issueRequest.generateFromXml(messageAsXml);
// invoke the WS Trust service on the STS
Message responseMessage = pThis.Issue(issueRequest);
// check what we received as answer...
var response = pThis.parseAndHandleResponse(responseMessage, pProfileSelectionCallback);
return response;
}
}