Exercise 2: Accepting Tokens from an Active Directory Federation Services (ADFS) STS

In this exercise, you will modify the service from the previous exercise for accepting tokens from an existing Active Directory Federation Services (ADFS) STS. You can expect this to be by far the most common scenario in which you will take advantage of an STS: Windows Identity Foundation makes this task very easy, thanks to its integration with Visual Studio and the use of federation metadata.

Note that in a real world scenario this task would require two steps:

  • Configuring the RP application for accepting tokens from the STS
  • Configuring the STS for issuing token for the RP application being developed

The current exercise focuses on the first step: the second step is unnecessary in our case. In order to make the lab more agile, we will take advantage of an instance of Active Directory Federation Services (ADFS) that is available through the Internet. Such an instance has been pre-provisioned with the data of the RP being used in this lab; hence, it will start issuing tokens for us as soon as we will request them. For this reason, it is of key importance that the application URI and the certificates will follow exactly what is specified in the lab instructions.

Figure 11

Invoking WeatherStationService with a token obtained from federatedidentity.net

Task 1 - Modifying the Service to Accept Tokens Issued by an STS Published via Active Directory Federation Services (ADFS)

  1. Open Microsoft Visual Studio 2010 with administrator privileges. From Start | All Programs | Microsoft Visual Studio 2010, right-click on Microsoft Visual Studio 2010 and select Run as administrator.
  2. Open the WeatherStation.sln solution file located in the %YourInstallationFolder%\Labs\WebServicesAndIdentity\Source\Ex2-OnlineSTS\Begin folder.

    Note:
    The starting solution is based on the Exercise 1 solution. It contains a Windows client application that consumes a simple WCF service which will be modified to use an online STS.

  3. On the Solution Explorer, right-click the https://localhost/WeatherStationService project and select Add STS reference.
  4. When the Federation Utility window shows up perform the following tasks for each step in the wizard.
    1. On the Welcome page click Next to continue using the pre-populated fields.

      Figure 12

      Welcome page

    2. Click Next on the list of services to configure.

      Figure 13

      Services list

    3. On the STS options page, select the third option button ("Use an existing STS"), set the STS metadata location to https://ip-sts-01.federatedidentity.net/federationmetadata/2007-06/federationmetadata.xml and click Next.

      Figure 14

      Selecting a STS option

    4. Select Disable certificate chain validation and click Next.

      Figure 15

      Certificate chain validation

    5. Select the Enable encryption option and then select the Select an existing certificate from store option.
    6. Click Select Certificate and select the certificate CN=WeatherStationService.

      Figure 16

      Using encryption with WeatherStationService certificate

      Figure 17

      Using encryption with WeatherStationService certificate

    7. Click Next.
    8. In the Offered Claims page, click Next.

      Figure 18

      Select claims window

    9. On the Summary page, review the changes and click Finish.

      Figure 19

      Summary

  5. Open the Web.config file of the https://localhost/WeatherStationService project.
  6. Add the following attribute to the system.serviceModel/bindings/ws2007FederationHttpBinding/binding/security/message element.

    (Code Snippet – Web Services And Identity Lab - Ex02 Asymmetric Key)

    XML

    <ws2007FederationHttpBinding>
    FakePre-dfd14919723148eb8ae69df8f8fc3e5f-1ca791db5e07426482dbec52c2220b1aFakePre-a3afd2940ff548769d800f6803bda4c4-d73373c270824072b5bdabb48bd58f46FakePre-2266caf7098d4225a3386764f92b2281-7df72854badb44108fa3741aa3553084 issuedKeyType="AsymmetricKey"FakePre-69ea28ecf88c4616b858f381758af843-e2f3a52f8ff34624946245f5b453f28cFakePre-7b427c3c2416434181d79b59740505fd-8c9234d741ec4d97b47b4bd9c783d011

Task 2 - Modifying the Client in Order to Secure Calls to the Service via Issued Token

  1. Update the service reference in the WeatherStationClient application. To do this, on the Solution Explorer, expand the Service References folder in the WeatherStationClient project. Right-click ServiceReference1 and select Update Service Reference.
  2. Open the app.config file from the WeatherStationClient project.
  3. Update the issuer in the service binding to use the address for username and password authentication instead of certificate-based authentication. To do this, replace the issuer element located inside system.serviceModel/bindings/customBinding/binding/security/secureConversationBootstrap/issuedTokenParameters with the following snippet.

    (Code Snippet – Web Services And Identity Lab - Ex02 UsernameMixed Issuer)

    XML

      </additionalRequestParameters>
    <issuer address="https://ip-sts-01.federatedidentity.net/adfs/services/trust/13/usernamemixed" bindingConfiguration="https://ip-sts-01.federatedidentity.net/adfs/services/trust/13/usernamemixed" binding="ws2007HttpBinding"> <identity> <certificate encodedValue="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" /> </identity> </issuer> FakePre-1f3e5c48faa24cc291cea85a93163723-0cc1ecfc5a7948a3872698eac1ff780a

  4. Save all the changes.

Task 3 - Using Claims for Authorizing the Service Call

Note:
In exercise 1, we used MyClaimsAuthorizationManager, a class in the Windows Identity Foundation SDK sample collection, to impose that only calls containing a certain claim type instance (username in our case) with a certain claim value should be allowed to successfully take place.

However, claims have much more expressive power and can describe much more than the classic username, roles and attributes; claims can be used to achieve things that would have been impossible with traditional approaches. One simple example uses claims to express attributes that have non-string values, such as dates (date of birth, expiration date, etc), numbers (spending limit, weight, etc) or even structured data. This data can be processed using criteria that are more sophisticated than the simple existence check (for example, the user has claims A with value X, or he does not): one example of such a criteria would be imposing that all the users of the web service should be older than 21. We already examined how MyClaimsAuthorizationManager leverages the CheckAccess method for embedding the existence check logic: here we will go one step further and write our own derivation of the ClaimsAuthorizationManager, implementing an engine capable of evaluating the age check described above.

  1. Right-click on the App_Code folder from the https://localhost/WeatherStationService project and select Add New Item.
  2. Create a new Class and name it AgeThresholdClaimsAuthorizationManager.cs
  3. Replace the content of the new class with the following to check access based on the DateOfBirth claim from the issued token.

    (Code Snippet – Web Services And Identity Lab - Ex02 AgeThresholdClaimsAuthorizationManager)

    C#

    namespace ClaimsBasedAuthorization { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using Microsoft.IdentityModel.Claims; public class AgeThresholdClaimsAuthorizationManager : ClaimsAuthorizationManager { private static Dictionary<string, int> _policies = new Dictionary<string, int>(); public AgeThresholdClaimsAuthorizationManager(object config) { XmlNodeList nodes = config as XmlNodeList; foreach (XmlNode node in nodes) { XmlTextReader rdr = new XmlTextReader(new StringReader(node.OuterXml)); rdr.MoveToContent(); string resource = rdr.GetAttribute("resource"); rdr.Read(); string claimType = rdr.GetAttribute("claimType"); if (claimType.CompareTo(System.IdentityModel.Claims.ClaimTypes.DateOfBirth) != 0) { throw new NotSupportedException("Only birthdate claims are supported"); } string minAge = rdr.GetAttribute("minAge"); _policies[resource] = int.Parse(minAge); } } public override bool CheckAccess(AuthorizationContext pec) { if (_policies.ContainsKey(pec.Resource.First().Value)) { int minAge = _policies[pec.Resource.First().Value]; string userBirthdate = pec.Principal.Identities[0].Claims .Where(c => c.ClaimType == System.IdentityModel.Claims.ClaimTypes.DateOfBirth) .First().Value; int userAge = DateTime.Now.Subtract(DateTime.Parse(userBirthdate)).Days / 365; if (userAge < minAge) { return false; } } return true; } } }
    FakePre-ba2dfcb6201247e7be790f2a57b8a95c-98fcd73205e94644b9939a567fded84cFakePre-b02514c60b0a43debdfbc20c0caecca0-7a09990308734d5d9659c0a6b869ed7dFakePre-754b2144406e4f089ca71a046f3a3ee9-71c36b39d70748b79147ae51ca5ca683FakePre-303455722a5240d988d9b1baaf822789-94cee39ff86e4bb3859e696b6c25dc74FakePre-2229733246a24999917b4129dd34b0f5-0fb5dae99b6141ebb318fa8ee011c1ddFakePre-02f338c96a5c40f888b6e690cd46aa62-710e00f3c20840d796585b107f9c6606FakePre-28e4adce8d7b498fa5f1c374d6ff41e6-0717b03f468d47f5a386bd201f3fb78cFakePre-18a5381e4a174e1f8e784ff8eb3c7fd2-1865a642697448aa9ab1cba887a45863FakePre-d8c883e8cc674343a67df6ff0ced0a5f-e2c31bba743848adbfd9353a6b916c59FakePre-51e2b0e16d4741b4aa79054773c0849e-ee1efc9cc82e405a825d5a8e83e704dfFakePre-710474fe0cdb449e82830f81a46c41b3-d41b45b5d58a444096c7228de8c35b16FakePre-78fe85461e49448a95ba8b99d1e42284-e7962d07baba432b84f01257bc275dac
    

    Note:
    The code above is very simple. The class constructor takes care of reading the authorization conditions from the web.config. The CheckAccess method retrieves the dateofbirth claim associated with the incoming user, and simply verifies that the corresponding age is above the threshold established by minAge. The age condition is not especially realistic in this scenario; however it should give you a good idea of how to use the object model.

  4. Open the Web.config file of the https://localhost/WeatherStationService project.
  5. Add the following settings under the microsoft.identityModel/service section with name="WeatherStationService.Service" to configure the ClaimsAuthorizationManager module to validate the age to be greater than 21 using the type AgeThresholdClaimsAuthorizationManager.

    (Code Snippet – Web Services And Identity Lab - Ex02 ClaimsAuthorizationManager configuration)

    XML

    <microsoft.identityModel>
    FakePre-98a59c26e80d4ffcb64f2a6e69c6b55d-dd7431ba4f3c4d44be7676833aa53d37FakePre-4602001efc5147029424f8cb5346f3bf-1665d147e65742afaba85428e1332b52 <claimsAuthorizationManager type="ClaimsBasedAuthorization.AgeThresholdClaimsAuthorizationManager"> <policy resource="https://localhost/WeatherStationService/Service.svc" action="GET"> <claim claimType="https://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth" minAge="21" /> </policy> </claimsAuthorizationManager>FakePre-9d2ba4bb15ca4320b859a266460f01e8-808f8ef81c274fe4b5a0d183b0ae2e2dFakePre-c54ede6369204bb2bf5dc6e1106f524f-41d3dd361a2e4aebbb3953daff3c101c

  6. Save all the changes.

Exercise 2: Verification

In order to verify that you have correctly performed all steps in the exercise, proceed as follows:

  1. On the Solution Explorer, right-click on the WeatherStationClient project and select Set as StartUp Project.
  2. To run the application, press Ctrl+F5.

    Figure 20

    Client application

  3. Enter any integer value in the ZIP code textbox.
  4. Click on Get 3 days button, when asked for credentials enter the username "frankm" and the password "p@ssw0rd".

    Figure 21

    Client application showing forecast for 3 days

  5. Click on Get 3 days button again, when asked for credentials enter the username "adamc" and the password "p@ssw0rd".

    Figure 22

    Access is denied message

    Note:
    The date of birth for the user adamc is 02/02/1998, therefore you get an "Access is denied" error message since the user's age is lower than the min age of 21.