WCF and Identity in .NET 4.5: External Authentication with WS-Trust

overview scenarios accessing claims windows authentication username authentication client certificate authentication

A typical configuration for a WCF service that uses a WS-Trust security token service would be this:

<system.serviceModel>

  <services>

 

    <service name=Common.ClaimsService>

      <endpoint address=wstrust

                binding=ws2007FederationHttpBinding

                contract=Common.IClaimsService />

    </service>

  </services>

 

  <bindings>

    <ws2007FederationHttpBinding>

      <binding>

        <security mode=TransportWithMessageCredential>

          <message establishSecurityContext=false>

            <issuerMetadata address=https://idsrv/issue/wstrust/mex />

          </message>

 

        </security>

      </binding>

    </ws2007FederationHttpBinding>

  </bindings>

</system.serviceModel>

This uses the 2007 version of the federation binding and advertises the security token service (or rather its metadata endpoint) in configuration (which ends up in the service metadata for svcutil support).

But you need more configuration – first let’s establish trust with the STS, specify the audience URI and set certificate checking:

<system.identityModel>

  <identityConfiguration>

 

    <audienceUris>

      <add value=https://roadie:444/identity/wstrust />

    </audienceUris>

 

    <issuerNameRegistry type=…ConfigurationBasedIssuerNameRegistry, …>

      <trustedIssuers>

        <add name=IdSrv

              thumbprint=BFF6C249A2FFAA51A959B422DAAEDCF10E8A38EB />

      </trustedIssuers>

    </issuerNameRegistry>

 

    <certificateValidation certificateValidationMode=None />

 

  </identityConfiguration>

</system.identityModel>

Next – because WCF defaults to symmetric proof keys, we also need to specify a decryption certificate. This is different compared to WIF, and goes directly into the service credentials behavior:

<serviceCredentials useIdentityConfiguration=true>

  <serviceCertificate findValue=CN=RP

                      storeLocation=LocalMachine

                      storeName=My

                      x509FindType=FindBySubjectDistinguishedName/>

</serviceCredentials>

 

…and of course you need the service authorization behavior to tell WCF to populate Thread.CurrentPrincipal:

<serviceAuthorization principalPermissionMode=Always />

Calling the Service
When you setup the service like above, you will get full svcutil (or ‘Add Service Reference’) support and all client config will be created for you.

I personally prefer the explicit approach of requesting and managing tokens myself. This is accomplished with the WSTrustChannelFactory which is new in .NET 4.5 (but an old friend for people that did WIF before).

private static SecurityToken RequestSecurityToken()

{

    // set up the ws-trust channel factory

    var factory = new WSTrustChannelFactory(

        new UserNameWSTrustBinding(
         
SecurityMode
.TransportWithMessageCredential),

          _idpAddress);

    factory.TrustVersion = TrustVersion.WSTrust13;

 

    factory.Credentials.UserName.UserName = “bob”;

    factory.Credentials.UserName.Password = “abc!123”;

 

    // create token request

    var rst = new RequestSecurityToken

    {

        RequestType = RequestTypes.Issue,

        KeyType = KeyTypes.Symmetric,

        AppliesTo = new EndpointReference(_serviceAddress.AbsoluteUri)

    };

 

    // request token and return

    return factory.CreateChannel().Issue(rst);

}

 

private static void CallService(SecurityToken token)

{

    var binding = new WS2007FederationHttpBinding(
     
WSFederationHttpSecurityMode
.TransportWithMessageCredential);

    binding.Security.Message.EstablishSecurityContext = false;

 

    // set up channel factory

    var factory = new ChannelFactory<IClaimsService>(
      binding,
     
new EndpointAddress
(_serviceAddress));

    factory.Credentials.SupportInteractive = false;

    factory.Credentials.UseIdentityConfiguration = true;

           

    // create channel with specified token

    var proxy = factory.CreateChannelWithIssuedToken(token);

 

    var id = proxy.GetIdentity();

}

 

Using Bearer Tokens
By default WCF uses symmetric proof keys. That means that the SAML token *must* be encrypted with the public key of the relying party to securely transmit the proof key.

Proof tokens provide a linkage between token requests and token usage and thus give you nice anti-repudiation and auditing features – in fact in some industries such audit trails are required.

The downside is that this requires additional key management and for many situations this may not be worth the effort. You can switch to bearer tokens which basically means that the linkage of the two protocol legs is gone, no additional encryption certificate is needed and you base all your protection on SSL.

<bindings>

  <ws2007FederationHttpBinding>

    <binding>

      <security mode=TransportWithMessageCredential>

        <message establishSecurityContext=false

                 issuedKeyType=BearerKey>

          <issuerMetadata address=https://idsrv/issue/wstrust/mex />

        </message>

      </security>

    </binding>

  </ws2007FederationHttpBinding>

</bindings>

That also means that you can remove the decrypting certificate in the service credentials behavior.

Then you’d request a bearer token from your STS (no encrypting certificate needed anymore in the STS configuration):

var rst = new RequestSecurityToken

{

    RequestType = RequestTypes.Issue,

    KeyType = KeyTypes.Bearer,

    AppliesTo = new EndpointReference(_serviceAddress.AbsoluteUri)

};

This entry was posted in IdentityModel, WCF. Bookmark the permalink.

40 Responses to WCF and Identity in .NET 4.5: External Authentication with WS-Trust

  1. Geert D says:

    Hi,
    An STS can expose a web UI that allows the user to supply his username/password and get a token with claims that can be passed to WCF calls.
    When using a fat client instead of a web client, the user might not like it that he has to enter his credentials in the fat client’s UI, because it could be recorded.
    The ‘Windows Azure Authentication Library’ (see http://blogs.msdn.com/b/vbertocci/archive/2012/08/01/windows-azure-authentication-library-a-deep-dive.aspx) can be used from a fat client to login through the web login page by using the AcquireUserCredentialUsingUI method. The question is how to use the token you get to secure WCF calls from the fat client. Can you offer some help here?

    • The WCF/SOAP world is centered around organizational identities and enterprise applications – so they really never bothered with a client that could feel uncomfortable typing in a password into a company-internal application.
      The OAuth space (where AAL lives) is designed around web identities and consumer applications – where this was a much bigger concern.

      So the two worlds are not directly compatible with each other. That said – you can add JWT support (that’s the token format typically used by OAuth) to WCF. It will be just a bit painful ;)

  2. Geert D says:

    Thanks for the clarification.
    It looks like the other scenario is the correct route to take for this case (using Web API layer to expose the functionality of the cloud application). I will do some further investigation in that area.
    Have a nice weekend.

  3. Matt says:

    Hi,

    Thanks for posting this so quickly after my query in one of your older posts.

    I have applied all the settings and feel like I am very close to getting this to work but cannot get past the exception ‘At least one security token in the message could not be validated.’ which has the relating event log entry ‘SecurityTokenValidationException: The X.509 certificate CN=AllocateSoftware chain building failed. The certificate that was used has a trust chain that cannot be verified. ‘

    I assume this is because I am using a self signed SSL certificate so followed the online advice to add the following config to my service behaviour

    and the following config to my service identity model configuration

    but it still errors.

    Any guidence? Thanks!

  4. duja says:

    Hi, if i am understanding well ( pls correct me if am wrong), u can ADFS on some host as IP-STS? yes? If that is the case, do I need and how to configure ADFS on host?
    Aslo
    var factory = new WSTrustChannelFactory(
    new UserNameWSTrustBinding(
    SecurityMode.TransportWithMessageCredential),
    _idpAddress);
    what should _ipAddress be? if my ADFS is on lets say ip address 10.0.0.5 . Also, _serviceAddress.AbsoluteUri , should be address of my service yes? for example https:\\localhost:2160\service?

    Thanks allot for any help.

  5. Uffe Seerup says:

    Hi Dominick

    Great write-up. Explicitly obtaining the token from the STS is exactly what I’m after with a “rich” client application. However, the documentation and samples for the refactored 4.5 WIF is … sparse … (to put it mildly) if one is not using passive WS-fed. One way to translate to a rich client scenario seems to be to obtain the token explicitly and then create channels with that token.

    Can you point to the documentation/assembly for the UserNameWSTrustBinding class? I cannot find it in WIF 4.5 nor in WCF. A search only turns up the 3.5 version (in the Microsoft.* namespace).

    If I wanted to use the credentials of the signed in user (Windows credentials) to obtain a token from an ADFS2, how would you send those credentials?

  6. Gavin Draper says:

    If I’m using ADFS to authenticate against a console app and I’m not using any services what URL should be specified in

    AppliesTo = new EndpointReference(_serviceAddress.AbsoluteUri)

  7. Karlton Zeitz says:

    Dominick,

    I’d like to use WIF 4.5 and WCF to call a REST service using Azure ACS. As Uffe points out I haven’t seen a lot of ports from the older 3.5 Microsoft namespace examples to the 4.5 System namespace and I’m struggling a bit. I think the change from WIF 3.5 to 4.5, along with binding name changes and increased integration into .NET has confused me. I’m unclear (now) what tasks are even required by me and what can be deferred to WIF and the WCF stack.

    Your sample MVC and WCF example helps, but when I convert it to a REST service I can’t seem to get it to work any longer. Perhaps you could point me in the right direction, or outline the correct recipe using the current technologies.

    Thanks so much.

  8. @Pvilevac says:

    Thanks a million. Next step is to extend to a more secure token with the ThinkTecture STS (register cert, add symmetric key, what?). It is important to emphasize in the behavior section of the web config (or app on service hosted). Without it, the service calls can’t see the claims (though you can enforce authentication and authorization).

  9. Peter says:

    What if I have a token from an STS on the Web Tier and want to get my Claims propagated across my WCF calls so the I can do OpertationContext.Current.ClaimsPrincipal or Thread.CurrentPrincipal -> Claims

  10. Mike says:

    Dominick,
    I have a rich client with a browser plugin from which I get a SWT token, from Azure ACS. I pass the token to the WCF service configured to use WIF, to the claims assigned. The claims is supposed to be assigned via a custom token handler decoding the SWT. I would like to ask if this is at all possible? If so how should the web.config file for the service be configured? It would be great to get a pointer to a sample.
    Sorry, the problem is that I’m using .NET 4.5. (samples are hard to find)

    • Sure this is possible. But not built into .NET.

      I would recommend to use the JWT token format instead of SWT – SWT is dead.

      On Nuget there is a JWT authorization manager for WCF (search for JWT).

  11. Pat says:

    Hi
    I can’t find a small sample of an Active STS using certificates anywhere. Microsoft has released a few sample but none of Active STS implemetnation. They keep pushing use of ADFS or Azure for STS. This post comes closest to any sample code. Thank you for that.

    I have looked at the thinktecture project and that is way more extensive than a simple sample. I have also seen all your pluralsight videos but I dont think there is a description of developing Active STS.

    I have setup an Active STS but I keep getting the error

    The message with Action ‘http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT’ cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

    Any ideas where to look?
    Thanks
    Pat

    • Well – SCT is WS-SecureConversation (establishSecurityContext on the binding). You typically disable that, because you don’t want sessions. Especially on the STS.

      IdentityServer has all you need – look for the service host factory in the WS-Trust folder of the Protocols project.

      • Pat says:

        Hi Dominick,
        Thanks for getting back.

        I notice that in .Net 3.5 we could implement
        <service name="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract"
        in the web.config towards implementing an ActiveSTS

        I think we cant do that anymore in .Net 4.5. How does one go about implementing something similar in .Net 4.5?

        Thanks
        Pat

  12. You can use WSTrustServiceHost e.g. this hosts a WSTrustServiceContract, which implements the various IWSTrustxxx interfaces. So nothing has really changed between WIF and .NET 4.5 (besides the namespaces and assembly names).

    • Pat says:

      Hi Dominick,
      Thank you for your help.
      I started implementing a class that implements ServiceHostFactory, however I am having a tough time mapping the service name in the web.config to the class that implements ServiceHostFactory

      I keeps forcing me to map service name to object that is implemented as the code behind for the svc file.
      I keep getting this error:
      Service ‘System.ServiceModel.Security.WSTrustServiceContract’ has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

      Hope that makes sense.

      – Pat

  13. Rasheed says:

    Hello Dominick,

    I followed your courses on PluralSight and you’ve done a great job, I highly recommand them. I’m in the process of securing my WCF services and before installing an STS in our environment, I would like to send an hardcoded token, I can’t find a piece of code to do that, I would like to use the same code as above except that create my hardcoded token instead of request it, like this :


    var myHarded = .. // How can I create this token? That will be valid on the server side with the same server config as above (audienceUris, issuerNameRegistry, ..), also a bearer token.

    // create channel with specified token
    var proxy = factory.CreateChannelWithIssuedToken(myHardedToken);

    var id = proxy.GetIdentity();

    How can I do that?

    Thanks
    Rasheed

    • Well – this is doable but not straightfoward – the principal steps are:

      Create a SecurityTokenDescriptor to describe your token, use that to feed the saml security token handler CreateToken method.

      Then use WriteToken and turn the result into a GenericXmlSecurityToken. That token can then be used to with CreateChannelWithIssuedToken.

      • Rasheed says:

        Thank you for the hints. With fiddler and a few trials, I was able to generate an hardcoded security token that is exactly the same as the token generated by the identity server v2. So now I can use the CurrentPrincipal.Current from the web app and send the identity of the current user to the WCF service using (one leg) of the WS-Trust protocol and when are ready with the STS installation/configuration only the values of the web.config will change and the call to the STS to get the security token before calling the WCF service.

        You can find the code source here : https://bitbucket.org/rachkoud/hardcoded-token/wiki/Home

  14. Pat says:

    Hi Dominic,
    I am passing in an encrypted token got from and Idp (authentication) to another Idp (Authorization)
    I am using CreateChannelWithIssuedToken and passing in the IdpToken, which is encrypted.
    How can I get to the claims in the IdpToken inside the authorization Idp?
    Thanks
    Pat

  15. I don’t know how your authorization IdP works. In WIF they typically get turned into claims on ClaimsPrincipal.Current.

  16. Kondala Rao says:

    Hi ,
    factory.Credentials.UserName.UserName = “bob”;
    factory.Credentials.UserName.Password = “abc!123″;

    In the above code, instead of explicitly mentioning the password is there any we can get the password using windows credentials or any other way we can generate token using windows credentials.
    Thanks in advance.

    • Sure – the STS must support that of course – in WCF terms the STS must expose a Windows authenticated binding for that.

      • Kondala Rao says:

        Hi Dominick,
        Thanks for your reply.
        Currently I am consuming a REST based web service. I am passing the tokens in web request headers. I am not using any binding configurations. Can you please provide any code snippet. Thanks in advance.

  17. fox says:

    Hi Dominik,
    The property UserNameWSTrustBinding.MessageVersion does not provide a setter. I need to call an external STS which does not accept SOAP 1.2.
    How can I use UserNameWSTrustBinding with SOAP 1.1.?
    Thanks in advance.

  18. fox says:

    Hi Dominick,
    I am trying to use your example to connect to an existing Apache CXF based STS. I’ve already got the SAML token from the STS. I’ve set EstablishSecurityContext = true and IssuedKeyType = SecurityKeyType.Bearer on the WS2007FederationHttpBinding to receive an SCT. When I call the business service the RST-Issue with TokenType …/sct is sent to the service. The SAML Token is included.

    This message looks like this:

    http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT

    urn:uuid:f878193d-b3b7-4b54-ba02-c11a01285348


    http://www.w3.org/2005/08/addressing/anonymous

    https://server:8443/service

    2014-02-05T15:02:02.694Z
    2014-02-05T15:07:02.694Z

    ENCRYPTED SAML

    http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/sct

    http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue

    8Ae+h7iuAVGlxCOH5FtdIu0NPI+R52AtdtVecEPIGBA=

    256

    Everything is fine, but unfortunately the STS does not accept the in the security header due to a policy mismatch.
    So I tried to set IncludeTimestamp on the binding to false, which already worked for the initial SAML RST-Issue call.
    But unfortunately this has no effect. The timestamp is always included in the message. I even tried to remove it using a custom interceptor. But the interceptor is only called for the actual service call not for the RTS-SCT Issue call.
    Do you have an idea how to get rid of the in the message?
    Thank you very much in advance.

  19. sebastian says:

    hi dominik,
    i have some problems with Implementation Guide.
    My situation is like this:
    – i didn’t used proof key, these in bearer.
    – instead the object UserNameWSTrustBinding i use KerberosWSTrustBinding.
    – I do not know what to write in the where adress= “” (you write there wstrust..)
    – my big problem its when i try call the method from wcf i get the next error:
    “The signing token Generic XML token:
    validFrom: 01/07/2012 09:33:36
    validTo: 01/07/2012 10:03:36
    InternalTokenReference: Saml2AssertionKeyIdentifierClause( Id = ‘U8ovpOcJlJFu7udUreVI_4I69vj’ )
    Token Element: (Assertion, urn:oasis:names:tc:SAML:2.0:assertion)
    has no keys. The security token is used in a context that requires it to perform cryptographic operations, but the token contains no cryptographic keys. Either the token type does not support cryptographic operations, or the particular token instance does not contain cryptographic keys. Check your configuration to ensure that cryptographically disabled token types (for example, UserNameSecurityToken) are not specified in a context that requires cryptographic operations (for example, an endorsing supporting token).”

Leave a comment