Dec 6, 2010

A custom STS in .NET, Part 5: Getting active

In order for your STS to work with web services (in particular, WCF ones), you're going to need to expose it another way besides the standard web page handling passive requests. Whether you implemented the passive case or not, be sure you've implemented some custom Claims filling logic since the same code will run regardless of whether it was initiated passively or actively.

Basically, services will be created in other apps to require your STS service for authentication (and claims with it). This happens and can be set up entirely separately from WIF so your other apps need not reference the DLL nor have any idea that it is being used. The applications using the service will then know to call your special STS service first to get a token and then use that to call whatever service in question. This is why it is active: the app, being a much thicker client than a web browser, knows the deal up front, whereas browsers must passively get redirected around from the target site to the STS and back again.

When designing the service, you'll use your trusted FedUtil to set up the STS reference and point it to the STS service we're about to create. It's pretty easy, but first we need an STS service to point to.

Returning to our STS Web application project, you'll want to add a new WCF Service. I've lovingly called mine STSService.svc (shocker!). Start by deleting the .cs file it generates for it and opening up the markup of the .svc file. Gut it and leave only the following:

<%@ ServiceHost Language="C#" Debug="true" Factory="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceHostFactory" Service="CustomSecurityTokenServiceConfiguration" %>


The reason we've stripped the service code so much is because it's already made for us, as referenced by the Factory and Service attributes. The WIF assembly gives us a factory method to generate the service we want and the Service attribute should point to the custom configuration class copied over long ago (if you've changed namespaces at all you'll need to make sure they are included; the code above assumes the CustomSecurityTokenServiceConfiguration class is not in any namespace).



Most of the work will be in the web.config, where we have to define a lot of service-related stuff. If you've worked at all with WCF before, most of this will be quite familiar. Start by defining the serviceModel section if it hasn't been already.



	<system.serviceModel>
<services>
<service name="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract" behaviorConfiguration="stsBehavior">
<endpoint address="IWSTrust13" binding="ws2007HttpBinding" bindingConfiguration="ws2007HttpBindingConfiguration" contract="Microsoft.IdentityModel.Protocols.WSTrust.IWSTrust13SyncContract" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/stsweb/Services/STSService.svc" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<ws2007HttpBinding>
<binding name="ws2007HttpBindingConfiguration">
<security mode="Message">
<message clientCredentialType="Windows" establishSecurityContext="false" />
</security>
</binding>
</ws2007HttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="stsBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<windowsAuthentication includeWindowsGroups="true" allowAnonymousLogons="false" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>


It's important that the behavior defined use the windowsAuthentication block since we'll be relying on that to determine who's calling the service. The binding should also specify Windows as the clientCredentialType. You might need to change the baseAddress in the endpoint configuration to match your STS's correct URL. Also don't forget to turn off some of the debug things when you're done testing, like the inlcudeExceptionDetailInFaults attribute.



One of the important differences when fulfilling claims data in the active scenario is that you'll potentially be able to make use of the RST more so than in the passive. In passive cases, the RST is mostly default empty values, but in the active case an RST is actually issued by the calling app. This means, depending on how it was set up, you might be able to glean more info about who's calling than you could in the passive case. Recall that for passive applications we needed to set up custom settings sections in the web config and compare them to a parsed URI. In the active case, you can hopefully rely on the RST specifying the claims it wants for you, and thus not need to have them defined elsewhere.



The properties of interest are request.Claims and request.SecondaryParameters.Claims. Either contains a list of RequestClaim objects that, you guessed it, have a claim type we can use in much the same way as the collection of claim types from the config. From there the logic to fulfilling them is pretty much the same. Query your DBs and ADs to get the data you need to return.



That just about does it for a really basic active STS service. Thankfully, most of the code to handle claims is shared entirely between the passive and active cases. Determining which case you’re in is usually a simple as snooping through the RST, which isn't utilized in the passive case.

No comments:

Post a Comment