Red Hat Training

A Red Hat training course is available for Red Hat Fuse

9.3. Enabling Claims in the STS

Demonstration location

The sample code in this section is taken from an STS system test. If you download and install the source distribution of Apache CXF, you can find the system test Java code under the following directory:
CXFInstallDir/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts
And the system test resource files under the following directory:
CXFInstallDir/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts

What is a claim?

A claim is an additional piece of data (for example, e-mail address, telephone number, and so on) about a principal, which can be included in a token along with the basic token data. Because this additional data is subject to signing, verification, and authentication, along with the rest of the token, the recipient can be confident that this data is true and accurate.

Requesting claims in an IssuedToken policy

If you want to issue a token with claims embedded, you can add a WS-Trust Claims element to the RequestSecurityTokenTemplate part of the issued token policy, as follows:
<sp:IssuedToken
      sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
    <sp:RequestSecurityTokenTemplate>
        <t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</t:TokenType>
        <t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</t:KeyType>
        <t:Claims Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity"
                  xmlns:ic="http://schemas.xmlsoap.org/ws/2005/05/identity">
            <ic:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email"/>
            <ic:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"/>
            <ic:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/phone"
                          Optional="true"/>
        </t:Claims>
    </sp:RequestSecurityTokenTemplate>
    <wsp:Policy>
        <sp:RequireInternalReference />
    </wsp:Policy>
    <sp:Issuer>
        <wsaw:Address>http://localhost:8080/SecurityTokenService/UT</wsaw:Address>
    </sp:Issuer>
</sp:IssuedToken>
By adding the Claims element to the RequestSecurityTokenTemplate element, you ensure that the STS client includes the specified claims in the token issue request that is sent to the STS. The STS responds to this request by retrieving the relevant claim data for the principal and embedding it into the issued token.

Processing claims

Figure 9.9, “Processing Claims” shows an overview of the steps that the STS performs to process claims received in an issue token request.

Figure 9.9. Processing Claims

Steps to process claims

The STS processes claims as follows:
  1. One of the first things the TokenIssueOperation must do is to prepare for parsing the incoming request message.
    If a ClaimsManager object is registered with the TokenIssueOperation, the TokenIssueOperation invokes getClaimsParsers on the ClaimsManager instance, to obtain the list of available claims parsers.
  2. The TokenIssueOperation initiates parsing of the request message by invoking the parseRequest method on the RequestParser object, passing the list of ClaimsParser objects as one of the arguments to parseRequest. This ensures that the RequestParser is capable of parsing any Claims elements that might appear in the request message.
  3. If no claims parsers are configured on the claims manager (so that list of claims parsers is null), the RequestParser tries the IdentityClaimsParser claims parser by default. But the IdentityClaimsParser is applied to the Claims element, only if the Dialect attribute of the Claims element is equal to the identity claims dialect URI.
  4. After parsing the request message, the TokenIssueOperation tries to find the appropriate token provider, by calling canHandleToken on each of the registered token providers.
  5. In the current scenario, we assume that the client has requested the STS to issue a SAML token, so that the SAMLTokenProvider is selected to issue the token. The TokenIssueOperation invokes createToken on the SAMLTokenProvider.
  6. Before proceeding to issue the token, the SAMLTokenProvider checks whether handlers are available to process all of the non-optional claims. If the required claim handlers are not available, an exception is raised and the SAML token is not issued.
    For example, in the identity claims dialect, a claim can be tagged as non-optional by setting the Optional attribute to false on a ClaimsType element in the IssuedToken policy, as follows:
    <t:Claims Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity"
              xmlns:ic="http://schemas.xmlsoap.org/ws/2005/05/identity">
        <ic:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email"
                      Optional="false"/>
        ...
    </t:Claims>
    Important
    In the identity claims dialect, all claims are required (that is, non-optional) by default.
  7. When specifying the list of SAML attribute statement providers explicitly, it is good practice to include the DefaultAttributeStatementProvider instance in the list, so that the default token issuing behavior of the SAMLTokenProvider is preserved.
  8. In this example, the CustomAttributeStatementProvider encapsulates the code that embeds the requisite claim values into the issued SAML token. The SAMLTokenProvider invokes the getStatement method to obtain the SAML attribute statements containing the required claim values.
  9. The CustomAttributeStatementProvider obtains the claim values for the current principal, by invoking the retrieveClaimValues method on the ClaimsManager object.
    For example, if the request message included claims for the principal's e-mail address and phone number, it is at this point that the STS actually retrieves the principal's e-mail address and phone number.
  10. The ClaimsManager retrieves the claim values by iterating over all of the claims handlers, where each claims handler returns data for as many claims as it can.
    A claims handler implementation is effectively an intermediate layer between the ClaimsManager and a database. The database stores secure data about each user—such as, address, e-mail, telephone number, department, and so on—which can be used to populate claim values. For example, the database could be an LDAP server and Apache CXF provides an LdapClaimsHandler class for this scenario—see the section called “The LdapClaimsHandler”.
  11. After retrieving all of the claim values, the CustomAttributeStatementProvider proceeds to repackage the claim values as attribute statements, so that they can be embedded in the issued SAML token.

Claim dialects

In order to be as extensible and flexible as possible, the WS-Trust claims mechanism is designed to be pluggable and does not define the syntax of claims. That is, the contents of a WS-Trust Claims element is left unspecified by WS-Trust.
The detailed syntax of claims can be defined in third-party specifications, by defining a claim dialect. The Claim element allows you to specify the claim dialect in the Dialect attribute, as follows:
<t:Claims Dialect="DialectURI" xmlns:DialectPrefix="DialectURI">
    ...
</t:Claims>
You can then use the specified dialect to specify claims inside the Claims element.
For example, some of the claim dialects defined by the Oasis open standards foundation are as follows:
  • Identity claim dialect—defines the kind of data that is typically associated with a user account (for example, address, e-mail, telephone number) and is specified by the Identity Metasystem Interoperability Specification.
  • Common claim dialect(not supported) defines data that is used in WS-Federation and is specified by the WS-Federation Specification. Apache CXF does not provide an implementation of this claims dialect, but you could plug in a custom implementation to the STS, if you wish.
  • XSPA claim dialect(not supported) defines a claim dialect that is used in Cross-Enterprise Security and Privacy Authorization XSPA Specification, which is a security standard used in the context of healthcare organizations. Apache CXF does not provide an implementation of this claims dialect, but you could plug in a custom implementation to the STS, if you wish.

Identity claim dialect

The identity claim dialect is supported by default in Apache CXF. It enables you to request the kind of data fields that are typically stored under a user's LDAP account—for example, address details, telephone number, department, role, and so on. The identity claim dialect is associated with the following dialect URI:
http://schemas.xmlsoap.org/ws/2005/05/identity
You can specify identity claims using the following syntax:
<t:Claims Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity"
          xmlns:ic="http://schemas.xmlsoap.org/ws/2005/05/identity">
    <ic:ClaimType Uri="ClaimTypeURI" Optional="[true|false]"/>
    ...
</t:Claims>
The identity claim dialect defines a single element, ic:ClaimType, which has the following attributes:
Uri
Specifies the type of claim value that you want to include in the issued token. For example, the ClaimTypeURI might identify an e-mail address claim value, a phone number claim value, and so on.
Optional
Specifies whether or not this particular claim is optional or not. Setting to true means that the STS must be capable of populating the issued token with the claim value for the principal, otherwise the token cannot be issued. Default is true.

Claim type URIs for the identity claim dialect

The identity claim dialect supports the following claim type URIs:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
The subject's first name.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
The subject's surname.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
The subject's e-mail address.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress
The subject's street address.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality
The subject's locality, which could be a city, county, or other geographic region.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince
The subject's state or province.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode
The subject's postal code.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country
The subject's country.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone
The subject's home phone number.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone
The subject's secondary phone number (for example, at work).
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone
The subject's mobile phone number.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth
The subject's date of birth.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender
The subject's gender.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier
The subject's Private Personal Identifier (PPID). The PPID is described in detail in the Identity Metasystem Interoperability Oasis standard.
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage
The subject's Web page.

Claims parsers

Because WS-Trust claims have a pluggable architecture, you need a pluggable architecture for parsing claims. The STS allows you to configure a list of claims parsers to customize support for claims. Typically, you register a claims parser for each claim dialect you want to support.

The IdentityClaimsParser

By default, the STS provides a single claims parser implementation: the identity claims parser, org.apache.cxf.sts.claims.IdentityClaimsParser, which can parse the identity claim dialect.
You can optionally configure the identity claims parser explicitly, by registering it with the ClaimsManager instance. But this is not strictly necessary, because the request parser automatically defaults to the identity claims parser, even if you have not explicitly configured it.

Implementing a custom claims parser

You can extend the claims parsing capability of the STS by implementing a custom claims parser. For this, you would define a custom Java class that implements the following Java interface:
// Java
package org.apache.cxf.sts.claims;

import org.w3c.dom.Element;

public interface ClaimsParser {

    /**
     * @param claim Element to parse claim request from
     * @return RequestClaim parsed from claim
     */
    RequestClaim parse(Element claim);

    /**
     * This method indicates the claims dialect this Parser can handle.
     * 
     * @return Name of supported Dialect
     */
    String getSupportedDialect();

}

Claims handlers

The purpose of a claims handler is to retrieve the requested claim values for the specified principal. Typically, a claims handler is an intermediate layer that looks up claim values in persistent storage.
For example, suppose that an incoming request includes claims for an e-mail address and a phone number (the request claims). When the STS is ready to start populating the issued token with claim values, it calls on the registered claims handlers to retrieve the required claim values for the specified principal. If the principal is the user, Alice, for example, the claims handler would contact a database to retrieve Alice's e-mail address and phone number.

The LdapClaimsHandler

Apache CXF provides the claims handler, org.apache.cxf.sts.claims.LdapClaimsHandler, which is capable of retrieving claim values from an LDAP server.

Implementing a custom claims handler

You can provide a custom claims handler by defining a class that implements the following Java interface:
// Java
package org.apache.cxf.sts.claims;

import java.net.URI;
import java.security.Principal;
import java.util.List;

import javax.xml.ws.WebServiceContext;

/**
 * This interface provides a pluggable way to handle Claims.
 */
public interface ClaimsHandler {

    List<URI> getSupportedClaimTypes();

    ClaimCollection retrieveClaimValues(
            RequestClaimCollection claims,
            ClaimsParameters parameters);

    @Deprecated
    ClaimCollection retrieveClaimValues(
            Principal principal,
            RequestClaimCollection claims,
            WebServiceContext context,
            String realm);
}

Configuring the ClaimsManager

The ClaimsManager class encapsulates most of the functionality required to support claims and you must configure it if you want to support claims in the STS. In particular, the claims manager encapsulates a list of claims parsers and a list of claims handlers. In practice, if you are using just the identity claims dialect, there is no need to configure the list of claims parsers explicitly; it is sufficient to configure just the list of claims handlers.
For example, the following Spring XML fragment shows how to register a ClaimsManager instance with the TokenIssueOperation bean, where the claims manager is initialized with a claims handler list containing one claims handler, CustomClaimsHandler.
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:cxf="http://cxf.apache.org/core"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:sec="http://cxf.apache.org/configuration/security"
  xmlns:http="http://cxf.apache.org/transports/http/configuration"
  xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xmlns:util="http://www.springframework.org/schema/util"
  xsi:schemaLocation="
            http://cxf.apache.org/core
            http://cxf.apache.org/schemas/core.xsd
            http://cxf.apache.org/configuration/security
            http://cxf.apache.org/schemas/configuration/security.xsd
            http://cxf.apache.org/jaxws
            http://cxf.apache.org/schemas/jaxws.xsd
            http://cxf.apache.org/transports/http/configuration
            http://cxf.apache.org/schemas/configuration/http-conf.xsd
            http://cxf.apache.org/transports/http-jetty/configuration
            http://cxf.apache.org/schemas/configuration/http-jetty.xsd
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-2.0.xsd">
    ...
    <bean id="transportSTSProviderBean"
        class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider">
        <property name="issueOperation" ref="transportIssueDelegate" />
        <property name="validateOperation" ref="transportValidateDelegate" />
    </bean>

    <bean id="transportIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation">
        ...
        <property name="claimsManager" ref="claimsManager" />
        ...
    </bean>
    ...
    <bean id="claimsManager" class="org.apache.cxf.sts.claims.ClaimsManager">
        <property name="claimHandlers" ref="claimHandlerList" />
    </bean>

 <util:list id="claimHandlerList"> <ref bean="customClaimsHandler" /> </util:list> <bean id="customClaimsHandler" class="org.apache.cxf.systest.sts.deployment.CustomClaimsHandler"> </bean>
    ...   
</beans>
The CustomClaimsHandler class is a trivial implementation of a claims handler that appears in one of the STS system tests. For the purposes of the test, it returns a few fixed claim values for a couple of different principals.

Embedding claim values in a SAML token

The key step in processing claims is the point where the STS attempts to issue the token. Whichever token provider is selected to issue the token, it must be capable of inserting the retrieved claim values into the issued token. The token provider must therefore be customized or extended, so that it is capable of embedding the claims in the issued token.
In the case of issuing SAML tokens, the appropriate mechanism for embedding claim values is to generate SAML attribute statements containing the claim values. The appropriate way to extend the SAML token provider, therefore, is to implement a custom AttributeStatementProvider class and to register this class with the SAMLTokenProvider instance (see the section called “SAMLTokenProvider”).

Sample AttributeStatementProvider

Example 9.4, “The CustomAttributeStatementProvider Class” shows a sample implementation of an AttributeStatementProvider class, which is capable of embedding claim values in a SAML token. This sample implementation, CustomAttributeStatementProvider, is taken from the STS system tests, but it is generally quite useful as a starting point for a custom attribute statement provider implementation.

Example 9.4. The CustomAttributeStatementProvider Class

package org.apache.cxf.systest.sts.deployment;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.cxf.sts.claims.Claim;
import org.apache.cxf.sts.claims.ClaimCollection;
import org.apache.cxf.sts.claims.ClaimsManager;
import org.apache.cxf.sts.claims.ClaimsParameters;
import org.apache.cxf.sts.token.provider.AttributeStatementProvider;
import org.apache.cxf.sts.token.provider.TokenProviderParameters;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.saml.ext.bean.AttributeBean;
import org.apache.ws.security.saml.ext.bean.AttributeStatementBean;

public class CustomAttributeStatementProvider implements AttributeStatementProvider {

    public AttributeStatementBean getStatement(TokenProviderParameters providerParameters) {

        // Handle Claims
        ClaimsManager claimsManager = providerParameters.getClaimsManager();
        ClaimCollection retrievedClaims = new ClaimCollection();
        if (claimsManager != null) {
            ClaimsParameters params = new ClaimsParameters(); 1
            params.setAdditionalProperties(providerParameters.getAdditionalProperties());
            params.setAppliesToAddress(providerParameters.getAppliesToAddress());
            params.setEncryptionProperties(providerParameters.getEncryptionProperties());
            params.setKeyRequirements(providerParameters.getKeyRequirements());
            params.setPrincipal(providerParameters.getPrincipal());
            params.setRealm(providerParameters.getRealm());
            params.setStsProperties(providerParameters.getStsProperties());
            params.setTokenRequirements(providerParameters.getTokenRequirements());
            params.setTokenStore(providerParameters.getTokenStore());
            params.setWebServiceContext(providerParameters.getWebServiceContext());
            retrievedClaims = 
                claimsManager.retrieveClaimValues( 2
                    providerParameters.getRequestedClaims(),
                    params
                );
        }
        if (retrievedClaims == null) {
            return null;
        }
        
        Iterator<Claim> claimIterator = retrievedClaims.iterator();
        if (!claimIterator.hasNext()) {
            return null;
        }

        List<AttributeBean> attributeList = new ArrayList<AttributeBean>();
        String tokenType = providerParameters.getTokenRequirements().getTokenType();

        AttributeStatementBean attrBean = new AttributeStatementBean(); 3
        while (claimIterator.hasNext()) {
            Claim claim = claimIterator.next();
            AttributeBean attributeBean = new AttributeBean(); 4
            URI name = claim.getNamespace().relativize(claim.getClaimType());
            if (WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
                    || WSConstants.SAML2_NS.equals(tokenType)) {
                attributeBean.setQualifiedName(name.toString());
                attributeBean.setNameFormat(claim.getNamespace().toString());
            } else {
                attributeBean.setSimpleName(name.toString());
                attributeBean.setQualifiedName(claim.getNamespace().toString());
            }
            attributeBean.setAttributeValues(Collections.singletonList(claim.getValue())); 5
            attributeList.add(attributeBean);
        }
        attrBean.setSamlAttributes(attributeList);

        return attrBean;
    }

}
1
The first part of the getStatement method implementation is centered around the invocation of the ClaimsManager.retrieveClaimValues method.
In preparation for invoking the retrieveClaimValues method, you populate the ClaimsParameters object, which encapsulates most of the parameters needed to invoke retrieveClaimValues. The ClaimsParameters object is initialized simply by copying the relevant parameters from the TokenProviderParameters object.
2
Invoke the retrieveClaimValues method on the claims manager instance. This has the effect of retrieving the requested claim values from persistent storage, with the help of the claims handlers plug-ins (see Figure 9.9, “Processing Claims”).
3
The AttributeStatementBean class is a WSS4J class that is used to encapsulate a SAML attribute statement.
4
The WSS4J AttributeBean class encapsulates a single SAML attribute.
5
Each claim value is inserted into an AttributeBean instance.

Configuring the custom AttributeStatementProvider

The custom attribute statement provider can be installed into the SAMLTokenProvider instance, as follows:
<beans ...>
    ...
    <bean id="transportSTSProviderBean"
        class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider">
        <property name="issueOperation" ref="transportIssueDelegate" />
        <property name="validateOperation" ref="transportValidateDelegate" />
    </bean>

    <bean id="transportIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation">
        ...
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="claimsManager" ref="claimsManager" />
        ...
    </bean>

    <util:list id="transportTokenProviders">
        <ref bean="transportSamlTokenProvider" />
        ...
    </util:list>
    ...
    <bean id="transportSamlTokenProvider"
          class="org.apache.cxf.sts.token.provider.SAMLTokenProvider">
        <property name="attributeStatementProviders" ref="attributeStatementProvidersList" />
    </bean>

    <util:list id="attributeStatementProvidersList">
        <ref bean="defaultAttributeProvider" />
        <ref bean="customAttributeProvider" />
    </util:list>

    <bean id="defaultAttributeProvider"
        class="org.apache.cxf.sts.token.provider.DefaultAttributeStatementProvider">
    </bean>

    <bean id="customAttributeProvider"
        class="org.apache.cxf.systest.sts.deployment.CustomAttributeStatementProvider">
    </bean>
    ...   
</beans>
Note that a DefaultAttributeStatementProvider instance should also be included in the list of attribute statement providers, so that the issued SAML token also includes the default attribute statement.