Give your WCF Security Architecture a Makeover with IdentityServer3

Not everybody has the luxury of being able to start over and build the new & modern version of their software from scratch. Many people I speak to have existing investments in WCF and their “old-school” desktop/intranet architecture.

Moving to an internet/mobile world while preserving the existing services is not easy because the technologies (and in my case the security technologies) are fundamentally incompatible. Your new mobile/modern clients will not be seamlessly able to request tokens from your existing WS-Trust STS and SOAP is not really compatible with OAuth2. So what to do?

You could try to teach your WS-Trust STS some basic HTTP based token service capabilities and continue using SAML tokens. You could provide some sort of SAML/JWT conversion mechanism and create Web APIs for your new clients that proxy / convert to the WCF world. Or you could provide to separate token services and establish trust between them. All approaches have their own advantages and disadvantages.

For a project I am currently working on I chose a different approach – get rid of the old WS-Trust STS altogether, replace it with an OAuth2 authorization server (IdentityServer3) and make your WCF services consume JWT tokens. This way both old and new clients can request tokens via OAuth2 and use them with either existing WCF services and the new Web APIs (which ultimately will be also used in the desktop version of the product). How does that work?

Requesting the token
The OAuth2 resource owner flow is what comes closest to WS-Trust and it is easy to replace the WCF WSTrustChannel code with that. Going forward the web view based flows actually give more features like external IdPs etc. but need a bit more restructuring of the existing clients. New clients can use them straight away.

Sending the token
This is the tricky part. WCF can not deal with JWTs directly since they are not XML based. You first need to wrap them in an XML data structure and the typical approach for that is to use a so called binary security token. This worked fine at some point but the latest version of WCF and the JWT token handler don’t seem to work together anymore (here’s a nice write up from Mickael describing the problem).

Since WCF is really done – I did not expect anyone to fix that bug anytime soon, so I needed a different solution.

Another XML container data structure that is well tested and does the job equally well is SAML – so I simply created a minimal SAML assertion to hold the JWT token.

static GenericXmlSecurityToken WrapJwt(string jwt)
{
   
var subject = new ClaimsIdentity("saml"
);
    subject.AddClaim(
new Claim("jwt"
, jwt));

   
var descriptor = new SecurityTokenDescriptor
    {
        TokenType =
TokenTypes
.Saml2TokenProfile11,
        TokenIssuerName =
"urn:wrappedjwt"
,
        Subject = subject
    };

   
var handler = new Saml2SecurityTokenHandler
();
   
var
token = handler.CreateToken(descriptor);

   
var xmlToken = new GenericXmlSecurityToken
(
      
XElement
.Parse(token.ToTokenXmlString()).ToXmlElement(),
       
null
,
       
DateTime
.Now,
       
DateTime
.Now.AddHours(1),
       
null
,
       
null
,
       
null
);

   
return xmlToken;
}

Since we are using SAML solely as a container, there is no signature, no audience URI and just a single attribute statement containing the JWT.

After that you can use the wrapped JWT with the CreateChannelWithIssuedToken method over a federation binding:

var binding = new WS2007FederationHttpBinding(
WSFederationHttpSecurityMode
.TransportWithMessageCredential);
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.IssuedKeyType =
SecurityKeyType
.BearerKey;
var factory = new ChannelFactory<IService
>(
    binding,
   
new EndpointAddress(https://localhost:44335/token
));

var channel = factory.CreateChannelWithIssuedToken(xmlToken);

Validating the token
On the service side I sub-classed the SAML2 security token handler to get the SAML deserialization. In the ValidateToken method I retrieve the JWT token from the assertion and validate it.

Since I have to do the validation manually anyways, I wanted feature parity with our token validation middleware for Web API which means that the token handler can auto-configure itself using the OpenID Connect discovery document as well as do the scope validation.

identityConfiguration.SecurityTokenHandlers.Add(
new IdentityServerWrappedJwtHandler("https://localhost:44333/core", "write"));

The end result is that both WCF and Web API can now consumes JWT tokens from IdentityServer and the customer can smoothly migrate and extend their architecture.

The POC can be found here. It is “sample quality” right now – feel free to make it more robust and send me a PR.

This entry was posted in .NET Security, IdentityServer, OAuth, WCF, WebAPI. Bookmark the permalink.

38 Responses to Give your WCF Security Architecture a Makeover with IdentityServer3

  1. Robert says:

    What’s the advantage of using SAML over a message inspector and just passing the tokens through HTTP headers? Or just use basicHttpBinding and just have a custom SOAP header?

    In my organization we currently have a custom authentication/authorization system, most of our web services expose both a basicHttpBinding and a WebHttpBinding. This way a single service can be consumed by auto generated proxies in .Net clients and can be consumed by single page applications. Our security information is passed around through the HTTP headers. At the time we built this security mechanism WCF 2.0 was the latest web service technology offered by Microsoft.

    We are in the early stages of overhauling our security for all services. We started with looking into oAuth, after that we soon discovered OpenID Connect and its features, then tried to figure out how it would be able to easily replace our existing security components. It appears that at some point the WebHttpBinding was effectively replaced with WebAPI, so our JSON services could technically be rebuilt using WebAPI and easily support this new protocol. There is no clean implementation for WCF though, aside from these wrapper approaches. This means we effectively have two code bases to handle authorization, one for WCF and one for WebAPI where before we had one message inspector that handled both.

    Maybe having services split up into the two APIs isn’t a bad option, I’m just a little confused on how to proceed. I was hoping for a consistent approach to security for services of all kinds.

    Would appreciate any suggestions.

    • Well – as I said there a multiple ways to approach the problem. In the situation I described – the long term plan was to remove WCF and move to Web APIs. Thus new investments in SAML and WS-Trust did not make sense.

  2. Robert says:

    Thanks for the quick reply. Don’t worry I am not criticizing you, I am really just looking for best approach to move forward with. This may be the only blog post on the internet discussing this.

    As far a I know WebAPI does not easily support SOAP(if at all). So are you suggesting that dropping SOAP in favor of just HTTP/JSON? I just like being able to go into my WinRT applications and add service reference and I get a nice auto generated proxy. Everything is type safe. Right now we are about 90% HTML5/JS clients and about 10% XAML/WinRT/C#. Would it make sense to just make straight HTTP requests and manually serialize the JSON for the C# clients as well?

    My logic behind initially using SOAP/basicHttpBinding was that data is cheap, speeds are getting faster, it’s type safe, allows for easy code generation due to having a WSDL/Schemas. Messages can be validated against schemas prior to sending. However when we started building single page applications(before WebAPI existed) we discovered JSON significantly improved performance. So we kept webHttpBinding for HTML clients and basicHttpBinding for .Net clients. JavaScript isn’t type safe anyway, so why bother using XML. I’m trying to figure out if it still makes sense to keep using SOAP for .Net clients.

    As far as I can tell it seems performance was the initial driving factor in the industry for this trend towards JSON instead of XML. If that is the case won’t we all likely just be moving more and more towards websockets? Effectively requiring another workaround for oAuth/OpenID connect since it will be even lower level on TCP instead HTTP? I’ve actually not used web sockets yet, but I’ve used .Net sockets quite a bit.

    To basically to summarize my ramblings… If you had a WebAPI service, and a C#/.Net client what would it look like? Would WebAPI still be the best approach for a .Net consumer? Is any code generation available similar to what was available with WCF service references?

    I’m mostly just talking out loud here, it helps me figure things out. I know you probably don’t have all the answers. Just bouncing some ideas off you since you seem knowledgeable.

    • Well – WCF is done (some call it dead). I would move to HTTP/JSON – especially since only 10% of your clients are C#.

      Performance wasn’t the biggest driver – it was simplicity and cross-platform (WCF failed horribly on both).

      Code generation for Web APIs do exist – but they are not necessary IMO. JSON is type safe and HttpClient (the formatting library) has built-in overloads for automatic serialization from/to DTOs.

      You will be pleasantly surprised how easy this all is compared to WCF.

  3. Robert says:

    Thanks again for the quick reply. I decided to go ahead set up an identity server(IdentityServer3) and wired it up to a users database. I also got a WinRT client going, looked at your implicit client samples. It all works good and I’m pretty happy with it.

    So my last major concern or point of confusion… How do you handle service accounts(Service to Service)? For instance some web services may get called as a result of an event/trigger or essentially be a background process with no UI. In this scenario the web service call was not initiated directly by a user, so obviously we have no one to present the login UI to. Is there anything in IdentityServer3 or the oAuth/OpenID protocols themselves that allow for some kind of authentication via service call rather than a UI? Basically how would a background process authenticate with IdentityServer3 with no user interaction?

    I was hoping for something along the lines of a token with long lifetime or certificate that can basically act as a credential in place of a username/password. Any thoughts?

  4. Robert says:

    That worked perfectly thanks.

    Also, I had an issue authenticating a local WinRT app against a local standalone instance of IdentityServer3. The Web Authentication Broker basically could not locate the service when it was running on localhost:44300, but worked fine when I moved it on to a public web server.

    I found a solution here…
    https://msdn.microsoft.com/en-us/library/windows/desktop/jj658959(v=vs.85).aspx

    I was actually having trouble with Fiddler as well, so this solved two problems.

  5. Angel says:

    Hello Dominick, I hope you’re fine, I have a little question. I have Installed this sample https://github.com/IdentityServer/IdentityServer3.AspNetIdentity which deals with Asp. Net Identity and It works fine when I try to get token with Fiddler, but when I try to start the WcfService from your sample and secure my services it throws this exception:

    {“The remote certificate is invalid according to the validation procedure.”}

    I don’t know what i’m doing wrong, I created my own Self-Signed Certificate and it doesn’t work either, I used the certificate within the sample and the result is that exception too

    So, I would like some little help lol, I hope you could help me
    Thanks in advance

    Best Regards
    Angel

    • Yea – that’s an SSL problem. You need to setup SSL correctly. Can’t help you with that.

      If you have a PluralSight subscription – i have a module on that in the web api security course.

  6. avmp2208 says:

    Sorry dominick, Is there any way to change the way the WcfService is trying to get the configuration? I mean I know that if I pass troghut this in my production host I will have to do the correct way but in localhost I’d like to pass it,

    Best Regards
    Angel

  7. mmihailov says:

    Hello, I see that in the code, when you are creating identity configuration, you are adding also

    identityConfiguration.ClaimsAuthorizationManager = new RequireAuthenticationAuthorizationManager();

    Could you tell me if this is required, what is its purpose?

    Best regards, Martin

    • Can’t remember ;) It is probably a global “filter” to reject anonymous users.

      • mmihailov says:

        Thank you for the reply Dominic.
        I have one more issue. Basically I have MVC Website and WCF Services applications. When a user logs in the website I use the approach you have described in this article.
        The website is registered in IS3, using Hybrid flow.

        However, in some cases calls from the website to the WCF services should be done, without logged in user. In order to be a valid call, I have to pass a token to CreateChannelWithIssuedToken method. I was thinking to register another client, e.g. anonymous_website, using ResourceOwenr or ClientCredentials flows. When I need anonymous call, I will request a token, for this anonymous_website client, and pass it to the CreateChannelWithIssuedToken. Do you think this approach is correct? I would appreciate if you have better suggestions.

        Thanks, Martin

  8. yes thats fine. Use client credentials flow for that since there is no user involved.

  9. Tomasz says:

    Hello,
    since WCF is not limited to XML/SOAP/SAML: what approach would you recommend these days to protect WCF REST/JSON (webHttpBinding) with JWT tokens?
    Ok, someone may ask why use WCF to handle REST calls in the first place. Just one of the reasons could be Azure Service Bus – practically dependent on WCF.
    Best regards,
    Tomasz

    • Sure – JWT works just fine.

      • Tomasz says:

        I mean, is it possible to plug in JwtTokenHandler into WCF-WIF 4.5 pipeline using webHttpBinding *without* implementing custom IDispatchMessageInspector/ServiceAuthorizationManager?
        I guess, this should not require any token wrapping. I believe many would appreciate some demo – looking for answers I came across many posts asking this particular question.

      • No – you need to grab the token from the authZ header and validate it manually. You would do that in a ServiceAuthorizationManager.

        I am pretty sure i have written about this on this blog – maybe 5-7 years ago ;)

      • Tomasz says:

        Well, that is why I asked – panta rhei :)

  10. Tomasz says:

    I wonder if anyone verified the problem you addressed here is attributed to WCF or current JwtTokenHandler 4.0 implementation. New version 5.0 is in beta-7 stage but no details publicly available. At least, I found none, project owners do not respond.

    • Does it matter? I am happy enough with the SAML container solution.

      This only applies to the SOAP pipeline anyways. All the HTTP/REST scenarios are not affected.

      • Tomasz says:

        In a corporate world it matters *a lot* whether solution requires custom security components or uses what is available out of the box and officially supported. My experience is that too highly influences go/no-go decisions but that is what it is.

      • Tomasz says:

        Actually, for that reason (suctom security components) I am unable to use JWT with WCF. I was hoping WCF 4.5 with the new could solve the problems but this is so little documented and “Programming WCF Services” 4th Edition by Juval Lowy is delayed by a year so far. I wish I could drop WCF but then there is MS Azure Service Bus. It really does bring a lot of value when otherwise you have to deal with multiple firewalls/DMZs and middle apps..

      • Tomasz says:

        removed: the new serviceCredentials/useIdentityConfiguration=”true” swith could solve the problems

  11. Well – which basically means – don’t use WCF anymore – because if bugs like this surface, the likelihood of getting them fixed is minimal.

    • Tomasz says:

      Sure, WCF configuration over the years has grown almost beyond manageable/comprehensible level. Like I said, but then there is MS Azure Service Bus and corporate standards too.

  12. Miles says:

    Thanks for this post – it has really helped securing our old wcf apps from the same source as our restful apps.

    I have one service which uses callback interfaces over net.tcp, so I can’t make it use the ws2007fed binding. Can I use this approach with net.tcp? I can’t figure out how to set the binding up without getting hard-to-comprehend wcf config errors and haven’t found much guidance.

  13. Zahar says:

    Hello
    Thank you for all info you are sharing. There are not many places on the net where I could find information about wcf especially in the active federation context.

    Could you please suggest the proper way of handing JWT Token on the client side – when the token is received from STS?
    The chain is as following – I authenticate (.net 4.5 desktop app) on the ADSF and getting SAML2 token, after that I exchange it on some custom STS (using token over transport binding) for the JWT token.
    WCF fails at ReadKeyIdentifierClause since it can’t handle http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd (Binary token).
    I’m not going to open this token, I only need to get its out of the GenericXMLToken.

    I’m looking for the proper way to handle this flow and appreciate any suggestions.

    Thank you

  14. Alex Tertyshnyk says:

    Hello Dominick.
    We are facing same issue now on our project.
    Do you have any information, probably something has changed since the time of your post and ws-trust support was added to Azure AD?

  15. Bashar says:

    Dear Dominick.
    I just used your solution, to integrate WCF with Identity Server.
    I have one question, that i saw the message are not encrypted or singed in the app_messages.svclog , so is there any configuration for doing encryption ?

    Regards,

Leave a comment