Condividi tramite


Securing REST Services

The Premise | Goals and Requirements | Overview of the Solution | Inside the Implementation - The ACS Configuration, Implementing the Web Service, Implementing the Active Client | Setup and Physical Deployment - Configuring ADFS 2.0 for Web Services, Configuring ACS | Questions | More Information

Download code samples

Download PDF

Download Paperback

In Chapter 8, "Claims Enabling Web Services," you saw how Adatum exposed a SOAP-based web service to a client application. The client used the WS-Trust active federation protocol to obtain a token containing the claims that it needed to access the web service. The scenario that this chapter describes is similar, but differs in that the web service is REST-based rather than SOAP-based. The client must now send a Simple Web Token (SWT) containing the claims to the web service using the OAuth protocol instead of a SAML token using the WS-Trust protocol. The client will obtain an SWT token from Microsoft Azure™ AppFabric Access Control services (ACS) v2.

Like Chapter 8, "Claims Enabling Web Services," this chapter describes an active scenario. In an active scenario, the client application actively contacts all issuers in a trust chain; these issuers are typically an identity provider (IdP) and a federation provider (FP). The client application communicates with the identity provider and federation provider to get and transform the tokens that it requires to access the relying party (RP) application.

The client application must actively call all the issuers in the trust chain.

In this chapter, you'll see an example of a Windows® Presentation Foundation (WPF) smart client application that uses federated identity. In Chapter 8, "Claims Enabling Web Services," the Windows Communication Foundation (WCF) bindings determined how the client application called the issuers in the trust chain; in this chapter, you'll see how the client must call the identity provider and federation provider programmatically because WCF does not support the calling of RESTful web services.

The Premise

Litware wants to write an application that can read the status of its orders directly from Adatum. To satisfy this request, Adatum agrees to provide a web service called a-Order.OrderTracking.Services that users at Litware can access by using a variety of client applications over the Internet.

Adatum and Litware have already done the work necessary to establish federated identity, and they both have issuers capable of interacting with active clients. The necessary communications infrastructure, which includes firewalls and proxies, is in place. To review these elements, see Chapter 4, "Federated Identity for Web Applications."

Hh446531.note(en-us,PandP.10).gifBharath Says:
Bharath If Active Directory® Federation Services (ADFS) 2.0 is used, you'll get support for federated identity with active clients as a standard feature.

Now, Adatum only needs to expose a claims-aware web service on the Internet. Litware will invoke Adatum's web service from within its client application. Because the client application runs in Litware's security realm, it can use Microsoft® Windows® authentication to establish the identity of the user and then use this identity to obtain a token it can pass along to Adatum's federation provider. In this scenario Adatum uses ACS as its federation provider.

Goals and Requirements

Both Litware and Adatum see benefits in a collaboration based on claims-aware web services. Litware wants programmatic access to Adatum's a-Order application. Adatum does not want to be responsible for authenticating any people or resources that belong to another security realm. For example, Adatum doesn't want to keep and maintain a database of Litware users.

Active clients use claims to get access to remote services.

Both Adatum and Litware want to reuse the existing infrastructure as much as possible. For example, Adatum wants to enforce permissions for its web service with the same rules it has for the browser-based web application. In other words, the browser-based application and the web service will both use roles for access control.

Adatum has decided to expose the a-Order order tracking data as a RESTful web service to expand the range of clients that can access the application. Adatum anticipates that partners will implement client applications on mobile platforms; in these environments partners will prefer a lightweight REST API to a SOAP-based API.

Hh446531.note(en-us,PandP.10).gifJana Says:
Jana SWT tokens are smaller than SAML tokens because they do not include any XML markup. It is also much easier to manipulate SWT tokens in JavaScript, making SWT the preferred token format for rich JavaScript clients.

Overview of the Solution

Figure 1 gives an overview of the proposed solution.

Hh446531.bba521f5-5c3a-42e4-bbf5-9b22522c9bf6-thumb(en-us,PandP.10).png

Figure 1

Federated identity with a smart client

The diagram presents an overview of the interactions and relationships among the different components. It is similar to the diagrams you saw in the previous chapters.

Litware has a single client application based on Windows Presentation Foundation (WPF) deployed on Litware employees' desktops. Rick, a Litware employee, uses this application to track orders with Adatum.

Adatum exposes a RESTful web service on the Internet. This web service expects to receive Simple Web Token (SWT) tokens that it will use to implement authorization rules in the a-Order application. In order to access this service, the client must present an SWT token from the Adatum ACS instance.

The sequence shown in the diagram proceeds as follows:

  1. The Litware WPF application uses Rick's credentials to request a security token from the Litware issuer. The Litware issuer authenticates Rick and, if the authentication succeeds, it returns a Group claim with the value Sales because Rick is in the sales organization. The Litware issuer returns a SAML token to the client application.
  2. The WPF application then forwards the SAML token to ACS (the Adatum federation provider), which trusts the Litware issuer.
  3. ACS, acting as a federation provider, transforms the claim Group:Sales into Role:Sales and adds a new claim, Organization:Litware. The transformed claims are the ones required by the Adatum a-Order RESTful web service. These are the same rules that were defined in the browser-based scenario. ACS also transitions the incoming SAML token to an SWT token that it returns to the client WPF application. The interaction between the client application and ACS uses the OAuth protocol.
    Hh446531.note(en-us,PandP.10).gifBharath says:
    Bharath It's also possible to wrap SWT tokens in the WS-Trust and WS-Federation protocols by using a BinarySecurityTokenElement.
    4. Finally, the WPF application sends the web service the request for the order tracking data. This request includes the SWT token obtained in the previous step. The web service uses the claims in the token to implement its authorization rules.

    This sequence is a bit different from the scenario described in Chapter 8, "Claims Enabling Web Services." In this scenario, the federation provider is an ACS instance that performs token format transition from SAML to SWT in addition to mapping the claims from the identity provider into claims that the relying party expects to see.

    Inside the Implementation

    Now is a good time to walk through some of the details of the solution. As you go through this section, you may want to download the Visual Studio® development system solution called 8ActiveRestClientFederation from https://claimsid.codeplex.com. If you are not interested in the mechanics, you should skip to the next section.

    WCF does not provide built-in support for REST on the client or for SWT on the server so this sample requires more code than you saw in Chapter 8, "Claims Enabling Web Services."

    The following sections describe some of the key parts of the implementation of the active client, the RESTful web service, and ACS.

    The ACS Configuration

    In this scenario, in addition to handling the claims mapping rules, ACS is also responsible for transitioning the incoming token from the Litware identity provider from the SAML format to the SWT format. This is partially a configuration task, but the active client application must be able to receive an SWT token from ACS. For more details, see the section, "Implementing the Active Client," later in this chapter.

    The configuration step in ACS is to ensure that the token format for the aOrderService relying party is set to SWT. This makes sure that ACS issues an SWT token when it receives a token from any of the identity providers configured for the aOrderService relying party.

    Implementing the Web Service

    In this scenario, Adatum exposes the order-tracking feature of the a-Order application as a RESTful web service. The following snippet from the Web.config file shows how the application defines the HTTP endpoint for the service.

    <services>
      <service name="AOrder.OrderTracking.Services.OrderTrackingService" 
               behaviorConfiguration="serviceBehavior">
        <endpoint
            address=""
            binding="webHttpBinding"
            contract="AOrder.OrderTracking.Contracts.IOrderTrackingService"
            behaviorConfiguration="orders" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="serviceBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="orders">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    
    Hh446531.note(en-us,PandP.10).gifMarkus Says:
    Markus In this scenario, the web service does not use Windows Identity Foundation (WIF) to handle the incoming tokens. However, the service does use WIF for some claims processing; for example, it uses it in the CustomClaimsAuthorizationManager class. You will see the details in the microsoft.identityModel section in the Web.config file.

    The Global.asax file contains code to route requests to the service definition. The following code sample from the Global.asax.cs file shows the routing definition in the service.

    protected void Application_Start(object sender, EventArgs e)
    {
      RouteTable.Routes.Add(new ServiceRoute("orders", 
        new WebServiceHostFactory(), typeof(OrderTrackingService)));
    }
    

    The Adatum a-Order application must also extract the claims information from the incoming SWT token. The application uses the claims to determine the identity of the caller and the roles that the caller is a member of in order to apply the authorization rules in the application. The following code sample from the OrderTrackingService class shows how the GetOrdersFromMyOrganization method retrieves the current user's organization claim to use when it fetches a list of orders from the order repository.

    public Order[] GetOrdersFromMyOrganization()
    {
      string organization = ClaimHelper.GetClaimsFromPrincipal(
        HttpContext.Current.User, Adatum.ClaimTypes.Organization).Value;
    
      var repository = new OrderRepository();
      return repository.GetOrdersByCompanyName(organization).ToArray();
    }
    

    This method retrieves a claim value from the IClaimsPrincipal object. In the scenarios described in previous chapters, WIF has been responsible for populating the IClaimsPrincipal object with claims from a SAML token: in the current scenario, we are using SWT tokens and the OAuth protocol, which are not directly supported by WIF. The Visual Studio solution, 8ActiveRestClientFederation, includes a project called DPE.OAuth that implements an extension to WIF to provide support for SWT tokens and the OAuth protocol.

    The following snippet from the Web.config file in the a-Order.OrderTracking.Services.8 project shows how Adatum installed the modules for the extension to WIF.

    Note

    In addition to the extension module, Microsoft.Samples.DPE.OAuth.ProtectedResource.ProtectedResourceModule, it's necessary to install the standard WSFederationAuthenticationModule and SessionAuthenticationModule modules.

    …
    <configSections>
      <section name="microsoft.identityModel" 
        type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, 
        Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    …
    <system.webServer>
      <validation validateIntegratedModeConfiguration="false" />
      <modules runAllManagedModulesForAllRequests="true">
        <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, 
          System.Web, Version=4.0.0.0, Culture=neutral, 
          PublicKeyToken=b03f5f7f11d50a3a" />
        <add name="ProtectedResourceModule" 
          type="Microsoft.Samples.DPE.OAuth.ProtectedResource.ProtectedResourceModule, 
          Microsoft.Samples.DPE.OAuth, Version=1.0.0.0, Culture=neutral" />
        <add name="WSFederationAuthenticationModule" 
          type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, 
          Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
        <add name="SessionAuthenticationModule" 
          type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, 
          Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
      </modules>
    </system.webServer>
    

    You use the microsoft.identityModel section to configure the extension module to handle SWT tokens and the OAuth protocol.

    <microsoft.identityModel>
      <service name="OAuth">
        <audienceUris>
          <add value="https://localhost/a-Order.OrderTracking.Services.8" />
        </audienceUris>
        <claimsAuthorizationManager 
          type="AOrder.OrderTracking.Services.CustomClaimsAuthorizationManager, 
          AOrder.OrderTracking.Services.8, Culture=neutral" />
        <securityTokenHandlers>
          <add type="Microsoft.Samples.DPE.OAuth.Tokens.SimpleWebTokenHandler, 
            Microsoft.Samples.DPE.OAuth" />
        </securityTokenHandlers>
        <issuerTokenResolver 
          type="Microsoft.Samples.DPE.OAuth.ProtectedResource
         .ConfigurationBasedIssuerTokenResolver, Microsoft.Samples.DPE.OAuth">
          <serviceKeys>
            <add serviceName="https://localhost/a-Order.OrderTracking.Services.8" 
              serviceKey="lJFL02dwy9n3rCe2YEToblDFHdZmbecmFK1QB88ax7U=" />
          </serviceKeys>
        </issuerTokenResolver>
        <issuerNameRegistry type="Microsoft.Samples.DPE.OAuth.ProtectedResource
          .SimpleWebTokenTrustedIssuersRegistry, Microsoft.Samples.DPE.OAuth">
          <trustedIssuers>
            <add issuerIdentifier="https://aorderrest-dev.accesscontrol.windows.net/" 
              name="aOrder" />
          </trustedIssuers>
        </issuerNameRegistry>
      </service>
    </microsoft.identityModel>
    

    This section also configures a custom claims authorization manager that Adatum uses to apply custom authorization rules in the service. The following code example shows how the service implements the custom claims authorization manager class that checks the caller's role membership and the resource the caller is requesting. The IOrderTrackingService interface defines the mapping from the paths "/all" and "/frommyorganization" to the service methods GetAllOrders and GetOrdersFromMyOrganization.

    public class CustomClaimsAuthorizationManager : ClaimsAuthorizationManager
    {
      public override bool CheckAccess(AuthorizationContext context)
      {
        Claim actionClaim =
          context.Action.Where(x => x.ClaimType == ClaimTypes.Name).FirstOrDefault();
        Claim resourceClaim =
          context.Resource.Where(x => x.ClaimType == ClaimTypes.Name).FirstOrDefault();
        IClaimsPrincipal principal = context.Principal;
    
        var resource = new Uri(resourceClaim.Value);
        string action = actionClaim.Value;
    
        if (action == "GET" && resource.PathAndQuery.Contains("/frommyorganization"))
        {
          if (!principal.IsInRole(Adatum.Roles.OrderTracker))
          {
            return false;
          }
        }
    
        if (action == "GET" && resource.PathAndQuery.Contains("/all"))
        {
          if (!principal.IsInRole(Adatum.Roles.OrderApprover))
          {
            return false;
          }
        }
    
        return true;
      }
    }
    
    Hh446531.note(en-us,PandP.10).gifMarkus Says:
    Markus You can also use a custom ClaimsAuthenticationManager class to modify the set of claims attached to the IClaimsPrincipal object in the context.

    To find out more about authorization strategies, take a look at Appendix G, "Authorization Strategies."

    Implementing the Active Client

    The ACS configuration ensures that the token format for the Adatum a-Order relying party application is set to SWT. ACS issues an SWT token when it receives a token from any of the identity providers configured for the Adatum a-Order relying party (the client obtains the token from the identity provider and sends it ACS). The client application uses a custom endpoint behavior to intercept all outgoing requests; the behavior obtains the token that the relying party requires and attaches it to the request. Figure 2 shows an overview of this process.

    Hh446531.d2aacd97-4f91-4161-b8d1-dff9da1b38ec-thumb(en-us,PandP.10).png

    Figure 2

    Attaching an SWT token to the outgoing request

    The sequence shown in Figure 2 proceeds as follows.

    1. The service client, the OrderTrackingServiceClient class, attaches a new behavior to the channel endpoint. This CustomHeaderBehavior behavior class instantiates a custom message inspector that has access to every outgoing request on the channel.
    2. The client application invokes the GetOrdersForMyOrganization method that sends a request to the a-Order order tracking Service.
    3. The CustomHeaderMessageInspector class intercepts the message before it is sent.
    4. The CustomHeaderMessageInspector class requests a SAML token from the Litware identity provider.
    5. The CustomHeaderMessageInspector class sends the SAML token to ACS and receives an SWT token.
    6. The CustomHeaderMessageInspector class attaches the SWT token to the outgoing message header.
    Hh446531.note(en-us,PandP.10).gifMarkus Says:
    Markus The inspector caches the SWT token to avoid having to revisit the identity provider and ACS for every request to the a-Order application. The sample caches the token for 30 seconds, but you should adjust this to a suitable value for your application.

    Note

    Adatum chose to use WCF in the client to manage the call to the REST-based service rather than the WebClient or HttpWebRequest classes because it was a convenient way to attach the SWT token. For an example that uses the HttpWebRequest class (because WCF is not available on the Windows® Phone 7 platform), see Chapter 10, "Accessing REST Services from a Windows Phone Device."

    Although WIF does not provide full support for REST-based web services, the sample client application uses WIF to handle some of the token processing. This reduces the amount of code required to implement this sample client application. One of the reasons for using a RESTful web service is to support other client environments, and Chapter 10, "Accessing REST Services from a Windows Phone 7 Device," shows you how to implement a client application without using WIF.

    The inspector must first obtain a SAML token from the identity provider. The following code example from the CustomHeaderMessageInspector class shows how the a-Order.OrderTracking.Client application uses WIF to perform this task. This method takes three arguments; the service endpoint, the STS endpoint, and the user's credentials.

    private static SecurityToken GetSamlToken(
      string realm, string stsEndpoint, ClientCredentials clientCredentials)
    {
      using (var factory = new WSTrustChannelFactory(
        new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), 
        new EndpointAddress(new Uri(stsEndpoint))))
      {
        factory.Credentials.UserName.UserName = clientCredentials.UserName.UserName;
        factory.Credentials.UserName.Password = clientCredentials.UserName.Password;
    
        factory.TrustVersion = TrustVersion.WSTrust13;
    
        WSTrustChannel channel = null;
    
        try
        {
          var rst = new RequestSecurityToken
                        {
                          RequestType = WSTrust13Constants.RequestTypes.Issue, 
                          AppliesTo = new EndpointAddress(realm), 
                          KeyType = KeyTypes.Bearer, 
                        };
    
          channel = (WSTrustChannel)factory.CreateChannel();
    
          return channel.Issue(rst);
        }
        finally
        {
          if (channel != null)
          {
            channel.Abort();
          }
    
          factory.Abort();
        }
      }
    }
    

    Note

    The token request specifies a bearer token; ACS expects to receive a bearer token and not a holder-of-key token. For this reason it's important to use Secure Sockets Layer (SSL) to secure the connections between the client application and the identity provider, and between the client application and ACS in order to mitigate the threat of a man-in-the-middle attack.

    The inspector can then send the SAML token to ACS. The following code example from the CustomHeaderMessageInspector class shows how the client application sends the SAML token to ACS and receives the SWT token in return. The application uses the OAuth protocol to communicate with ACS.

    private static NameValueCollection GetOAuthToken(string xmlSamlToken, string serviceEndpoint, string acsRelyingParty)
    {
        var values = new NameValueCollection
                            {
                              { "grant_type", "urn:oasis:names:tc:SAML:2.0:assertion" }, 
                              { "assertion", xmlSamlToken },
                              { "scope", acsRelyingParty }
                            };
    
        var client = new WebClient { BaseAddress = serviceEndpoint };
        byte[] acsTokenResponse = client.UploadValues("v2/OAuth2-13", "POST", values);
        string acsToken = Encoding.UTF8.GetString(acsTokenResponse);
        var tokens = new NameValueCollection();
        var json = new JavaScriptSerializer();
        var parsed = json.DeserializeObject(acsToken) as Dictionary<string, object>;
        foreach (var item in parsed)
        {
            tokens.Add(item.Key, item.Value.ToString());
        }
    
        return tokens;
    }
    

    The inspector attaches the SWT token in the Authorization header in the HTTP request message that the client application is sending to the a-Order order tracking service. The following code example shows how the client application performs this task in the BeforeSendRequest method.

    var oauthAuthorizationHeader =
      string.Format("OAuth {0}", oauthToken["access_token"]);
    httpRequestMessageProperty.Headers.Add(
      HttpRequestHeader.Authorization, oauthAuthorizationHeader);
    

    The SWT token expiry time is accessible in the response from ACS and the code in the sample checks the expiry time on the SWT token before attaching it to the outgoing request. With a SAML token, the expiry time is in the token (not part of the response); if the issuer encrypts the SAML token, the client application may not have access to the contents of this token. In this solution, the client application simply forwards the SAML token on to ACS.

    You can read the expiry time of a SAML token using the following code:

    var rst = new RequestSecurityToken
                    {
                        RequestType = WSTrust13Constants.RequestTypes.Issue, 
                        AppliesTo = new EndpointAddress(realm), 
                        KeyType = KeyTypes.Bearer, 
                    };
    channel = (WSTrustChannel)factory.CreateChannel();
    RequestSecurityTokenResponse response;
    var token = channel.Issue(rst, out response);
    var expires = response.Lifetime.Expires.Value; 
    

    Setup and Physical Deployment

    By default, the web service uses the local host for all components. In a production environment, you would want to use separate computers for the client, the web service, the federation provider, and the identity provider.

    To deploy this application, you must substitute the mock issuer with a production-grade component such as ADFS 2.0 that supports active clients. You must also adjust the settings in the client application's App.config file to account for the new server names: the addresses for the identity provider and ACS are located in the appSettings section.

    Remove the mock issuer during deployment.

    Note that neither the client nor the web service needs to be recompiled to be deployed to a production environment unless you are changing the ACS service namespace that your solution uses; in this case, you must update the service namespace name and key in the CustomServiceHostFactory class in the a-Order order tracking web service.

    Configuring ADFS 2.0 for Web Services

    In the case of ADFS 2.0, you enable the endpoints using the Microsoft Management Console (MMC).

    To obtain a token from the Litware issuer, you could use the UsernameMixed or WindowsMixed endpoint. UsernameMixed requires a user name and password to be sent across the wire, while WindowsMixed works with the Windows credentials. Both endpoints will return a SAML token.

    Note

    The "Mixed" suffix indicates that the endpoint uses transport security (based on HTTPS). For integrity and confidentiality, client credentials are included in the header of the SOAP message.

    Configuring ACS

    As a minimum, you should configure the aOrderService relying party in ACS to issue name and organization claims. If you implement any additional authorization rules, you should ensure that ACS issues any additional claims that your rules require.

    Note

    To avoid the risk of a partner spoofing an organization name in a token, you should configure ACS to generate the organization claim and not simply pass it through from the identity provider.

    Questions

    1. In the scenario described in this chapter, which of the following statements best describes what happens the first time that the smart client application tries to use the RESTful a-Order web service?
      1. It connects first to the ACS instance, then to the Litware IP, and then to the a-Order web service.
      2. It connects first to the Litware IP, then to the ACS instance, and then to the a-Order web service.
      3. It connects first to the a-Order web service, then to the ACS instance, and then to the Litware IP.
      4. It connects first to the a-Order web service, then to the Litware IP, and then to the ACS instance.
    2. In the scenario described in this chapter, which of the following tasks does ACS perform?
      1. ACS authenticates the user.
      2. ACS redirects the client application to the relying party.
      3. ACS transforms incoming claims to claims that the relying party will understand.
      4. ACS transitions the incoming token format from SAML to SWT.
    3. In the scenario described in this chapter, the Web.config file in the a-Order web service does not contain a <microsoft.identity> section. Why?
      1. Because it configures a custom ServiceAuthorizationManager class to handle the incoming SWT token in code.
      2. Because it is not authenticating requests.
      3. Because it is not authorizing requests.
      4. Because it is using a routing table.
    4. ACS expects to receive bearer tokens. What does this suggest about the security of a solution that uses ACS?
      1. You do not need to use SSL to secure the connection between the client and the identity provider.
      2. You should use SSL to secure the connection between the client and the identity provider.
      3. The client application must use a password to authenticate with ACS.
      4. The use of bearer tokens has no security implications for your solution.
    5. You should use a custom ClaimsAuthorizationManager class for which of the following tasks.
      1. To attach incoming claims to the IClaimsPrincipal object.
      2. To verify that the claims were issued by a trusted issuer.
      3. To query ACS and check that the current request is authorized.
      4. To implement custom rules that can authorize access to web service methods.

    More Information

    To learn more about proof tokens and bearer tokens, see the blog posts at: https://blogs.msdn.com/b/vbertocci/archive/2008/01/02/on-prooftokens.aspx and http://travisspencer.com/blog/2009/02/what-is-a-proof-key.html.

    For more information about the DPE.OAuth project used in this solution, see: https://www.fabrikamshipping.com/.

    Next | Home