December 23, 2011

Updates on federation sources

In the last weeks I was working in enabling IBM Websphere with WS-Federation and setting up a testcase where a federation enabled web application consumes web services where the security context must be propagated. I need some more time to prepare an example and post a blog. Stay tuned.

There are some updates with respect to the federation examples I posted in the last weeks.

  • Sources have been moved from Google docs to the Apache CXF project
  • The SAML security token had a lifetime of 5 minutes which has been increased to 20 minutes
  • The Tomcat authenticator doesn't redirect if the security token is expired instead it blocks the incoming request


Sources have been moved from Google docs to the Apache CXF project

You can find the sources here. Right now, you have to check out the sources and build them on your own. This is fairly simple with Maven (mvn clean install).

The package names have changed. Please ensure to configure the new package name org.apache.cxf.fediz. ...

The STS of the CXF release 2.5.1 is used

If you have any questions please raise them at the Apache CXF mailing list.


The SAML security token had a lifetime of 5 minutes which has been increased to 20 minutes

If you deploy the previous Tomcat example you could test the application for 5 minutes. Further requests have been rejected because the security token  lifetime is expired (Condition in SAML 2.0).

I made the following changes to the STS configuration to change the default lifetime which is fairly simple to do due to the usage of Spring.


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

    <bean id="conditionsProvider"
        class="org.apache.cxf.sts.token.provider.DefaultConditionsProvider">
        <property name="lifetime" value="1200" />
    </bean>


If you don't configure the DefaultConditionsProvider the default lifetime is 5 minutes. You configure a different lifetime by setting a value in seconds for the property lifetime.

The Tomcat authenticator doesn't redirect if the security token is expired instead it blocked the incoming request

As mentioned in the previous section, the issued SAML token expires after 5 minutes and blocks any incoming request of this user. The lifetime of the HTTP session and the security token can be different in deployments due to different non functional requirements for the application and the security infrastructure. Thus, the application server (Relying Party) should transparently redirect the user to the IDP. Usually, the user still has got a session with the IDP and is not challenged to enter username and password. A new SAML token is issued and sent back to the application server which validates the token and updates the session.

This fix has been applied in revision r1222148 here.


November 2, 2011

Configure Tomcat for federation - Part III


I described in the previous blogs how to set up the IDP which plays a key role in Web SSO based on the Passive requestor profile of WS-Federation and the CXF STS to issue security tokens. The IDP delegates authentication requests to the STS.

In WS-Federation, the authentication process is externalized to the IDP/STS. It provides you a SAML token (or another security token, WS-Federation is not tight to SAML) which contains all information of the authenticated user you need to implement finer grained authorization requirements without being tight to the user name.

The information in the SAML token must be integrated in Tomcat in such a way that application developers can use J2EE security as they are used to.
Example:
  • HttpServletRequest.getUserPrincipal()
  • HttpServletRequest.isUserInRole()
  • security constraints in web.xml
There is no need to configure any security related servlets/filters in the web.xml. This is managed on the Tomcat container level to benefit of container managed security.

It's standardized here how claim information (firstname, lastname, email, etc) must be represented in an AttributeStatement of a SAML token (assertion) but there is no Java API standard how this information could be accessed by programming. The federation plugin provides an extension of the Tomcat principal class which provides all these information.


This blog addresses two topics:
  • how to build and deploy the federation plugin into Tomcat
  • how to build and deploy a federation enabled web application into Tomcat

The plugin has been tested with JDK 1.6 and Tomcat 7.0.



Build and deploy the federation plugin into Tomcat
The source code is bundled as maven plugins which can be downloaded here and contains the following maven modules:
  • plugins/core
    This module contains the core ws-federation functionality which is container agnostic. The core functionality includes SAML 1.1/2.0 token validation (signature verification), lifetime and audience restriction validation. The FederationProcessor returns all information like roles, claims and authenticated user.
  • plugins/tomcat
    This module contains the tomcat authenticator which integrates the ws-federation functionality into Tomcat. It hydrates the tomcat security context
  • examples/simpleWebapp/
    This is a sample web application where the federation plugin is configured and makes use of security constraints and the Servlet Security API.
    This module is described in section "Build and deploy the federation sample application".

Run mvn clean install in each module to build the jar file.
After the build, please follow these steps to deploy and configure the federation plugin.

1) Create sub-directory fediz in ${catalina.home}/lib

2) Update calatina.properties in ${catalina.home}/conf
add the previously created directory to the common loader:
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,${catalina.home}/lib/fediz/*.jar


3) Deploy the built libraries and dependencies to the directory created in (1)
The built libraries and their dependencies can be found in the zip file located in fediz-tomcat/target/...zip-with-dependencies.zip

4) Configure Federation plugin
The current release of the federation plugin requires to configure the FederationAuthenticator which is configured like any other Valve as described here.

You can either configure the context in the server.xml or in META-INF/context.xml as part of your WAR file.

server.xml
  <Context path="/fedizhelloworld" docBase="fedizhelloworld">
    <Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator"

      issuerURL="https://localhost:9443/fedizidp/"
      truststoreFile="conf/stsstore.jks"
      truststorePassword="stsspass"
      trustedIssuer=".*CN=www.sts.com.*" />
  </Context>
       
META-INF/context.xml


  <Context> 
    <Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator"
      issuerURL="https://localhost:9443/fedizidp/"
      truststoreFile="conf/stsstore.jks"
      truststorePassword="stsspass"
      trustedIssuer=".*CN=www.sts.com.*" />
  </Context>



The attribute issuerURL tells the plugin where to redirect unauthenticated requests which access a protected resource (security constraint matches URI pattern).
The current release of the federation plugin supports SAML 1.1/2.0 tokens. The SAML token is signed to provide a way to establish a trust. The federation plugin must validated the SAML token. The validation process includes verifying whether the certificate has been issued by a trusted CA and whether the certificate subject is trusted. The certificate subject identifies the IDP/STS.
You can have more than one IDP/STS in your company. As soon as you grant B2B partners access (internet) to your web application you have at least 2 IDP/STS instances. Usually, you create a chain of IDP instances where the RP trusts only the internal IDP/STS instance and the internal IDP (so called Relying Party IDP) trusts the external IDP. This scenario is addressed in one of the next blogs.
The attribute truststoreFile tells the plugin where the truststore is located (Java Keystore format) and the password is configured in the attribute truststorePassword. The trusted IDP/STS is configured as a regular expression in the attribute trustedIssuer.

It's not yet possible to configure a custom security token validator.


5) Configure the CA certificates

If you use the STS as described here you can copy the keystore from fediz-idp-sts/src/test/resources/stsstore.jks to the configured location in the attribute truststoreFile.

(you can ignore the fact in this demo that the private key is contained in this keystore also which must not be the case for production. The certificate is sufficient for signature validation)


Build and deploy the federation sample application
I've chosen the META-INF/context.xml approach to configure the federation plugin for ease of use.

This web application provides three accessible resources:
  • index.html
    This static page is not secured by a security constraint
  • secure/test.html
    This static page is secured by a security constraint in the web.xml
  • /secureservlet/fed
    This servlet provides information about the authenticated user like username, roles and claims as illustrated in the following picture:
    
The user and role information can be retrieved using standard J2EE security API. Unfortunately, there is no standard API to retrieve claims information thus the federation plugin provides an extension of the principal which can be accessed as illustrated here:

  Principal p = request.getUserPrincipal();
  if (p instanceof FederationPrincipal) {
     FederationPrincipal fp = (FederationPrincipal)p;
     ClaimCollection claims = fp.getClaims();
     ...
  }

Every security constraint must define at least one role in auth-constraint as defined in the Servlet specification. There are use cases where you might want to give access to resources to everybody who is authenticated. Therefore, the plugin can add a default role if no roles are provided as part of the SAML token. If not explicitly configured, the default role name is Authenticated.



Encoding of roles in SAML token

The federation authenticator provides the following attributes to customize where and how the role information is encoded in the SAML token. The role is like any other claim represented within the attribute statement.

The IDP/STS in this example uses the following URI for the role claim:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role

If your IDP/STS stores the role information in another claim, configure the attribute roleClaimURI in the FederationAuthenticator. The value of the attribute is a string where the list of roles are delimited by a character. The IDP/STS in this example use "," as the default delimiter. If you want to use another delimiter configure the attribute roleDelimiter in the FederationAuthenticator.

October 20, 2011

How to enable an interceptor without configuration in CXF

Imagine you develop an interceptor within your company which is for general use and not for a specific business project. There are different ways to configure an interceptor as described in more detail here. In summary:
  • by programming
  • configure the interceptor on the bus, client or endpoint level
  • configure a feature which registers interceptor(s)
  • configure a policy which registers interceptors
There might be use cases where you want to enable an interceptor just because the interceptor is available in the classpath. This means, you developed an interceptor, built a Jar and published to the maven repository. Another project should only add a dependency to your jar in their POM to enable the interceptor automatically. Done.

This blog will explain how this works and an example can be downloaded here.

CXF 2.4 introduced a new feature called bus extension which are loaded during the initialization phase of a bus automatically. Thus, your interceptor registration code must get notified whenever a new client or server is created to be able to add the interceptor. CXF provides three LifecycleListners:
This example shows the usage of the Server- and ClientLifecycleListner. A lifecycle listener is registered by the corresponding lifecycle manager which can be resolved using the CXF bus as illustrated in the following code snippet:

public class DemoListener implements ClientLifeCycleListener, ServerLifeCycleListener {

 public DemoListener(Bus bus) {
    ServerLifeCycleManager slm = bus.getExtension(ServerLifeCycleManager.class);
    slm.registerListener(this);

This class implements the lifecycle listener methods and registers itself to the lifecycle manager. The interceptor could be registered as illustrated in the following code snippet:

public void startServer(Server server) {
  System.out.println("--------- startServer");
  server.getEndpoint().getInInterceptors().add(new DemoInterceptor());
  server.getEndpoint().getOutInterceptors().add(new DemoInterceptor());
}

public void clientCreated(Client client) {
  System.out.println("--------- clientCreated");
  client.getOutInterceptors().add(new DemoInterceptor());
  client.getInInterceptors().add(new DemoInterceptor());
}

So far so good, but there must be a way to trigger the instantiation of the class DemoListener. One option is to use Spring and make this class a spring managed component or you register this bean as a CXF bus extension.

I'll explain how to register this bean as a bus extension thus it gets instantiated when the bus is created which means that the lifecycle listeners are registered in the startup phase too.
Your Jar file must contain a file called bus-extensions.xml at the following location in the Jar:
META-INF/cxf/bus-extensions.txt

The content of this text file is very simple. You just list the classname:
org.talend.ps.examples.busextension.DemoListener::false

The last parameter is false which tells the bus whether the instantiation of the bean should be defered or not. If deferred is true, (default) the bus doesn't create the beans during startup. However, if something specifically asks for one of those beans, it will be created and loaded.

If the class provides a constructor with the Bus argument, CXF will pass the bus during initialization.

Where else is this feature used? In CXF itself.

This was a very simple example how to instantiate beans during bus startup without the requirement for Spring. A lot of the advanced and WS-* features in CXF 2.3 and earlier required that you explicitly imported resources like:

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-jms.xml" />

This was required to import the spring configuration files which contained bean definitions which usually implemented lifecycle listeners or support for policy. This is not required any more in CXF 2.4 and above. You only have to import the following resource now:

<import resource="classpath:META-INF/cxf/cxf.xml" />

With CXF 2.4, these features are not plugged into the bus using a spring mechanism but instead the bus extension mechnism as illustrated in the simple example above.

If you want to see more advanced usage of bus extension have a look to the sources of some of these CXF modules:

An interesting interceptor example can be found in the Talend Service Factory.

October 18, 2011

Configure LDAP directory for CXF STS

I explained in my previous blog how to set up the CXF STS where you manage your users and claims in a file.This blog explains the required changes to integrate the CXF STS with a LDAP directory.

You can attach an LDAP directory either for username/password validation or for retrieving the claims data or both.

1. Username and password authentication

WSS4j supports username/password authentication against a JAAS based backend since version 1.6.3.

The JDK provides a JAAS LoginModule for LDAP which can be configured as illustrated here in a sample jaas configuration (jaas.config):
  
myldap {
 com.sun.security.auth.module.LdapLoginModule REQUIRED
 userProvider=ldap://ldap.mycompany.org:389/OU=Users,DC=mycompany,DC=org"
 authIdentity="cn={USERNAME},OU=Users,DC=mycompany,DC=org"
 useSSL=false
 debug=true;
};

You can get more information about this LoginModule here.

In this example, all the users are stored in the organization unit Users within mycompany.org. The configuration filename can be chosen, i.e. jaas.config. The filename must be configured as a JVM argument. I recommend to set JVM related configurations for Tomcat in the setenv.sh/bat file located in tomcat/bin directory. This script is called by catalina.bat/sh implicitly and might look like this for UNIX:

#!/bin/sh 
JAVA_OPTS="-Djava.security.auth.login.config=/opt/tomcat/conf/jaas.config"
export JAVA_OPTS

Now, the LDAP LoginModule is configured. Next we have to configure the JAASUsernameTokenValidator for the STS endpoint.

<bean
  class="org.apache.ws.security.validate.JAASUsernameTokenValidator"
      id="jaasUTValidator">
   <property name="contextName" value="myldap"/>
</bean>

<jaxws:endpoint id="transportSTSUT"
  endpointName="ns1:TransportUT_Port"
  serviceName="ns1:SecurityTokenService"
  xmlns:ns1=http://docs.oasis-open.org/ws-sx/ws-trust/200512/
  wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl"
  address="/STSServiceTransportUT"
  implementor="#transportSTSProviderBean">

  <jaxws:properties>
    <entry key="ws-security.ut.validator"
         value-ref="jaasUTValidator"/>
  </jaxws:properties>
</jaxws:endpoint>


The property contextName must match with the context name defined in the JAAS configuration file which is "myldap" in this example.
<
 2. Claims management

When a STS client requests a claim, the ClaimsManager in the STS checks every registered ClaimsHandler who can provide the data of the requested claim.  The CXF STS provides a claims handler implementation which allows to add claims which are stored as user attributes in a LDAP directory. You can configure which claim URI maps to which LDAP user attribute. The implementation uses the spring ldap module (LdapTemplate).

<util:list id="claimHandlerList">
  <ref bean="ldapClaimsHandler" />
</util:list>

<bean id="contextSource"
   class="org.springframework.ldap.core.support.LdapContextSource">
  <property name="url" value="ldap://ldap.mycompany.org:389" />
  <property name="userDn"
    value="CN=techUser,OU=Users,DC=mycompany,DC=org" />
  <property name="password" value="mypassword" />
</bean>

<bean id="ldapTemplate"
   class="org.springframework.ldap.core.LdapTemplate">
  <constructor-arg ref="contextSource" />
</bean>

<util:map id="claimsToLdapAttributeMapping">
  <entry
key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
value="givenName" />
  <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
value="sn" />
  <entry
key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
value="mail" />
  <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country"
value="c" />
</util:map>

<bean id="ldapClaimsHandler"
    class="org.apache.cxf.sts.claims.LdapClaimsHandler">
  <property name="ldapTemplate" ref="ldapTemplate" />
  <property name="claimsLdapAttributeMapping"
            ref="claimsToLdapAttributeMapping" />
  <property name="userBaseDN"
      value="OU=Users,DC=mycompany,DC=org" />
</bean>


The claim id's are configured according to chapter 7.5 in the specification Identity Metasystem Interoperability. You can add as many entries in the map claimsToLdapAttributeMapping as you want. Thus you can add any user attribute from your LDAP directory to the issued SAML token.

October 13, 2011

Configure and deploy Identity Provider (IdP) - Part II

In my previous blog I talked about setting an STS which supports username/password authentication and the issuance of a SAML 2.0 token which contains additional claims information.
We need these claims information to provide the application (called Relying Party in WS-Federation specification) the information like application roles for role based access control. Claims based authorization goes one step further and provides other claims data of the authenticated entity (SAML subject).

Introduction

This blog is about the Identity Provider (IDP) implementation which is referenced in the WS-Federation specification. Therefore, I'm giving a short introduction. This blog series looks at the passive requestor profile only of WS-Federation.

The following picture is used by Microsoft which supports WS-Federation in their Windows Identity Foundation framework.


(C) Microsoft



The key principals of WS-Federation are:
  • Externalize authentication process from the application container
  • Provide the claims/attributes of the authenticated identity to the application for role based and fine grained authorization
WS-Federation gives you the following benefits:
  • Applications can benefit of stronger security mechanism without changes
  • Identities/users don't have to be provisioned in all security domains to propagate identities across the security domains
  • B2B partners can be integrated without changing the application (includes programming and configuration)
  • Audit-Trail end-to-end


    Deploy Identity Provider (IdP)



    This sample IDP will support the following functionality:
    • Authentication based on username/password (Basic Authentication)
    • The authentication store is configured in the STS and can be file based (part of this example) or LDAP
    • Following federation parameters are supported:
      • wtrealm
      • wreply
      • wctx
      • wresult
    • Required claims can be configured per Relying Party (based on wtrealm value)
    Jürg Portmann and myself have put together this IDP. You can download the maven project services/idp here.


    1. Claims configuration per relying party

    The required claims per relying party are configured in the WEB-INF/RPClaims.xml. The XML file has the following structure. The key of each map entry must match with the wtrealm paramater in the redirect triggered by the relying party.
    (the set up of the relying part will be covered in the next blog).


        <util:map id="realm2ClaimsMap">
            <entry key="http://localhost:8080/wsfedhelloworldother/"
                value-ref="claimsWsfedhelloworld" />
            <entry key="http://localhost:8080/wsfedhelloworld/"
                value-ref="claimsWsfedhelloworldother" />
        </util:map>

        <util:list id="claimsWsfedhelloworld">
            <value>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
            </value>
            <value>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
            </value>
        </util:list>

        <util:list id="claimsWsfedhelloworldother">
            <value>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
            </value>
            <value>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
            </value>
            <value>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
            </value>
        </util:list>   


    You group the required claims in beans which are a list of String as illustrated in claimsWsfedhelloworld and claimsWsfedhelloworldother.

    The map bean must be named realm2ClaimsMap and maps the different relying parties (applications) to one of the claim lists.


    In a further release, this information will be pulled from a WS-Federation Metadata document published by the replying party.


    2. Project dependencies
     
    The IDP reuses a lot of existing projects like Apache CXF to communicate with the STS for instance and provides an adaption of the WS-Trust interface to pure HTTP functionality as it is supported by a browser. The IDP has the following dependencies in the Maven project:


        <properties>
          <cxf.version>2.5.2</cxf.version>
       </properties>

       <dependencies>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-transports-http</artifactId>
                <version>${cxf.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-frontend-jaxws</artifactId>
                <version>${cxf.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-ws-policy</artifactId>
                <version>${cxf.version}</version>
            </dependency>
            <dependency>
                <groupId>
    org.apache.cxf</groupId>
                <artifactId>
    cxf-rt-ws-security</artifactId>
                <version>${cxf.version}</version>
            </dependency>

       </dependencies>  


    3. IDP web application configuration

    Setting up the IDP involves a few steps only. If you you don't deploy the IDP in the same servlet container as the STS you must first download Tomcat 7 and update server.xml. If you deploy the IDP in the same servlet container you can skip 3.1

    3.1 Configure HTTP/S connector in Tomcat

    The HTTP connector should be configured with port 9080.


    The HTTPS connector in Tomcat is configured in conf/server.xml. Deploy the tomcatkeystore.jks of the example project  to the Tomcat root directory if the Connector is configured as illustrated:

        <Connector port="9443" protocol="HTTP/1.1" SSLEnabled="true"
                   maxThreads="150" scheme="https" secure="true"
                   keystoreFile="tomcatKeystore.jks"
                   keystorePass="tompass" sslProtocol="TLS" />

    This connector configures a self-signed certificate which is used for simplification only. You should get a certificate signed by a Certificate Authority for production usage.

    3.2 Configure Username/password authentication


    As described in section 1. the requested claims per relying party are managed in the file WEB-INF/RPclaims.xml.

    3.3 IDP and STS distributed
     
    If the IDP and STS are not deployed on the same machine (likewise in production) you have to update the following configuration:






    1) The remote WSDL location of the STS:

    WEB-INF/web.xml:

      <init-param>
        <param-name>sts.wsdl.url</param-name>
        <param-value>https://localhost:9443/wsfedidpsts/STSService?wsdl</param-value>
      </init-param>


    2) the transport conduit to enable the truststore as described in more detail here:


    WEB-INF/beans.xml

      <http:conduit name="https://localhost:9443/.*">
        <http:tlsClientParameters disableCNCheck="true">
          <sec:trustManagers>
            <sec:keyStore type="jks" password="cspass" resource="clientstore.jks"/>
          </sec:trustManagers>
        </http:tlsClientParameters>
      </http:conduit>




    3) fully qualified realm in realm2ClaimsMap


    As described in 1) the key of an entry in the map realm2ClaimsMap must match with the  parameter wtrealm in the redirect triggered by the relying party. If you access the relying party using a fully qualified URL, you must use the fully qualified URL in the IDP too.  


    WEB-INF/RPclaims.xml


        <util:map id="realm2ClaimsMap">
            <entry key="http://localhost:8080/wsfedhelloworldother/"
                value-ref="claimsWsfedhelloworld" />
            <entry key="http://localhost:8080/wsfedhelloworld/"
                value-ref="claimsWsfedhelloworld2" />
        </util:map>



    4. Deploy the IDP to Tomcat

    To deploy the IDP using Maven you have to follow these steps:
    • Configuring the following maven plugin
          <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>tomcat-maven-plugin</artifactId>
              <version>1.1</version>
              <configuration>
                   <server>myTomcat</server>
                   <url>http://localhost:9080/manager/text</url>
                   <path>/${project.build.finalName}</path>
              </configuration>
          </plugin>
    • Add the server with username and password to your settings.xml
    • Ensure the user has the role "manager-script" as described here
    • Run mvn tomcat:redeploy
      (I recommend to use redeploy as deploy works the first time only)
    If you use Tomcat 6, you must change the url of the tomcat maven plugin:
        <url>http://localhost:9080/manager</url>


    4. Test the IDP

    As long as you don't have a relying party in place you can't easily test the IDP. The following post explains the set up of the relying party using Tomcat 7.


    If you like you can test the IDP by using an HTTP client and pass the following request parameters (urls must be encoded):

    wa       wsignin1.0
    wreply   http://localhost:8080/wsfedhelloworld/secureservlet/fed
    wtrealm  http://localhost:8080/wsfedhelloworld/


    The browser will get a HTML form back (auto-submit). The action of the form is equal to the value of wreply which doesn't exist. You see the response of the STS escaped in the form parameter wresult.

    October 11, 2011

    Configure and deploy CXF 2.5 STS - Part I

    Talend has donated an STS implementation to the Apache CXF community as posted already on this here

    This is the first part of a series of blogs on using WS-Federation Passive Requestor Profile to implement a Web and Web Services SSO solution from a web application to a target Web Service. The used technologies are CXF 2.5 (to be released soon) and Tomcat 7.

    • Part I
      Configure and deploy CXF STS using Claims
    • Part II
      Configure and deploy Identity Provider (IdP)
    • Part III
      Configure and deploy Tomcat Relying Party (RP)
    • Part IV
      Enhance Tomcat RP to call a target web services which delegates the identity
    • Part V
      Interoperability testing with Microsoft Windows Identity Foundation

    The STS in this part is configured to support the following functionality:
    • STS WSDL is enriched with the WS-SecurityPolicy information
    • STS issues a signed SAML 2.0 token
    • STS is secured using HTTPS
    • STS validates an incoming UsernameToken against a local file store
    • STS adds claims information to the SAML token in an attribute statement
    You can find a running maven project called services/sts here.


    1. Username and password management

    The users and passwords are configured in a spring configuration file in WEB-INF/passwords.xml. The XML file has the following structure:

        <util:map id="passwords">
            <entry key="alice"
                value="ecila" />
            <entry key="bob"
                value="bob" />
            <entry key="ted"
                value="det" />
        </util:map>

    The intention of this STS example is to illustrate how to set up an STS. If you have an LDAP directory in place or any other JAAS based LoginModule you can also plug in the WSS4J JAASUsernameTokenValidator.

    2. Claims management

    The claims for each user are configured in a spring configuration file also in WEB-INF/userClaims.xml. The XML file has the following structure:

        <util:map id="userClaims">
            <entry key="alice"
                value-ref="aliceClaims" />
            <entry key="bob"
                value-ref="bobClaims" />
            <entry key="ted"
                value-ref="tedClaims" />
        </util:map>
       
        <util:map id="aliceClaims">
            <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
                value="Alice" />
            <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
                value="Smith" />
            <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
                value="alice@mycompany.org" />
            <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"
                value="user" />
               
        </util:map>


    The claim id's are configured according to chapter 7.5 in the specification Identity Metasystem Interoperability. The mapping of claims to a SAML attribute statement are described in chapter 7.2.
    There is no standard URI for role. Therefore, I reuse Microsoft's role URI which is used by ADFS (Active Directory Federation Service) and Windows Identity Foundation (WIF). If the STS issues claims using the same role URI, you get role-based access control (or claims based authorization support for WIF based applications out-of-the-box).

    The intention of this STS example is to illustrate how to set up an STS. If you have an LDAP directory in place you can configure the LdapClaimsHandler where you configure the mapping of the claim id (URI) to an LDAP user attribute. 

    3. Project dependencies
     
    The STS has the following dependencies in the Maven project.

        <properties>
          <cxf.version>2.5.2</cxf.version>

       </properties>

       <dependencies>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-transports-http</artifactId>
                <version>${cxf.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-frontend-jaxws</artifactId>
                <version>${cxf.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-rt-ws-policy</artifactId>
                <version>${cxf.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.cxf.services.sts</groupId>
                <artifactId>cxf-services-sts-core</artifactId>
                <version>${cxf.version}</version>
            </dependency>

       </dependencies>  


    4. STS endpoint configuration

    Setting up the STS involves several steps. The STS is configured using the spring framework. First step is to download Tomcat 7.

    4.1 Configure HTTP/S connector in Tomcat


    The HTTP connector should be configured with port 9080.

    The HTTPS connector in Tomcat is configured in conf/server.xml. Deploy the tomcatkeystore.jks of the example project  to the Tomcat root directory if the Connector is configured as illustrated:

        <Connector port="9443" protocol="HTTP/1.1" SSLEnabled="true"
                   maxThreads="150" scheme="https" secure="true"
                   keystoreFile="tomcatKeystore.jks"
                   keystorePass="tompass" sslProtocol="TLS" />

    Update: Have a read through the following blog here which describes how to generate a keystore.

    4.2 Configure the WS-SecurityPolicies of the STS endpoint

    The following policies must be added to the WSDL. CXF provides other ways to correlate policies with a wsdl subject (port type, service, port, ...). I've chosen the simplest one where the policies are embedded into the wsdl for illustration purposes. The WSDL can be found in WEB-INF/wsdl/ws-trust-1.4-service.wsdl

    The following policy defines a transport binding (https) and expects a UsernameToken be present in the WS-Security header. The UsernameToken must be signed which is implicitly supported by HTTPS:

        <wsp:Policy wsu:Id="Transport_policy">
          <wsp:ExactlyOne>
             <wsp:All>
                <wsap10:UsingAddressing/>
                <sp:TransportBinding
                   xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                   <wsp:Policy>
                      <sp:TransportToken>
                         <wsp:Policy>
                            <sp:HttpsToken RequireClientCertificate="false"/>
                         </wsp:Policy>
                      </sp:TransportToken>
                      <sp:AlgorithmSuite>
                         <wsp:Policy>
                            <sp:TripleDesRsa15 />
                         </wsp:Policy>
                      </sp:AlgorithmSuite>
                      <sp:Layout>
                         <wsp:Policy>
                            <sp:Lax />
                         </wsp:Policy>
                      </sp:Layout>
                      <sp:IncludeTimestamp />
                   </wsp:Policy>
                </sp:TransportBinding>
                <sp:SignedSupportingTokens
                   xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                   <wsp:Policy>
                      <sp:UsernameToken
                         sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                         <wsp:Policy>
                            <sp:WssUsernameToken10 />
                         </wsp:Policy>
                      </sp:UsernameToken>
                   </wsp:Policy>
                </sp:SignedSupportingTokens>
                <sp:Wss11
                   xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                   <wsp:Policy>
                      <sp:MustSupportRefKeyIdentifier />
                      <sp:MustSupportRefIssuerSerial />
                      <sp:MustSupportRefThumbprint />
                      <sp:MustSupportRefEncryptedKey />
                   </wsp:Policy>
                </sp:Wss11>
                <sp:Trust13
                   xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                   <wsp:Policy>
                      <sp:MustSupportIssuedTokens />
                      <sp:RequireClientEntropy />
                      <sp:RequireServerEntropy />
                   </wsp:Policy>
                </sp:Trust13>
             </wsp:All>
          </wsp:ExactlyOne>
       </wsp:Policy>



    4.3 Configure TokenProvider

    This STS endpoint configuration only supports to issue SAML tokens (2.0 or 1.1). For a full list of the supported features by the STS check this blog.

    The configuration related to issuing a token is located in the following spring configuration file (cxf-transport.xml):

    <bean id="transportIssueDelegate"
    class="org.apache.cxf.sts.operation.TokenIssueOperation">

        <property name="tokenProviders" ref="transportTokenProviders"/>
        <property name="services" ref="transportService"/>
        <property name="stsProperties" ref="transportSTSProperties"/>
        <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="claimsAttributeProvider"/>

    </util:list>
       
    <bean id="claimsAttributeProvider"
            class="org.apache.cxf.sts.claims.ClaimsAttributeStatementProvider">
    </bean>


    The last bean claimsAttributeProvider is described in section 4.5



    4.4 Configure Username/password authentication


    As described in section 1. the user and passwords are managed in the file WEB-INF/passwords.xml.


    To configure username/password authentication in CXF/WSS4J you must provide a CallbackHandler. The CallbackHandler is part of this example project.


    The configuration is located in the following spring configuration file (cxf-transport.xml):

         <import resource="passwords.xml" />
      
        <bean id="upCallBackHandler"
            class="org.talend.security.sts.UsernamePasswordCallbackHandler">
            <property name="passwords" ref="passwords" />
        </bean>

        <jaxws:endpoint id="transportSTS1"
            implementor="#transportSTSProviderBean"
            address="/STSService"
            wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl"
            xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
            serviceName="ns1:SecurityTokenService"
            endpointName="ns1:TransportUT_Port">
            <jaxws:properties>
                <entry key="ws-security.callback-handler" value-ref="upCallBackHandler"/>
            </jaxws:properties>
        </jaxws:endpoint>


    The bean upCallBackHandler implements the CallbackHandler which is configured as a jaxws property ws-security.callback-handler in jaxws:properties of the jaxws:endpoint configuration. 



    4.5 Configure ClaimsManager

    Claims data can be stored in different kind of ID systems which can be accessed using LDAP, Web Services, REST whatever. The retrieval of claims is independent of any specific security token type and group in a list of claimshandler. Each claims handler must implement what kind of claims he can provide. It's the responsibility of the ClaimsManager to call the corresponding ClaimsHandler if a specific claim is requested by a STS client.

    The claims related configuration is summerized in the following spring configuration file (cxf-transport.xml):

        <import resource="userClaims.xml" />
       
        <bean id="claimsManager"
            class="org.apache.cxf.sts.claims.ClaimsManager">
            <property name="claimHandlers" ref="claimHandlerList" />
        </bean>
       
        <util:list id="claimHandlerList">
            <ref bean="fileClaimsHandler"/>
        </util:list>
       
        <bean id="fileClaimsHandler"
            class="org.talend.security.sts.FileClaimsHandler">
           
            <property name="userClaims" ref="userClaims" />
        </bean>

    The bean userClaims is defined in the imported spring configuration file userClaims.xml.

    5. Deploy the STS to Tomcat

    To deploy the STS using Maven you have to follow these steps:
    • Configuring the following maven plugin
          <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>tomcat-maven-plugin</artifactId>
              <version>1.1</version>
              <configuration>
                   <server>myTomcat</server>
                   <url>http://localhost:9080/manager/text</url>
                   <path>/${project.build.finalName}</path>
              </configuration>
          </plugin>
    • Add the server with username and password to your settings.xml
    • Ensure the user has the role "manager-script" as described here
    • Run mvn tomcat:redeploy
      (I recommend to use redeploy as deploy works the first time only)
    If you use Tomcat 6, you must change the url of the tomcat maven plugin:
        <url>http://localhost:9080/manager</url>


    6. Test the STS with SoapUI

    This is a sample request (called RST) to the STS:

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
       <soap:Header>
          <wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
             <wsse:UsernameToken wsu:Id="UsernameToken-1">
                <wsse:Username>alice</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">ecila</wsse:Password>
             </wsse:UsernameToken>
          </wsse:Security>
       </soap:Header>
       <soap:Body>
          <wst:RequestSecurityToken xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
             <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType>
             <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</wst:KeyType>
             <wst: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/givenname"/>
                <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/emailaddress"/>
             </wst:Claims>
             <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
             <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
                <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
                   <wsa:Address>https://localhost:8081/doubleit/services/doubleittransportsaml1claims</wsa:Address>
                </wsa:EndpointReference>
             </wsp:AppliesTo>
          </wst:RequestSecurityToken>
       </soap:Body>
    </soap:Envelope>



    and this the expected response (called RSTR):

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
       <soap:Header/>
       <soap:Body>
          <RequestSecurityTokenResponseCollection xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns4="http://www.w3.org/2005/08/addressing" xmlns:ns5="http://docs.oasis-open.org/ws-sx/ws-trust/200802">
             <RequestSecurityTokenResponse>
                <TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</TokenType>
                <RequestedSecurityToken>
                   <saml2:Assertion ID="_ACF774CE2C8F387D9413183197088603" IssueInstant="2011-10-11T07:55:08.860Z" Version="2.0" xsi:type="saml2:AssertionType" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                      <saml2:Issuer>DoubleItSTSIssuer</saml2:Issuer>
                      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                         <ds:SignedInfo>
                            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                            <ds:Reference URI="#_ACF774CE2C8F387D9413183197088603">
                               <ds:Transforms>
                                  <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                                  <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                                     <ec:InclusiveNamespaces PrefixList="xs" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                                  </ds:Transform>
                               </ds:Transforms>
                               <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                               <ds:DigestValue>YIHAnHYol0pOs1Mc4MWhgwTP540=</ds:DigestValue>
                            </ds:Reference>
                         </ds:SignedInfo>
                         <ds:SignatureValue>Mb3WfLefs0KziHe7NjhLUBsgfD2spr8M3HpqqhpO+yzIqMrw9eY1r7nFIh3nWeDOHY4odPBa0w06XDpzPGSzdmm9k/Ay+S6trtkgS/Hoi3sL8CGAmAHEPWSO4+td6MNrucdVhG9P+do6JflXDOppDroGh/YjvxpdosM55G2TbL0=</ds:SignatureValue>
                         <ds:KeyInfo>
                            <ds:X509Data>REMOVED</ds:X509Certificate>
                            </ds:X509Data>
                         </ds:KeyInfo>
                      </ds:Signature>
                      <saml2:Subject>
                         <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="http://cxf.apache.org/sts">alice</saml2:NameID>
                         <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"/>
                      </saml2:Subject>
                      <saml2:Conditions NotBefore="2011-10-11T07:55:08.860Z" NotOnOrAfter="2011-10-11T08:00:08.860Z">
                         <saml2:AudienceRestriction>
                            <saml2:Audience>https://localhost:8081/doubleit/services/doubleittransportsaml1claims</saml2:Audience>
                         </saml2:AudienceRestriction>
                      </saml2:Conditions>
                      <saml2:AttributeStatement>
                         <saml2:Attribute Name="givenname" NameFormat="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
                            <saml2:AttributeValue xsi:type="xs:string">Alice</saml2:AttributeValue>
                         </saml2:Attribute>
                         <saml2:Attribute Name="surname" NameFormat="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
                            <saml2:AttributeValue xsi:type="xs:string">Smith</saml2:AttributeValue>
                         </saml2:Attribute>
                         <saml2:Attribute Name="emailaddress" NameFormat="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
                            <saml2:AttributeValue xsi:type="xs:string">alice@mycompany.org</saml2:AttributeValue>
                         </saml2:Attribute>
                      </saml2:AttributeStatement>
                   </saml2:Assertion>
                </RequestedSecurityToken>
                <RequestedAttachedReference>
                   <ns3:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
                      <ns3:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID">#_ACF774CE2C8F387D9413183197088603</ns3:KeyIdentifier>
                   </ns3:SecurityTokenReference>
                </RequestedAttachedReference>
                <RequestedUnattachedReference>
                   <ns3:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
                      <ns3:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID">_ACF774CE2C8F387D9413183197088603</ns3:KeyIdentifier>
                   </ns3:SecurityTokenReference>
                </RequestedUnattachedReference>
                <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
                   <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
                      <wsa:Address>https://localhost:8081/doubleit/services/doubleittransportsaml1claims</wsa:Address>
                   </wsa:EndpointReference>
                </wsp:AppliesTo>
                <Lifetime>
                   <ns2:Created>2011-10-11T07:55:08.872Z</ns2:Created>
                   <ns2:Expires>2011-10-11T08:00:08.872Z</ns2:Expires>
                </Lifetime>
             </RequestSecurityTokenResponse>
          </RequestSecurityTokenResponseCollection>
       </soap:Body>
    </soap:Envelope>



    Have fun!