Koen about .Net

August 2, 2010

Making a web application use an active STS

Filed under: Development, dotnetmag — Tags: , , , — koenwillemse @ 19:46

At my current assignment we’re working on a solution which among others consists of a custom Security Token Service which is used for the authentication of users in a web portal. In our case we’ve created an active STS by using Windows Identity Foundation and WCF. We’re now working a demo web application which uses the STS (and some other functionality we’re building) to show the team which creates the portal how they should consume the STS.

Since there is almost NO decent documentation about WIF, I started googling. The problem which I ran into was that the information I found was almost all about scenario’s with a passive STS. I found a bit information about consuming an active STS but all not complete, so I’ll put all the steps I had to take right here, so it can help some others.

First of all, I eventually found this blog post, which made it almost work for me. There was just some information missing which caused me to search for another 1 – 2 hours.

These are the steps to take when you want to consume an active STS from a web application:

  1. Add a reference to the Microsoft.IdentityModel assembly (WIF).
  2. Add the definition of the microsoft.IdentityModel config section to your config like this (check the correct version of the dll of course):
    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    
  3. Add the following two HttpModules to your config (when using IIS7, add them to you system.webserver section, otherwise to you system.web section):
    <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  4. The authentication mode should be set to None:
    <authentication mode="None" />
  5. Add the configuration for the microsoft.IdentityModel section, for instance:
    <microsoft.identityModel>
    <service>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <trustedIssuers>
    <add thumbprint="{Add the thumbprint of the certificate used by your STS, for instance: 80481e4041bd6758400c62e2c811831b98eed561}" name="{Add the name of the certificate, for instance: CN=devsts}" />
    </trustedIssuers>
    </issuerNameRegistry>
    <audienceUris>
    <add value="{Add the applies to url of your web application}"/>
    </audienceUris>
    <federatedAuthentication>
    <wsFederation passiveRedirectEnabled="false" issuer="{The address of the STS, for instance: https://devsts/mySts.svc}" realm="{The applies to address of your web application, for instance: http://myrelyingparty.nl}" persistentCookiesOnPassiveRedirects="true" />
    <cookieHandler requireSsl="false" />
    </federatedAuthentication>
    <serviceCertificate>
    <certificateReference x509FindType="FindByThumbprint" findValue="{The certificate used by your STS, for instance: 80481e4041bd6758400c62e2c811831b98eed561}" storeLocation="LocalMachine" storeName="My"/>
    </serviceCertificate>
    </service>
    </microsoft.identityModel> 

    As you can see, you register your information about the certificate being used by your sts and the information about your application, the relying party.
    The line about the cookieHandler was the one that caused me some problems because I didn’t have that. The problem is that my local site was working on http and not https, but the created cookies required https. I didn’t have this line at first, which had the effect that the session cookie was not maintained over postbacks.

  6. After you’ve configured everything, you can use the following code to consume your STS and get an IClaimsIdentity:
    // authenticate with WS-Trust endpoint
    var factory = new WSTrustChannelFactory(
    new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
    new EndpointAddress("<a href="https://devsts/MySts.svc">https://devsts/MySts.svc</a>"));
    factory.Credentials.UserName.UserName = usernameField.Text;
    factory.Credentials.UserName.Password = passwordField.Text;
    var channel = factory.CreateChannel();
    var rst = new RequestSecurityToken
    {
    RequestType = RequestTypes.Issue,
    AppliesTo = new EndpointAddress("http://myrelyingparty.nl/"),
    KeyType = KeyTypes.Bearer
    };
    var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
    // Now you parse and validate the token which results in a claimsidentity
    var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
    var token = handlers.ReadToken(new XmlTextReader(new StringReader(genericToken.TokenXml.OuterXml)));
    var identity = handlers.ValidateToken(token).First();
    
    // Create the session token using WIF and write the session token to a cookie
    var sessionToken = new SessionSecurityToken(ClaimsPrincipal.CreateFromIdentity(identity));
    FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
    
    //Perform some redirect
    Response.Redirect("~/secure/default.aspx");
    

In our situation, this was not complete, since we need the SAML token received from the STS furtheron to authenticate to WCF services which we consume. After using reflector and trying something out, it can be easily done by changing just two lines of code. I can be done by the following:

var identity = handlers.ValidateToken(token).First();
Thread.CurrentPrincipal = new ClaimsPrincipal(new IClaimsIdentity [] { new ClaimsIdentity(identity.Claims, token) });

The code itself looks probably a bit strange, since we get a ClaimsIdentity from the ValidateToken and then we create another ClaimsIdentity. I hoped that WIF would have used this way to construct the identity, of at least provide an overload or something to do this. I created the identity like this, since the securitytoken is now availalbe in the BootstrapToken property of the ClaimsIdentity. At first we were thinking that we had to keep the security token in session but that is not necessary when you do it like this. Now we can access it simply with the following lines of code:

var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
var theOriginalSecurityToken = identity.BootstrapToken;

I hope this helps somebody else. It would have saved me a lot of time if I could have found this information somewhere.

Update (17-8-2010)

We’ve been using the code as listed above, but we ran into some problems, because the retrieved token is a GenericXmlSecurityToken which caused problems when supplying it to our backend services.

After some searching I found that it is possible to get the BootstrapToken property filled by WIF, but you need to set a configuration switch (some decent documentation would really be helpfull). All you have to do is change the following in your web application configuration (add the saveBootstrapTokens attribute):

<microsoft.identityModel>
<service saveBootstrapTokens="true>
<issuerNameRegistry ……

The code to creating your principal is then the following:

var identity = handlers.ValidateToken(token).First();
Thread.CurrentPrincipal = new ClaimsPrincipal(new [] { identity });

Now the BootstrapperToken is a SamlSecurityToken which is exactly what we want to be able to authenticate to the backend services. I’ll show in a new post how we have all this tied together.

Advertisements

22 Comments »

  1. Good Post…

    I tried in the same way ut i et exception on statement ..

    var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;

    The exception is…
    {“Could not establish trust relationship for the SSL/TLS secure channel with authority ‘”}

    Any suggestion
    Regards

    Comment by Sharad — December 30, 2010 @ 16:43

  2. I tried in the same way ut i et exception on statement ..

    var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;

    The exception is…
    {“Could not establish trust relationship for the SSL/TLS secure channel with authority ‘”}

    Any suggestion
    Regards

    Comment by SharadK — December 30, 2010 @ 16:44

    • This error has to do with the certificate you are using for your SSL connection. It seems that it’s an invalid certificate. If it’s a self signed certificate, then you should add it to your trusted certificates using mmc.exe. A good way to check if the certificate is then valid is to navigate in your browser to the endpoint of the STS and check if you don’t have any certificate errors / warnings.

      Please let me know if this fixed it for you.

      Comment by koenwillemse — December 31, 2010 @ 00:32

  3. Its a Good Article.

    I tried the above, and I get the following error for the line

    var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;

    The error is “there was no endpoint listening at . and the inner exception is “the Remote server returned an error 404 not found.”.

    Any suggestions on how to resolve the same.

    Thanks

    Comment by Himanshu — February 1, 2011 @ 08:55

    • Resolved the error and was bale to authenticate as well.

      Thanks

      Comment by Himanshu — February 1, 2011 @ 11:22

      • How did you resolve this issue. I am having the same problem.

        Comment by Chris — March 17, 2011 @ 22:16

      • Can yo reach the endpoint using your browser? or do you get certificate error?

        Comment by koenwillemse — March 17, 2011 @ 22:26

      • I can pull it up in a browser

        Comment by Chris — March 17, 2011 @ 22:59

  4. I can see the “SecurityTokenService Service” definition screen and can access the wsdl. I was getting cert errors, but I resolved those and now i get the following through the factory channel call:

    There was no endpoint listening at http://localhost/STSService/Service.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.

    Inner Exception:
    The remote server returned an error: (404) Not Found.

    Comment by Chris — March 17, 2011 @ 23:03

    • Just happened to see the post today, I hope you have resolved this by now. In case you have not and for other people facing similar issue please check the endpoint address in web.config , in my case it was IWSTrust13, based on that we need to change the endpointaddress in the code, so the code mentioned in point 6 will change to
      var factory = new WSTrustChannelFactory(
      new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
      new EndpointAddress(“https://localhost/STSService/Service.svc/IWSTrust13”));
      which will resolve the issue.

      Comment by Himanshu — May 2, 2011 @ 12:59

  5. Hi, I am getting a timeout exception at the line channel.Issue(rst). I could not find a way to increase the timeout way config, since the binding does not seem to work in web.config.

    The service.svc comes up fine and even the wsdl.

    Any idea what is going wrong here?

    Comment by Ghanshyam — March 22, 2011 @ 15:18

    • It’s difficult to point you to a specific problem. It’s probably a ‘standard’ WCF problem. Are your bindings, endpoints etc correctly declared (both on the server and client)?

      Comment by koenwillemse — March 22, 2011 @ 20:41

  6. Hi, I got the issue fixed by hosting in IIS rather than in web development server.

    I think the last paragraph in the article below was what I would have needed to get it working:
    http://cloudythoughts.siadis.com/windows-azure/windows-azure-appfabric/creating-a-custom-sts-with-windows-identity-foundation

    Comment by Ghanshyam — March 31, 2011 @ 10:33

  7. For the exception is…
    {“Could not establish trust relationship for the SSL/TLS secure channel with authority ‘”}

    You must generate the certificate with Issue Name equal that url service STS.

    Comment by rodrijp — May 12, 2011 @ 16:59

  8. In your update of 17-8-2010: The following is not fully correct : … = new ClaimsPrincipal(new IClaimsIdentity [] { identity.Claims });

    This should be … = new ClaimsPrincipal(new [] { identity });

    Comment by William — June 10, 2011 @ 15:40

  9. I had the same problem with STS returning remote server returned an error: (404) Not Found in calling .Issue. I has setup SSL on the STS and RP..and also changed the address in the service web.config to https:// …but I needed to change the security mode from “Message” to “TransportWithMessageCredential” – it was first then that the endpoint actually changed in the WSDL.

    If in doubt ..look at the address under wsdl:port in the WSDL of the STS. ..DOOOH 🙂

    Comment by JAXN — July 15, 2011 @ 13:41

  10. Hi there, great post. We are currently trying to consume a java service to issue a token to us. Essentially we are trying to authenticate a user with an external system.
    We have tried the code implementation above and have come up short.
    Seems that the Soap call from us is much smaller and un encrypted when received by the service. We receive a “Invalid Security Header” error.

    The external system admin has created a java implementation of the same service and it seems to work well for them.

    Any suggestions of where to look for the right answer or areas to change?

    This is a c# .NET app. Eventually its supposed to be used as a ClaimsProvider for sharepoint 2010

    Any help would be awesome.

    Comment by Alla (@allaboglaeva) — October 11, 2012 @ 06:34

  11. Pretty great post. I simply stumbled upon your weblog and
    wished to mention that I have truly enjoyed browsing your weblog posts.
    After all I will be subscribing in your feed and
    I hope you write again soon!

    Comment by web24 australia — January 30, 2013 @ 11:42

  12. Great Post. I have struggled to make WS Trust work until i find this post. every other post on the internet missed the configuration part and only had the code part which is incomplete. However, Could you please post the complete configuration from the web app’s config that includes both securing the web app using STS and also have the configuration to the backend service that is also protected by the same service

    Comment by vijay — September 19, 2013 @ 04:04

  13. Have you a code example completed about this?

    Comment by javier — May 7, 2014 @ 18:16

    • Sorry, this is a long time ago, don’t have the code anymore

      Comment by koenwillemse — May 11, 2017 @ 07:19


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: