JBoss.orgCommunity Documentation

PicketLink Reference Documentation

Version 2.1.7.Final


Table of Contents

1. Federation
1.1. Overview
1.2. SAML SSO
1.3. SAML Web Browser Profile
1.4. PicketLink SAML Specification Support
1.5. SAML v2.0
1.5.1. Which Profiles are supported ?
1.5.2. Which Bindings are supported ?
1.5.3. PicketLink Identity Provider (PIDP)
1.5.4. PicketLink Service Provider (PSP)
1.5.5. SAML Authenticators (Tomcat,JBossAS)
1.5.6. Digital Signatures in SAML Assertions
1.5.7. SAML2 Handlers
1.5.8. Single Logout
1.5.9. SAML2 Configuration Providers
1.5.10. Metadata Support
1.5.11. Token Registry
1.5.12. Standalone vs JBossAS Distribution
1.5.13. Standalone Web Applications(All Servlet Containers)
1.6. SAML v1.1
1.6.1. SAML v1.1
1.6.2. PicketLink SAML v1.1 Support
1.7. Trust
1.7.1. Security Token Server (STS)
1.8. Extensions
1.8.1. Extensions
1.8.2. PicketLinkAuthenticator
1.9. PicketLink API
1.9.1. Working with SAML Assertions
1.10. 3rd party integration
1.10.1. Picketlink as IDP, Salesforce as SP
1.10.2. Picketlink as SP, Salesforce as IDP
1.10.3. Picketlink as IDP, Google Apps as SP

List of Figures

1.1. TODO InformalFigure image title empty
1.2. TODO InformalFigure image title empty
1.3. TODO InformalFigure image title empty
1.4. TODO Gliffy image title empty
1.5. TODO InformalFigure image title empty
1.6. TODO InformalFigure image title empty
1.7. TODO InformalFigure image title empty
1.8. TODO InformalFigure image title empty
1.9. TODO InformalFigure image title empty
1.10. TODO InformalFigure image title empty
1.11. TODO InformalFigure image title empty

List of Examples

1.1. context.xml
1.2. context.xml
1.3. context.xml
1.4. context.xml
1.5. context.xml
1.6. WEB-INF/picketlink-handlers.xml
1.7. WEB-INF/picketlink-handlers.xml
1.8. WEB-INF/picketlink-handlers.xml
1.9. WEB-INF/picketlink-handlers.xml
1.10. WEB-INF/picketlink-handlers.xml
1.11. WEB-INF/picketlink-handlers.xml
1.12. WEB-INF/picketlink-handlers.xml
1.13. WEB-INF/picketlink-handlers.xml
1.14. web.xml
1.15. web.xml
1.16. jsp/login.jsp
1.17. jsp/error.jsp
1.18. STSWSClientTestCase.java
1.19. handlers.xml

The PicketLink Authenticator is basically a Tomcat Valve (org.apache.catalina.authenticator.FormAuthenticator). The only thing you need to do is change the valves configuration for your application.

This configuration changes for each supported binding.

IDPWebBrowserSSOValve from PicketLink provides the core IDP functionality on JBoss Application Server or Apache Tomcat.

Before configuring your application as an Identity Provider you need to add some configurations to your web.xml.

Let's start by defining a security-constraint element to restrict access to resources from unauthenticated users:

<security-constraint> 
    <web-resource-collection> 
        <web-resource-name>Manager command</web-resource-name> 
        <url-pattern>/*</url-pattern> 
    </web-resource-collection> 
    <auth-constraint> 
        <role-name>manager</role-name> 
    </auth-constraint> 
</security-constraint>


<security-role>
    <description>
      The role that is required to log in to IDP Application
    </description>
    <role-name>manager</role-name>
</security-role>

As you can see above, we define that only users with a role named manager are allowed to access the protected resources. Make sure to give your users the same role you defined here, otherwise they will get a 403 HTTP status code.

The next step is define your FORM login configuration using the login-config element:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>PicketLink IDP Application</realm-name>
    <form-login-config> 
        <form-login-page>/jsp/login.jsp</form-login-page> 
        <form-error-page>/jsp/login-error.jsp</form-error-page> 
    </form-login-config> 
</login-config>

Make sure you have inside your application the pages defined in the elements form-login-page and form-error-page .

Important

Please, make sure you have a welcome file page in your application. You can define it in your web.xml or simply create an index.jsp at the root directory of your application.

All the configuration for an especific Identity Provider goes at the WEB-INF/picketlink.xml file. This file is responsible to define the behaviour of the Authenticator. During the identity provider startup, the authenticator parses this file and configures itself.

Bellow is how the picketlink.xml file should looks like:

<PicketLink xmlns="urn:picketlink:identity-federation:config:2.1">


    <PicketLinkIDP xmlns="urn:picketlink:identity-federation:config:2.1">

        <IdentityURL>http://localhost:8080/idp/ </IdentityURL>


        <Trust>
            <Domains>locahost,mycompany.com</Domains>
        </Trust>

        <KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">

            <Auth Key="KeyStoreURL" Value="/jbid_test_keystore.jks" />
            <Auth Key="KeyStorePass" Value="store123" />
            <Auth Key="SigningKeyPass" Value="test123" />
            <Auth Key="SigningKeyAlias" Value="servercert" />

            <ValidatingAlias Key="localhost" Value="servercert" />
            <ValidatingAlias Key="127.0.0.1" Value="servercert" />

        </KeyProvider>

    </PicketLinkIDP>

    <PicketLinkSTS xmlns="urn:picketlink:identity-federation:config:1.0" TokenTimeout="1000" ClockSkew="1000">
        <TokenProviders>
            <TokenProvider ProviderClass="org.picketlink.identity.federation.core.saml.v2.providers.SAML20AssertionTokenProvider"
                TokenType="urn:oasis:names:tc:SAML:2.0:assertion" TokenElement="Assertion"
                TokenElementNS="urn:oasis:names:tc:SAML:2.0:assertion" />
        </TokenProviders>
    </PicketLinkSTS>

    <Handlers xmlns="urn:picketlink:identity-federation:handler:config:2.1">

        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2IssuerTrustHandler" />
        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler" />
        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler" />
        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler" />

     </Handlers>

</PicketLink>

This element defines the basic configuration for the identity provider. The table bellow provides more information about the attributes supported by this element:

Name

Description

Value

AssertionValidity

Defines the timeout for the SAML assertion validity, in miliseconds.

Defaults to 300000 . Deprecated. Use the PicketLinkSTS element, instead.

RoleGenerator

Defines the name of the org.picketlink. identity.federation. core.interfaces. RoleGenerator subclass to be used to obtain user roles.

Defaults to org.picketlink.identity. federation.core. impl.EmptyRoleGenerator .

AttributeManager

Defines the name of the org.picketlink. identity.federation. core.interfaces. AttributeManager subclass to be used to obtain the SAML assertion attributes.

Defautls to org.picketlink. identity.federation. core.impl. EmptyAttributeManager .

StrictPostBinding

SAML Web Browser SSO Profile has a requirement that the IDP does not respond back in Redirect Binding. Set this to false if you want to force the IDP to respond to SPs using the Redirect Binding.

Values: true|false . Defaults to true, the IDP always respond via POST Binding.

SupportsSignatures

Indicates if digital signature/verification of SAML assertions are enabled. If this attribute is marked to true the Service Providers must support signatures too, otherwise the SAML messages will be considered as invalid.

Values: true|false. Defaults to false.

Encrypt

Indicates if SAML Assertions should be encrypted. If this attribute is marked to true the Service Providers must support signatures too, otherwise the SAML messages will be considered as invalid.

Values: true|false. Defaults to false

IdentityParticipantStack

Defines the name of the org.picketlink. identity.federation. web.core. IdentityParticipantStack subclass to be used to register and deregister participants in the identity federation.

Defaults to org.picketlink. identity.federation. web.core. IdentityServer.STACK.

PicketLink provides some built-in Handlers to help the Identity Provider Authenticator processing the SAML requests and responses.

The handlers are configured through the Handlers element.

To issue/renew/cancel/validate SAML tokens, the IDP relies on the PicketLink STS API and configuration. This configurations define how the tokens should be used by the IDP.

This PicketLinkSTS element defines the basic configuration for the Security Token Service. The table bellow provides more information about the attributes supported by this element:

Name

Description

Value

STSName

Name for this STS configuration.

Name for this Security Token Service.

TokenTimeout

Defines the token timeout in miliseconds.

Defaults to 3600 miliseconds.

ClockSkew

Defines the clock skew, or timing skew, for the token timeout.

Defaults to 2000 miliseconds.

SignToken

Indicates if the tokens should be signed.

Values: true|false . Defaults to false .

EncryptToken

Indicates if the tokens should be encrypted.

Values: true|false . Defaults to false .

CanonicalizationMethod

Sets the canonicalization method.

Defaults to http://www.w3.org/2001/10/xml-exc-c14n#WithComments

All the configuration for an especific Service Providers goes at the WEB-INF/picketlink.xml file. This file is responsible to define the behaviour of the Authenticator. During the service provider startup, the authenticator parses this file and configures itself.

Bellow is how the picketlink.xml file should looks like:

<PicketLink xmlns="urn:picketlink:identity-federation:config:2.1">


    <PicketLinkSP xmlns="urn:picketlink:identity-federation:config:2.1"
        BindingType="REDIRECT"
        RelayState="someURL"
        ErrorPage="/someerror.jsp"
        LogOutPage="/customLogout.jsp"
        IDPUsesPostBinding="true"
        SupportsSignatures="true">

        <IdentityURL>http://localhost:8080/idp/ </IdentityURL>
        <ServiceURL>http://localhost:8080/employee/ </ServiceURL>

        <KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">

            <Auth Key="KeyStoreURL" Value="/jbid_test_keystore.jks" />
            <Auth Key="KeyStorePass" Value="store123" />
            <Auth Key="SigningKeyPass" Value="test123" />
            <Auth Key="SigningKeyAlias" Value="servercert" />

            <ValidatingAlias Key="localhost" Value="servercert" />
            <ValidatingAlias Key="127.0.0.1" Value="servercert" />

        </KeyProvider>

    </PicketLinkSP>

    <Handlers xmlns="urn:picketlink:identity-federation:handler:config:2.1">

        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2IssuerTrustHandler" />
        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler" />
        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler" />
        <Handler class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler" />

     </Handlers>

</PicketLink>

PicketLink provides some built-in Handlers to help the Service Provider Authenticator processing the SAML requests and responses.

The handlers are configured through the Handlers element.

The PicketLink Authenticator is basically a Tomcat Valve (org.apache.catalina.authenticator.FormAuthenticator). The only thing you need to do is change the valves configuration for your application.

This configuration changes for each supported binding.

SPRedirectSignatureFormAuthenticator is used to provide signature/encryption services to a Service Provider (SP) application for HTTP/Redirect binding of SAMLv2 specification. This authenticator

is an extension of the Section 1.5.4.4.6, “SPRedirectFormAuthenticator” .

SPRedirectFormAuthenticator provides the SAMLv2 HTTP/Redirect binding support for service provider (SP) applications.

SPPostSignatureFormAuthenticator is used to provide signature/encryption services to a Service Provider (SP) application for HTTP/POST binding of SAMLv2 specification. This authenticator

is an extension of the Section 1.5.4.4.8, “SPPostFormAuthenticator” .

SPPostFormAuthenticator is the main authenticator used to configure a service provider (SP) application for SAMLv2.0

To support digital signatures of SAML assertions you should define a KeyProvider element inside a PicketLinkIDP or PicketLinkSP.

Important

When using digital signatures you need to configure and enable it in both Identity Provider and Service Providers. Otherwise the SAML assertions would probably be considered as invalid.

<KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">
    <Auth Key="KeyStoreURL" Value="/jbid_test_keystore.jks" />
    <Auth Key="KeyStorePass" Value="store123" />
    <Auth Key="SigningKeyPass" Value="test123" />
    <Auth Key="SigningKeyAlias" Value="servercert" />

    <ValidatingAlias Key="idp.example.com" Value="servercert" />
    <ValidatingAlias Key="localhost" Value="servercert" />
</KeyProvider>

In order to configure the KeyProvider, you need to specify some configurations about the Java KeyStore that should be used to sign SAML assertions:

Auth Key

Description

KeyStoreURL

Where the value of the Value attribute points to the location of a Java KeyStore with the properly installed certificates.

KeyStorePass

Where the value of the Value attribute refers to the password of the referenced Java KeyStore.

SigningKeyAlias

Where the value of the Value attribute refers to the password of the installed certificate to be used to sign the SAML assertions.

SigningKeyPass

Where the value of the Value attribute refers to the alias of the certificate to be used to sign the SAML assertions.

The Service Provider also needs to know how to verify the signatures for the SAML assertions. This is done by the ValidationAlias elements.

<ValidatingAlias Key="idp.example.com" Value="servercert" />

Tip

Note that we declare the validating certificate for each domain using the ValidatingAlias .

At the IDP side you need an entry for each server/domain name defined as a trusted domain (Trust/Domains elements).

At the SP side you need an entry for the the server/domain name where the IDP is deployed.

When digital signatures are enabled, the authenticator will look at the ValidatingAlias table for a entry that matches the value of the Key attribute with the host name of the Issuer of the SAML assertion. For example, consider the following SAML Assertion issued by an Identity Provider located at http://idp.example.com:

<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="ID_ab0392ef-b557-4453-95a8-a7e168da8ac5" IssueInstant="2010-09-30T19:13:37.869Z"
    Version="2.0">
    <saml2:Issuer>http://idp.example.com </saml2:Issuer>
    <saml2:Subject>
        <saml2:NameID NameQualifier="urn:picketlink:identity-federation">jduke</saml2:NameID>
        <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" />
    </saml2:Subject>
    <saml2:Conditions NotBefore="2010-09-30T19:13:37.869Z"
         NotOnOrAfter="2010-09-30T21:13:37.869Z" />
    <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#WithComments" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmlds#rsa-sha1" />
            <ds:Reference URI="#ID_ab0392ef-b557-4453-95a8-a7e168da8ac5">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmlds#enveloped-signature" />
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmlds#sha1" />
                <ds:DigestValue>0Y9QM5c5qCShz5UWmbFzBmbuTus=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>
            se/flQ2htUQ0IUYieVkXNn9cfjnfgv6H99nFarsTNTpRI9xuSlw5OTai/2PYdZI2Va9+QzzBf99m
            VFyigfFdfrqug6aKFhF0lsujzlFfPfmXBbDRiTFX+4SkBeV71uuy7rOUI/jRiitEA0QrKqs0e/pV
            \+C8PoaariisK96Mtt7A=
        </ds:SignatureValue>
        <ds:KeyInfo>
            <ds:KeyValue>
                <ds:RSAKeyValue>
                    <ds:Modulus>
                        suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1
                        dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJwspqVnMuRK19ju2dxpKw
                        lYGGtrP5VQv00dfNPbs=
                    </ds:Modulus>
                    <ds:Exponent>AQAB</ds:Exponent>
                </ds:RSAKeyValue>
            </ds:KeyValue>
        </ds:KeyInfo>
    </ds:Signature>
</saml2:Assertion>

During the signature validation for this SAML assertion, the authenticator (in this case a Service Provider Authenticator) will try to find a ValidationAlias element with the value idp.example.com for its Key attribute. This alias references a certificate in your Java KeyStore that will be used to check the signature validity.

Usually, Java KeyStores would contain a key pair (public and private keys) to be used for signing and validating messages for an specific server and the trusted public keys to be used to validate messages received from others servers.

#

Name

Type

Objective

SP/IDP

Since Version

1

CLOCK_ SKEW_ MILIS

string

a long value in miliseconds to add a clock skew to assertion expiration validation at the Service provider

SP

2.0

2

DISABLE_ AUTHN_ STATEMENT

boolean

Setting a value will disable the generation of an AuthnStatement

IDP

2.0

3

DISABLE_ SENDING_ ROLES

boolean

Setting any value will disable the generation and return of roles to SP

IDP

2.0

4

DISABLE_ ROLE_ PICKING

boolean

Setting to true will disable picking IDP attribute statements

SP

2.0

5

ROLE_ KEY

String

a csv list of strings that represent the roles coming from IDP

SP

2.0

6

ASSERTION_ CONSUMER_ URL

String

the url to be used for assertionConsumerURL

SP

2.0

7

NAMEID_ FORMAT

String

Setting to a value will provide the nameid format to be sent to IDP

SP

2.0

8

ASSERTION_ SESSION_ ATTRIBUTE_ NAME

String

Specifies the name of the session attribute where the assertion will be stored. The assertion is stored as a DOM Document. This option is useful when you need to obtain the user's assertion to propagate or validate it against the STS.

SP

2.1.7

Table of Contents

Even though the SAML v2.0 specification has support for Global Logout, you have to use it very very wisely. Just remember that you need to keep the participants to a low number (say upto 5 participants with one IDP).

Global Logout : The user initiates GLO at one service provider which will log out the user at the IDP and all the service providers.

Local Logout : The user logs out of one service provider only. The session at the IDP and other service providers is intact.

The service provider url should be appended with "?LLO=true"

Basically, in the service provider page, have a url that has the query parameter.

Assume, your service provider is http://localhost:8080/sales/, then the url for the local log out would be http://localhost:8080/sales/?LLO=true

When using LLO, you must be aware of some security implications. The user is only disconnect from the service provider from which he logged out, which means that the user's session in the identity provider and others service providers are still active. In other words, the user's SSO session is still active and he is still able to log in in any other service provider. We strongly recommend to always use the Single Logout Profile (GLO).

Important

In the case of LLO, the service provider invalidates the session and forwards to a default logout page (logout.jsp) .Custom logout page can be configured in picketlink.xml page. Please refer to Service Provider Configuration.

Table of Contents

It is possible to use different Configuration Providers at the IDP and SP.

The configuration providers will then be the sole configuration leaders (instead of picketlink.xml)

Table of Contents

To configure the SAML Metadata Configuration Providers you need to follow these steps:

  • Define the PicketLink Authenticator (SP or IDP valves) and provide the configuration provider class name as an attribute

  • Depending if you're configuring an IDP or SP, provide a metadata file and put it on the classpath:

  • For Identity Providers : WEB-INF/classes/idp-metadata.xml

  • For Service Providers : WEB-INF/classes/sp-metadata.xml

Token Registries are configured through the PicketLinkSTS (Security Token Service configuration) element in the WEB-INF/picketlink.xml file:

<PicketLinkSTS xmlns="urn:picketlink:identity-federation:config:1.0" TokenTimeout="5000" ClockSkew="0">
        <TokenProviders>
		<TokenProvider
			ProviderClass="org.picketlink.identity.federation.core.saml.v2.providers.SAML20AssertionTokenProvider"
			TokenType="urn:oasis:names:tc:SAML:2.0:assertion"
			TokenElement="Assertion" TokenElementNS="urn:oasis:names:tc:SAML:2.0:assertion">
			<Property Key="TokenRegistry" Value="org.picketlink.identity.federation.core.sts.registry.JPABasedTokenRegistry" />
		</TokenProvider>
	</TokenProviders>
</PicketLinkSTS>

The example above uses a SAML v2 Token Provider configured with the org.picketlink.identity.federation.core.sts.registry.JPABasedTokenRegistry implementation. This is done by the TokenRegistry property.

If your IDP or SP applications are not running on JBoss Application Server or Apache Tomcat, then you can use the standalone mode of PicketLink.

For an IDP web application to be SAML enabled on any Servlet Container, you will have to add listeners and servlets as shown in the web.xml below:

Part of the idp-standalone.war


A jsp for login would be:


The jsp for error would be:


Page to list all the JBoss Web Services handlers that are part of the PicketLink project.

  1. SAML2Handler

  2. BinaryTokenHandler

  3. WSAuthenticationHandler

  4. WSAuthorizationHandler

This is a JBossWS handler (stack agnostic) that supports the SAML token profile of the Oasis Web Services Security (WSS) standard.

It can be configured both on the client side and the server side. The configuration is shown below both the client(outbound) as well as server(inbound).

This is the behavior when the handler is configured on the client side.

The client side usage is shown in the following client class. If you need to use an XML file to specify the handler on the client side, then please look in the references section below.

Example 1.18. STSWSClientTestCase.java

package org.picketlink.test.trust.tests;

import java.net.URL;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;

import org.junit.Test;
import org.picketlink.identity.federation.api.wstrust.WSTrustClient;
import org.picketlink.identity.federation.api.wstrust.WSTrustClient.SecurityInfo;
import org.picketlink.identity.federation.core.wstrust.WSTrustException;
import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil;
import org.picketlink.test.trust.ws.WSTest;
import org.picketlink.trust.jbossws.SAML2Constants;
import org.picketlink.trust.jbossws.handler.SAML2Handler;
import org.w3c.dom.Element;

/**
 * A Simple WS Test for the SAML Profile of WSS
 * @author Marcus Moyses
 * @author Anil Saldhana
 */
public class STSWSClientTestCase
{
   private static String username = "UserA";
   private static String password = "PassA";

   @SuppressWarnings("rawtypes")
   @Test
   public void testWSInteraction() throws Exception {
      WSTrustClient client = new WSTrustClient("PicketLinkSTS", "PicketLinkSTSPort",
            "http://localhost:8080/picketlink-sts/PicketLinkSTS",
            new SecurityInfo(username, password));
    Element assertion = null;
    try {
        System.out.println("Invoking token service to get SAML assertion for " + username);
        assertion = client.issueToken(SAMLUtil.SAML2_TOKEN_TYPE);
        System.out.println("SAML assertion for " + username + " successfully obtained!");
    } catch (WSTrustException wse) {
        System.out.println("Unable to issue assertion: " + wse.getMessage());
        wse.printStackTrace();
        System.exit(1);
    }

    URL wsdl = new URL("http://localhost:8080/picketlink-wstest-tests/WSTestBean?wsdl");
    QName serviceName = new QName("http://ws.trust.test.picketlink.org/", "WSTestBeanService");
    Service service = Service.create(wsdl, serviceName);
    WSTest port = service.getPort(new QName("http://ws.trust.test.picketlink.org/", "WSTestBeanPort"), WSTest.class);
    BindingProvider bp = (BindingProvider)port;
    bp.getRequestContext().put(SAML2Constants.SAML2_ASSERTION_PROPERTY, assertion);
    List<Handler> handlers = bp.getBinding().getHandlerChain();
    handlers.add(new SAML2Handler());
    bp.getBinding().setHandlerChain(handlers);

    port.echo("Test");
   }
}

Note: the SAML2Handler is instantiated and added to the handler list that is obtained from the BindingProvider binding.

There are two ways by which the SAML2Handler picks the SAML2 Assertion to send via the SOAP message.

  • The Client can push the SAML2 Assertion into the SOAP MessageContext under the key " org.picketlink.trust.saml.assertion ". In the example code above, look in the call bindingProvider.getRequestContext().put(xxxxx)

  • The SAML2 Assertion is available as part of the JAAS subject on the security context. This can happen if there has been a JAAS interaction with the usage of PicketLink STS login modules.

Assume that you have a POJO.

package org.picketlink.test.trust.ws;

import javax.jws.HandlerChain;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

/**
 * POJO that is exposed as WS
 * @author Anil Saldhana
 */
@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
@HandlerChain(file="authorize-handlers.xml")
public class POJOBean
{
   @WebMethod
   public void echo(String echo)
   {
      System.out.println(echo);
   }

   @WebMethod
   public void echoUnchecked(String echo)
   {
      System.out.println(echo);
   }
}

Note the use of the @HandlerChain annotation that defines the handler xml.

The handler xml is authorize-handlers.xml.

<?xml version="1.0" encoding="UTF-8"?>


<handler-chains xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee javaee_web_services_1_2.xsd">

  <handler-chain>


    <handler>
      <handler-name>WSAuthorizationHandler</handler-name>
      <handler-class>org.picketlink.trust.jbossws.handler.WSAuthorizationHandler</handler-class>
    </handler>

    <handler>
      <handler-name>WSAuthenticationHandler</handler-name>
      <handler-class>org.picketlink.trust.jbossws.handler.WSAuthenticationHandler</handler-class>
    </handler>

    <handler>
      <handler-name>SAML2Handler</handler-name>
      <handler-class>org.picketlink.trust.jbossws.handler.SAML2Handler</handler-class>
    </handler>


  </handler-chain>


</handler-chains>

Warning

Note : The order of execution of the handlers is SAML2Handler, WSAuthenticationHandler and WSAuthorizationHandler. These need to be defined in reverse order in the xml.

Since we intend to expose a POJO as a webservice, we need to package in a web archive (war).

The web.xml is:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<servlet>
		<display-name>POJO Web Service</display-name>
		<servlet-name>POJOBeanService</servlet-name>
		<servlet-class>org.picketlink.test.trust.ws.POJOBean</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>POJOBeanService</servlet-name>
		<url-pattern>/POJOBeanService</url-pattern>
	</servlet-mapping>
</web-app>

Warning

Please do not define any <security-constraint> in the web.xml

The jboss-web.xml is:

<jboss-web>
  <security-domain>sts</security-domain>
</jboss-web>

The jboss-wsse.xml is

<jboss-ws-security xmlns="http://www.jboss.com/ws-security/config"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.jboss.com/ws-security/config
                   http://www.jboss.com/ws-security/schema/jboss-ws-security_1_0.xsd">

  <port name="POJOBeanPort">
    <operation name="{http://ws.trust.test.picketlink.org/}echoUnchecked">
      <config>
        <authorize>
          <unchecked/>
        </authorize>
      </config>
    </operation>

    <operation name="{http://ws.trust.test.picketlink.org/}echo">
      <config>
        <authorize>
          <role>JBossAdmin</role>
        </authorize>
      </config>
    </operation>
  </port>



</jboss-ws-security>

As you can see, there are two operations defined on the POJO web services and each of these operations require different access control. The echoUnchecked() method allows free access to any authenticated user whereas the echo() method requires the caller to have "JBossAdmin" role.

The war should look as:

anil@localhost:~/picketlink/picketlink/integration-tests/trunk/picketlink-trust-tests$ jar tvf target/pojo-test.war
     0 Mon Apr 11 19:48:32 CDT 2011 META-INF/
   123 Mon Apr 11 19:48:30 CDT 2011 META-INF/MANIFEST.MF
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/classes/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/classes/org/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/classes/org/picketlink/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/classes/org/picketlink/test/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/classes/org/picketlink/test/trust/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/classes/org/picketlink/test/trust/ws/
     0 Mon Apr 11 19:48:30 CDT 2011 WEB-INF/lib/
   858 Mon Apr 11 19:48:26 CDT 2011 WEB-INF/classes/authorize-handlers.xml
  1021 Mon Apr 11 19:48:28 CDT 2011 WEB-INF/classes/org/picketlink/test/trust/ws/POJOBean.class
    65 Mon Apr 11 12:00:32 CDT 2011 WEB-INF/jboss-web.xml
   770 Mon Apr 11 17:44:16 CDT 2011 WEB-INF/jboss-wsse.xml
   598 Mon Apr 11 16:25:46 CDT 2011 WEB-INF/web.xml
     0 Mon Apr 11 19:48:32 CDT 2011 META-INF/maven/
     0 Mon Apr 11 19:48:32 CDT 2011 META-INF/maven/org.picketlink/
     0 Mon Apr 11 19:48:32 CDT 2011 META-INF/maven/org.picketlink/picketlink-integration-trust-tests/
  7918 Mon Apr 11 18:56:16 CDT 2011 META-INF/maven/org.picketlink/picketlink-integration-trust-tests/pom.xml
   142 Mon Apr 11 19:48:30 CDT 2011 META-INF/maven/org.picketlink/picketlink-integration-trust-tests/pom.properties
anil@localhost:~/picketlink/picketlink/integration-tests/trunk/picketlink-trust-tests

The Test Case is something like:

 package org.picketlink.test.trust.tests;

import java.net.URL;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;

import org.junit.Test;
import org.picketlink.identity.federation.api.wstrust.WSTrustClient;
import org.picketlink.identity.federation.api.wstrust.WSTrustClient.SecurityInfo;
import org.picketlink.identity.federation.core.wstrust.WSTrustException;
import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil;
import org.picketlink.test.trust.ws.WSTest;
import org.picketlink.trust.jbossws.SAML2Constants;
import org.picketlink.trust.jbossws.handler.SAML2Handler;
import org.w3c.dom.Element;

/**
 * A Simple WS Test for POJO WS Authorization using PicketLink
 * @author Anil Saldhana
 * @since Oct 3, 2010
 */
public class POJOWSAuthorizationTestCase
{
   private static String username = "UserA";
   private static String password = "PassA";

   @SuppressWarnings("rawtypes")
   @Test
   public void testWSInteraction() throws Exception
   {
      // Step 1:  Get a SAML2 Assertion Token from the STS
      WSTrustClient client = new WSTrustClient("PicketLinkSTS", "PicketLinkSTSPort",
            "http://localhost:8080/picketlink-sts/PicketLinkSTS",
            new SecurityInfo(username, password));
      Element assertion = null;
      try {
         System.out.println("Invoking token service to get SAML assertion for " + username);
         assertion = client.issueToken(SAMLUtil.SAML2_TOKEN_TYPE);
         System.out.println("SAML assertion for " + username + " successfully obtained!");
      } catch (WSTrustException wse) {
         System.out.println("Unable to issue assertion: " + wse.getMessage());
         wse.printStackTrace();
         System.exit(1);
      }

      // Step 2: Stuff the Assertion on the SOAP message context and add the SAML2Handler to client side handlers
      URL wsdl = new URL("http://localhost:8080/pojo-test/POJOBeanService?wsdl");
      QName serviceName = new QName("http://ws.trust.test.picketlink.org/", "POJOBeanService");
      Service service = Service.create(wsdl, serviceName);
      WSTest port = service.getPort(new QName("http://ws.trust.test.picketlink.org/", "POJOBeanPort"), WSTest.class);
      BindingProvider bp = (BindingProvider)port;
      bp.getRequestContext().put(SAML2Constants.SAML2_ASSERTION_PROPERTY, assertion);
      List<Handler> handlers = bp.getBinding().getHandlerChain();
      handlers.add(new SAML2Handler());
      bp.getBinding().setHandlerChain(handlers);

      //Step 3: Access the WS. Exceptions will be thrown anyway.
      port.echo("Test");
   }
}

PicketLink provides ways to protect your EJB endpoints using a SAML Security Token Service. This means that you can apply some security to your EJBs where only users with a valid SAML assertion can invoke to them.

This scenario is very common if you are looking for:

  1. Leverage your Single Sign-On infrastructure to your service layer (EJBs, Web Services, etc)

  2. Integrate your SAML Service Providers with your services by trusting the assertion previously issued by the Identity Provider

  3. Any situation that requires the propagation of authorization/authentication information from one domain to another

The client must first obtain the SAML assertion from PicketLink STS by sending a WS-Trust request to the token service. This process usually involves authentication of the client. After obtaining the SAML assertion from the STS, the client includes the assertion in the security context of the EJB request before invoking an operation on the bean. Upon receiving the invocation, the EJB container extracts the assertion and validates it by sending a WS-Trust validate message to the STS. If the assertion is considered valid by the STS (and the proof of possession token has been verified if needed), the client is authenticated.


On JBoss, the SAML assertion validation process is handled by the SAML2STSLoginModule. It reads properties from a configurable file (specified by the configFile option) and establishes communication with the STS based on these properties. We will see how a configuration file looks like later on. If the assertion is valid, a Principal is created using the assertion subject name and if the assertion contains roles, these roles are also extracted and associated with the caller's Subject.

The client must first obtain the SAML assertion from the PicketLink STS or you Identity Provider. This process usually involves authentication of the client. After obtaining the SAML assertion, the client includes the assertion in the security context of the EJB request before invoking an operation on the bean. Upon receiving the invocation, the EJB container extracts the assertion and validates it by sending a WS-Trust validate message to the STS. If the assertion is considered valid by the STS (and the proof of possession token has been verified if needed), the client is authenticated.

On JBoss, the SAML assertion validation process is handled by the Section 1.7.1.5.3, “SAML2STSLoginModule” . It reads properties from a configurable file (specified by the configFile option) and establishes communication with the STS based on these properties. We will see how a configuration file looks like later on. If the assertion is valid, a Principal is created using the assertion subject name and if the assertion contains roles, these roles are also extracted and associated with the caller's Subject.

This section will cover two possible scenarios to protect and access your secured EJB endpoints. The main difference between these two scenarios is where the EJB client is deployed.

  • Remote EJB Client using JNDI

  • EJB Client is deployed at the same instance than your EJB endpoints

Important

Before starting, please take a look at the following documentation Remote EJB invocations via JNDI .

The configuration described in this section only works with versions 7.2.0+ and 7.1.3+ of JBoss Application Server.

If your endpoints are accessible from remote clients (in a different VM or server than your endpoints) you need to configure your JBoss Application Server 7 to allow use a SAML Assertion during the InitialContext creation.

Basically, the configuration envolves the following steps:

  1. Add a new Security Realm to your standalone.xml

  2. Create a Security Domain using the Section 1.7.1.5.3, “SAML2STSLoginModule”

  3. Change the Remoting Connector to use the new Security Realm

Important

Security Realms are better described in the JBoss Application Server Documentation.

Edit your standalone.xml and add the following configuration for a new Security Realm:

<security-realm name="SAMLRealm">
    <authentication>
        <jaas name="ejb-remoting-sts"/>
    </authentication>
</security-realm>

The configuration above defines a Security Realm that delegates the username/password information to a JAAS Security Domain (that we'll create later) in order to authenticate an user.

When using the JAAS configuration for Security Realms, the remoting subsystem enables the PLAIN SASL authentication. This will allow your remote clients send the username/password where the password would be the previously issued SAML Assertion.In our case, the password will be the String representation of the SAML Assertion.

Tip

Make sure you also enable SSL. Otherwise all communication with the server will be done using plain text.

This page references the PicketLink Login Modules for the Security Token Server.

Options Include:

  • configFile : a properties file that gives details on the STS to the login module. This can be optional if you want to specify values directly.

  • handlerChain : Comma separated list of handlers you need to set for handling outgoing message to STS. Values: binary (to inject BinaryTokenHandler), saml2 (to inject SAML2Handler), map (to inject MapBasedTokenHandler) or class name of your own handler with default constructor.

  • cache.invalidation : set it to "true" if you want the JBoss auth cache to invalidate caches based on saml token expiry. By default, this value is false.

  • inject.callerprincipa l: set it to "true" if the login module should add a group principal called "CallerPrincipal" to the subject. This is useful in JBoss AS for programmatic security in web/ejb components.

  • groupPrincipalName : by default, JBoss AS security uses "Roles" as the group principal name in the subject. You can give a different value.

  • endpointAddress : endpoint url of STS

  • serviceName : service Name of STS

  • portName : port name of STS

  • username : username of account on STS.

  • password : password of account on STS

  • wsaIssuer : if you need to customize the WS-Addressing Issuer address in the WS-Trust call to the STS.

  • wspAppliesTo : if you need to customize the WS-Policy AppliesTo in the WS-Trust call to the STS.

  • securityDomainForFactory : if you have a JaasSecurityDomain mbean service in JBoss AS that provides the truststore.

  • map.token.key : key to find binary token in JAAS sharedState map. Defaults to "ClientID".

  • soapBinding : allow to change SOAP binding for SAML reuest.

  • requestType : allows to override SAML request type when sending request to STS. Default: "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue" Other possible value: "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Validate".

Note: The configFile option is optional. If you provide that, then it should be as below.

Configuration file such as sts-client.properties.

serviceName=PicketLinkSTS

portName=PicketLinkSTSPort

endpointAddress=http://localhost:8080/picketlink-sts/PicketLinkSTS

username=admin

password=admin

wsaIssuer=http://localhost:8080/someissuer

wspAppliesTo=http://localhost:8080/testws

Note:

  • the password can be masked according to http://community.jboss.org/wiki/PicketLinkConfigurationMaskpassword which would give us something like, password=MASK-dsfdsfdslkfh

  • wsaIssuer can be optionally added if you want a value for the WS-Addressing issuer in the WS-Trust call to the STS.

  • wspAppliesTo can be optionally added if you want a value for WS-Policy AppliesTo in the WS-Trust call to the STS.

  • serviceName, portName, endpointAddress are mandatory .

  • username and password keys are not needed if you are using mutual authenticated ssl (MASSL) with the STS.

Either you specify the module options directly or you can use a properties file for the STS related properties.

If the STS sends roles via Attribute Statements in the SAML assertion, then the user has to use the SAMLRoleLoginModule.

<application-policy name="saml">
  <authentication>
    <login-module code="org.picketlink.trust.jbossws.jaas.JBWSTokenIssuingLoginModule"  flag="required">
         <module-option name="endpointAddress">SOME_URL</module-option>
         <module-option name="serviceName">SecurityTokenService</module-option>
         <module-option name="portName">RequestSecurityToken</module-option>
         <module-option name="inject.callerprincipal">true</module-option>
         <module-option name="handlerChain">binary</module-option>
    </login-module>
    <login-module code="org.picketlink.trust.jbossws.jaas.SAMLRoleLoginModule"  flag="required"/>
  </authentication>
</application-policy>

If the STS does not send roles, then the user has to configure a different JAAS login module to pick the roles for the username. Something like the UsernamePasswordLoginModule.

<application-policy xmlns="urn:jboss:security-beans:1.0" name="binary">
      <authentication>
         <login-module code="org.picketlink.trust.jbossws.jaas.JBWSTokenIssuingLoginModule" flag="required">
            <module-option name="endpointAddress">http://localhost:8080/picketlink-sts/PicketLinkSTS</module-option>
            <module-option name="serviceName">PicketLinkSTS</module-option>
            <module-option name="portName">PicketLinkSTSPort</module-option>
            <module-option name="inject.callerprincipal">true</module-option>
            <module-option name="handlerChain">binary</module-option>
            <module-option name="username">admin</module-option>
            <module-option name="password">MASK-0BbleBL2LZk=</module-option>
            <module-option name="salt">18273645</module-option>
            <module-option name="iterationCount">56</module-option>
            <module-option name="useOptionsCredentials">true</module-option>
            <module-option name="overrideDispatch">true</module-option>
            <module-option name="wspAppliesTo">http://services.testcorp.org/provider1</module-option>
            <module-option name="wsaIssuer">http://something</module-option>
            <module-option name="password-stacking">useFirstPass</module-option>
         </login-module>

         <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
            <module-option name="usersProperties">sts-users.properties</module-option>
            <module-option name="rolesProperties">sts-roles.properties</module-option>
            <module-option name="password-stacking">useFirstPass</module-option>
         </login-module>
      </authentication>
   </application-policy>

This module defines the following module options:

  • configFile - this property identifies the properties file that will be used to establish communication with the external security token service.

  • cache.invalidation : set it to true if you require invalidation of JBoss Auth Cache at SAML Principal expiration.

  • jboss.security.security_domain -security domain at which Principal will expire if cache.invalidation is used.

  • roleKey : key of the attribute name that we need to use for Roles from the SAML assertion. This can be a comma-separated string values such as (Role,Membership)

  • localValidation : if you want to validate the assertion locally for signature and expiry

  • localValidationSecurityDomain : the security domain for the trust store information (via the JaasSecurityDomain)

  • tokenEncodingType : encoding type of SAML token delivered via http request's header. Possible values are:

    • base64 - content encoded as base64. In case of encoding will vary between base64 and gzip use base64 and LoginModule will detect gzipped data.

    • gzip - gzipped content encoded as base64

    • none - content not encoded in any way

  • samlTokenHttpHeader - name of http request header to fetch SAML token from. For example: "Authorize"

  • samlTokenHttpHeaderRegEx - Java regular expression to be used to get SAML token from "samlTokenHttpHeader". Example: use: . "(. )".* to parse SAML token from header content like this: SAML_assertion="HHDHS=", at the same time set samlTokenHttpHeaderRegExGroup to 1.

  • samlTokenHttpHeaderRegExGroup - Group value to be used when parsing out value of http request header specified by "samlTokenHttpHeader" using "samlTokenHttpHeaderRegEx".

pattern = Pattern.compile(samlTokenHttpHeaderRegEx, Pattern.DOTALL);
Matcher m = pattern.matcher(content);
m.matches();
m.group(samlTokenHttpHeaderRegExGroup)

Any properties specified besides the above properties are assumed to be used to configure how the STSClient will connect to the STS. For example, the JBossWS StubExt.PROPERTY_SOCKET_FACTORY can be specified in order to inform the socket factory that must be used to connect to the STS. All properties will be set in the request context of the Dispatch instance used by the STSClient to send requests to the STS.

An example of a configFile can be seen bellow:

serviceName=PicketLinkSTS
portName=PicketLinkSTSPort
endpointAddress=[http://localhost:8080/picketlink-sts/PicketLinkSTS]
username=JBoss
password=JBoss

The first three properties specify the STS endpoint URL, service name, and port name. The last two properties specify the username and password that are to be used by the application server to authenticate to the STS and have the SAML assertions validated.

NOTE: Sub-classes can use getSTSClient() method to customize the STSClient class to make calls to STS

Example Configuration 1:

<application-policy xmlns="urn:jboss:security-beans:1.0" name="cache-test">
      <authentication>
         <login-module code="org.picketlink.identity.federation.bindings.jboss.auth.SAML2STSLoginModule" flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="configFile">sts-config.properties</module-option>
            <module-option name="cache.invalidation">true</module-option>
            <module-option name="localValidation">true</module-option>
            <module-option name="localValidationSecurityDomain">MASSL</module-option>
         </login-module>
      </authentication>
   </application-policy>

Example Configuration 2 using http header and local validation:

   <application-policy xmlns="urn:jboss:security-beans:1.0" name="service">
      <authentication>
         <login-module code="org.picketlink.identity.federation.bindings.jboss.auth.SAML2STSLoginModule" flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="cache.invalidation">true</module-option>
            <module-option name="localValidation">true</module-option>
            <module-option name="localValidationSecurityDomain">java:jaas/localValidationDomain</module-option>
            <module-option name="tokenEncodingType">gzip</module-option>
            <module-option name="samlTokenHttpHeader">Auth</module-option>
            <module-option name="samlTokenHttpHeaderRegEx">.*"(.*)".*</module-option>
            <module-option name="samlTokenHttpHeaderRegExGroup">1</module-option>
         </login-module>
         <login-module code="org.picketlink.trust.jbossws.jaas.SAMLRoleLoginModule" flag="required"/>
      </authentication>
   </application-policy>

In case of local validation here is example of jboss-beans.xml file to use to configure JAAS Security Domain for (JBoss AS6 or EAP5):

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <!-- localValidationDomain bean -->
   <bean name="LocalValidationBean" class="org.jboss.security.plugins.JaasSecurityDomain">
     <constructor>
          <parameter>localValidationDomain</parameter>
     </constructor>
     <property name="keyStoreURL">file://${jboss.server.home.dir}/conf/stspub.jks</property>
     <property name="keyStorePass">keypass</property>
     <property name="keyStoreAlias">sts</property>
     <property name="securityManagement"><inject bean="JNDIBasedSecurityManagement"/></property>
   </bean>
</deployment>

For JBoss AS7 or JBoss EAP6 add following security domain to your configuration file:

<security-domain name="localValidationDomain">
      <jsse
         keystore-password="keypass"
         keystore-type="JKS"
         keystore-url="file:///${jboss.server.config.dir}/stspub.jks"
         server-alias="sts"/>
</security-domain>

and reference this security domain as: <module-option name="localValidationSecurityDomain">localValidationDomain</module-option>.

org.picketlink.identity.federation.bindings.jboss.auth. SAMLTokenCertValidatingLoginModule

This module defines the following module options:

  • roleKey : key of the attribute name that we need to use for Roles from the SAML assertion. This can be a comma-separated string values such as (Role,Membership)

  • localValidationSecurityDomain : the security domain for the trust store information (via the JaasSecurityDomain)

  • cache.invalidation - set it to true if you require invalidation of JBoss Auth Cache at SAML Principal expiration.

  • jboss.security.security_domain -security domain at which Principal will expire if cache.invalidation is used.

  • tokenEncodingType : encoding type of SAML token delivered via http request's header. Possible values are:

    • base64 - content encoded as base64. In case of encoding will vary between base64 and gzip use base64 and LoginModule will detect gzipped data.

    • gzip - gzipped content encoded as base64

    • none - content not encoded in any way

  • samlTokenHttpHeader - name of http request header to fetch SAML token from. For example: "Authorize"

  • samlTokenHttpHeaderRegEx - Java regular expression to be used to get SAML token from "samlTokenHttpHeader". Example: use: . "(. )".* to parse SAML token from header content like this: SAML_assertion="HHDHS=", at the same time set samlTokenHttpHeaderRegExGroup to 1.

  • samlTokenHttpHeaderRegExGroup - Group value to be used when parsing out value of http request header specified by "samlTokenHttpHeader" using "samlTokenHttpHeaderRegEx".

pattern = Pattern.compile(samlTokenHttpHeaderRegEx, Pattern.DOTALL);
Matcher m = pattern.matcher(content);
m.matches();
m.group(samlTokenHttpHeaderRegExGroup)

Example Configuration 1:

<application-policy xmlns="urn:jboss:security-beans:1.0" name="certpath">
      <authentication>
         <login-module code="org.picketlink.identity.federation.bindings.jboss.auth.SAMLTokenCertValidatingLoginModule" flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="cache.invalidation">true</module-option>
            <module-option name="localValidationSecurityDomain">java:jaas/localValidationDomain</module-option>
         </login-module>
      </authentication>
</application-policy>

Example Configuration 2 using http header:

   <application-policy xmlns="urn:jboss:security-beans:1.0" name="service">
      <authentication>
         <login-module code="org.picketlink.identity.federation.bindings.jboss.auth.SAML2STSLoginModule" flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="cache.invalidation">true</module-option>
            <module-option name="localValidationSecurityDomain">java:jaas/localValidationDomain</module-option>
            <module-option name="tokenEncodingType">gzip</module-option>
            <module-option name="samlTokenHttpHeader">Auth</module-option>
            <module-option name="samlTokenHttpHeaderRegEx">.*"(.*)".*</module-option>
            <module-option name="samlTokenHttpHeaderRegExGroup">1</module-option>
         </login-module>
      </authentication>
   </application-policy>

Example of jboss-beans.xml file to use to configure JAAS Security Domain containing trust store for above examples:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <!-- localValidationDomain bean -->
   <bean name="LocalValidationBean" class="org.jboss.security.plugins.JaasSecurityDomain">
     <constructor>
          <parameter>localValidationDomain</parameter>
     </constructor>
     <property name="keyStoreURL">file://${jboss.server.home.dir}/conf/stspub.jks</property>
     <property name="keyStorePass">keypass</property>
     <property name="keyStoreAlias">sts</property>
     <property name="securityManagement"><inject bean="JNDIBasedSecurityManagement"/></property>
   </bean>
</deployment>

Your web.xml will define some security constraints. But it will define a <login-config> that is different from the servlet specifcation mandated BASIC, CLIENT-CERT, FORM or DIGEST methods. We suggest the use of SECURITY_DOMAIN as the method.

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Restricted Access - Get Only</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
 	<role-name>STSClient</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<security-role>
    <role-name>STSClient</role-name>
</security-role>

<login-config>
    <auth-method>SECURITY_DOMAIN</auth-method>
    <realm-name>SECURITY_DOMAIN</realm-name>
    <form-login-config>
	<form-login-page>/login.html</form-login-page>
	<form-error-page>/error.html</form-error-page>
    </form-login-config>
</login-config>

Important

Note that we defined two pages in the <form-login-config> : login.html and error.html . Both pages must exists inside your deployment.

Change your WEB-INF/jboss-web.xml to configure the PicketLinkAuthenticator as a valve:

<jboss-web>
	<security-domain>authenticator</security-domain>
	<context-root>authenticator</context-root>
	<valve>
		<class-name>org.picketlink.identity.federation.bindings.tomcat.PicketLinkAuthenticator
		</class-name>
	</valve>
</jboss-web>

We also defined a <security-domain> configuration with the name of the security domain that you configured in your standalone.xml:

<security-domain name="authenticator" cache-type="default">
    <authentication>
        <login-module code="org.picketlink.test.trust.loginmodules.TestRequestUserLoginModule" flag="required">
            <module-option name="usersProperties" value="users.properties"/>
            <module-option name="rolesProperties" value="roles.properties"/>
        </login-module>
    </authentication>
</security-domain>

Tip

To use PicketLink you need to define it as a module dependency using the META-INF/jboss-deployment-structure.xml.

Your web.xml will define some security constraints. But it will define a <login-config> that is different from the servlet specifcation mandated BASIC, CLIENT-CERT, FORM or DIGEST methods. We suggest the use of SECURITY-DOMAIN as the method.

Create a context.xml in your WEB-INF directory of your web-archive.

<Context>
  <Valve className="org.picketlink.identity.federation.bindings.tomcat.PicketLinkAuthenticator" />
</Context>

Your web.xml may look as follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
   xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

   <description>Sales Application</description>

   <security-constraint>
      <display-name>Restricted</display-name>
      <web-resource-collection>
         <web-resource-name>Restricted Access</web-resource-name>
         <url-pattern>/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
         <role-name>Sales</role-name>
      </auth-constraint>
      <user-data-constraint>
         <transport-guarantee>NONE</transport-guarantee>
      </user-data-constraint>
   </security-constraint>

   <security-role>
      <role-name>Sales</role-name>
   </security-role>

   <login-config>
      <auth-method>SECURITY-DOMAIN</auth-method>
   </login-config>
</web-app>

Warning

NOTE: The use of SECURITY-DOMAIN as the auth-method.

The war should be packaged as a regular web archive.

  1. Go to the deploy directory.

  2. cp -R jmx-console.war test.war

  3. In deploy/test.war/WEB-INF/web.xml, change the auth-method element to SECURITY-DOMAIN.

  4. <login-config>
          <auth-method>SECURITY-DOMAIN</auth-method>
          <realm-name>JBoss JMX Console</realm-name>
       </login-config>
    
  5. Also uncomment the security constraints in web.xml. It should look as follows.

  6. <!-- A security constraint that restricts access to the HTML JMX console
       to users with the role JBossAdmin. Edit the roles to what you want and
       uncomment the WEB-INF/jboss-web.xml/security-domain element to enable
       secured access to the HTML JMX console.
       -->
       <security-constraint>
         <web-resource-collection>
           <web-resource-name>HtmlAdaptor</web-resource-name>
           <description>An example security config that only allows users with the
             role JBossAdmin to access the HTML JMX console web application
           </description>
           <url-pattern>/*</url-pattern>
           <http-method>GET</http-method>
           <http-method>POST</http-method>
         </web-resource-collection>
         <auth-constraint>
           <role-name>JBossAdmin</role-name>
         </auth-constraint>
       </security-constraint>
    
  7. In the /server/default/conf/jboss-log4j.xml , add trace category for org.jboss.security.

  8. Start JBoss AS.

  9. Go to the following url: http://localhost:8080/test/

  10. You should see a HTTP 403 message.

  11. If you look inside the log, log/server.log, you will see the following exception trace:

  12. 2011-04-20 11:02:01,714 TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.jmx-console] (http-127.0.0.1-8080-1) Login failure
    javax.security.auth.login.FailedLoginException: Password Incorrect/Password Required
            at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:252)
            at org.jboss.security.auth.spi.UsersRolesLoginModule.login(UsersRolesLoginModule.java:152)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
            at java.lang.reflect.Method.invoke(Method.java:597)
            at javax.security.auth.login.LoginContext.invoke(LoginContext.java:769)
            at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186)
            at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683)
            at java.security.AccessController.doPrivileged(Native Method)
            at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
            at javax.security.auth.login.LoginContext.login(LoginContext.java:579)
            at org.jboss.security.plugins.auth.JaasSecurityManagerBase.defaultLogin(JaasSecurityManagerBase.java:552)
            at org.jboss.security.plugins.auth.JaasSecurityManagerBase.authenticate(JaasSecurityManagerBase.java:486)
            at org.jboss.security.plugins.auth.JaasSecurityManagerBase.isValid(JaasSecurityManagerBase.java:365)
            at org.jboss.security.plugins.JaasSecurityManager.isValid(JaasSecurityManager.java:160)
            at org.jboss.web.tomcat.security.JBossWebRealm.authenticate(JBossWebRealm.java:384)
            at org.picketlink.identity.federation.bindings.tomcat.PicketLinkAuthenticator.authenticate(PicketLinkAuthenticator.java:104)
            at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:491)
            at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
            at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
            at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
            at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
            at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
            at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
            at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
            at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
            at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
            at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
            at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
            at java.lang.Thread.run(Thread.java:662)
    

    As you can see from the stack trace, PicketLinkAuthenticator method has been kicked in.

The PicketLink API provides the org.picketlink.identity.federation.saml.v2.assertion.AssertionType class to encapsulate the informations parsed from a SAML Assertion.

Let's suppose we have the following SAML Assertion:

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="ID_75291c31-93f7-4f7f-8422-aacdb07466ee" IssueInstant="2012-05-25T10:40:58.912-03:00" Version="2.0">
    <saml:Issuer>http: //192.168.1.1:8080/idp-sig/</saml:Issuer> 
    <saml:Subject> 
        <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">user</saml:NameID>
        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> 
            <saml:SubjectConfirmationData InResponseTo="ID_326a389f-6a8a-4712-b71d-77aa9c36795c" NotBefore="2012-05-25T10:40:58.894-03:00" NotOnOrAfter="2012-05-25T10:41:00.912-03:00" Recipient="http://192.168.1.4:8080/fake-sp" /> 
        </saml:SubjectConfirmation> 
    </saml:Subject> 
    <saml:Conditions NotBefore="2012-05-25T10:40:57.912-03:00" NotOnOrAfter="2012-05-25T10:41:00.912-03:00" /> 
    <saml:AuthnStatement AuthnInstant="2012-05-25T10:40:58.981-03:00"> 
        <saml:AuthnContext> 
            <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef> 
        </saml:AuthnContext> 
    </saml:AuthnStatement> 
    <saml:AttributeStatement> 
        <saml:Attribute Name="Role"> 
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">test-role1</saml:AttributeValue> 
        </saml:Attribute> 
        <saml:Attribute Name="Role"> 
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">test-role2</saml:AttributeValue>
        </saml:Attribute> 
        <saml:Attribute Name="Role"> 
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">test-role3</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
</saml:Assertion>

The code to parse this XML is:

/**
     * <p>
     * Parses a SAML Assertion XML representation and convert it to a {@link AssertionType} instance.
     * </p>
     * 
     * @throws Exception
     */    
    @Test
    public void testParseAssertion() throws Exception {
        // get a InputStream from the source XML file
        InputStream samlAssertionInputStream = getSAMLAssertion();

        SAMLParser samlParser = new SAMLParser();

        Object parsedObject = samlParser.parse(samlAssertionInputStream);

        Assert.assertNotNull(parsedObject);
        Assert.assertTrue(parsedObject.getClass().equals(AssertionType.class));

        // cast the parsed object to the expected type, in this case AssertionType
        AssertionType assertionType = (AssertionType) parsedObject;

        // checks if the Assertion has expired.
        Assert.assertTrue(AssertionUtil.hasExpired(assertionType));

        // let's write the parsed assertion to the sysout
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        SAMLAssertionWriter writer = new SAMLAssertionWriter(StaxUtil.getXMLStreamWriter(baos));

        writer.write(assertionType);

        System.out.println(new String(baos.toByteArray()));
    }

The PicketLink API provides the org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature to help during signature generation/validation for SAML Assertions.

/**
     * <p>
     * Signs a SAML Assertion.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void testSignAssertion() throws Exception {
        InputStream samlAssertionInputStream = getSAMLAssertion();
        
        // convert the InputStream to a DOM Document
        Document document = DocumentUtil.getDocument(samlAssertionInputStream);

        SAML2Signature samlSignature = new SAML2Signature();
        
        // get the key store manager instance.
        KeyStoreKeyManager keyStoreKeyManager = getKeyStoreManager();

        samlSignature.signSAMLDocument(document, keyStoreKeyManager.getSigningKeyPair());
        
        // let's print the signed assertion to the sysout
        System.out.println(DocumentUtil.asString(document));
    }

As you can see, we need to create a instance of org.picketlink.identity.federation.core.impl.KeyStoreKeyManager from where the certificates will be retrieved from. The code bellow shows you how to create it:

     /**
     * <p>
     * Creates a {@link KeyStoreKeyManager} instance.
     * </p>
     *
     * @throws Exception
     */
    private KeyStoreKeyManager getKeyStoreManager() 
        throws TrustKeyConfigurationException, TrustKeyProcessingException {

        KeyStoreKeyManager keyStoreKeyManager = new KeyStoreKeyManager();

        ArrayList<AuthPropertyType> authProperties = new ArrayList<AuthPropertyType>();

        authProperties.add(createAuthProperty(KeyStoreKeyManager.KEYSTORE_URL, Thread.currentThread().getContextClassLoader().getResource("./keystore/jbid_test_keystore.jks").getFile()));
        authProperties.add(createAuthProperty(KeyStoreKeyManager.KEYSTORE_PASS, "store123"));

        authProperties.add(createAuthProperty(KeyStoreKeyManager.SIGNING_KEY_ALIAS, "servercert"));
        authProperties.add(createAuthProperty(KeyStoreKeyManager.SIGNING_KEY_PASS, "test123"));

        keyStoreKeyManager.setAuthProperties(authProperties);

        return keyStoreKeyManager;
    }

    public AuthPropertyType createAuthProperty(String key, String value) {
        AuthPropertyType authProperty = new AuthPropertyType();

        authProperty.setKey(key);
        authProperty.setValue(value);

        return authProperty;
    }

Common scenario is to use Picketlink as both Identity Provider (IDP) and Service Provider (SP), but sometimes it may be useful to integrate with 3rd party vendors as well. If your company is using services provided by 3rd party vendors like SalesForce or Google Apps, then SSO with these vendors may be real benefit for you.

We support these scenarios:

In first scenario we will use Salesforce as SAML SP and we will use Picketlink application as SAML IDP. In this tutorial, we will reuse application idp-sig.war from Picketlink quickstarts .