JBoss.orgCommunity Documentation

PicketLink Reference Documentation

Version 2.5.0.Beta5


1. Overview
1.1. What is PicketLink?
1.2. Modules
1.2.1. Base module
1.2.2. Identity Management
1.2.3. Federation
1.3. License
1.4. Maven Dependencies
2. Authentication
2.1. Overview
2.2. The Authentication API
2.3. The Authentication Process
2.3.1. A Basic Authenticator
2.3.2. Multiple Authenticator Support
2.3.3. Credentials
2.3.4. DefaultLoginCredentials
3. Identity Management
3.1. Overview
3.2. Getting Started in 5 Minutes
3.3. Identity Model
3.3.1. Architectural Overview
3.4. Configuration
3.4.1. Architectural Overview
3.4.2. Programmatic Configuration
3.4.3. Security Context Configuration
3.4.4. Identity Store Feature Set
3.4.5. Identity Store Configurations
3.4.6. JPAIdentityStoreConfiguration
3.4.7. LDAPIdentityStoreConfiguration
3.4.8. FileIdentityStoreConfiguration
3.4.9. Providing a Custom IdentityStore
3.5. Java EE Environments
3.6. Using the IdentityManager
3.6.1. Accessing the IdentityManager in Java EE
3.6.2. Accessing the IdentityManager in Java SE
3.7. Managing Users, Groups and Roles
3.7.1. Managing Users
3.7.2. Managing Groups
3.8. Managing Relationships
3.8.1. Built In Relationship Types
3.8.2. Creating Custom Relationships
3.9. Authentication
3.10. Managing Credentials
3.11. Credential Handlers
3.11.1. The CredentialStore interface
3.12. Built-in Credential Handlers
3.12.1.
3.13. Advanced Topics
3.13.1. Multi Realm Support
4. Federation
4.1. Overview
4.2. SAML SSO
4.3. SAML Web Browser Profile
4.4. PicketLink SAML Specification Support
4.5. SAML v2.0
4.5.1. Which Profiles are supported ?
4.5.2. Which Bindings are supported ?
4.5.3. PicketLink Identity Provider (PIDP)
4.5.4. PicketLink Service Provider (PSP)
4.5.5. SAML Authenticators (Tomcat,JBossAS)
4.5.6. Digital Signatures in SAML Assertions
4.5.7. SAML2 Handlers
4.5.8. Single Logout
4.5.9. SAML2 Configuration Providers
4.5.10. Metadata Support
4.5.11. Token Registry
4.5.12. Standalone vs JBossAS Distribution
4.5.13. Standalone Web Applications(All Servlet Containers)
4.6. SAML v1.1
4.6.1. SAML v1.1
4.6.2. PicketLink SAML v1.1 Support
4.7. Trust
4.7.1. Security Token Server (STS)
4.8. Extensions
4.8.1. Extensions
4.8.2. PicketLinkAuthenticator
4.9. PicketLink API
4.9.1. Working with SAML Assertions
4.10. 3rd party integration
4.10.1. Picketlink as IDP, Salesforce as SP
4.10.2. Picketlink as SP, Salesforce as IDP
4.10.3. Picketlink as IDP, Google Apps as SP

The Identity bean (which can be found in the org.picketlink package) is central to PicketLink's security API. This bean represents the authenticated user for the current session, and provides many useful methods for controlling the authentication process and querying the user's assigned privileges. In terms of authentication, the Identity bean provides the following methods:

AuthenticationResult login();


void logout();      
boolean isLoggedIn();
Agent getAgent();

The login() method is the primary point of entry for the authentication process. Invoking this method will cause PicketLink to attempt to authenticate the user based on the credentials that they have provided. The AuthenticationResult type returned by the login() method is a simple enum that defines the following two values:

public enum AuthenticationResult {

    SUCCESS, FAILED
}

If the authentication process is successful, the login() method will return a result of SUCCESS, otherwise it will return a result of FAILED. By default, the Identity bean is session-scoped, which means that once a user is authenticated they will stay authenticated for the duration of the session.

Note

One significant point to note is the presence of the @Named annotation on the Identity bean, which means that its methods may be invoked directly from the view layer (if the view layer, such as JSF, supports it) via an EL expression.

One possible way to control the authentication process is by using an action bean, for example the following code might be used in a JSF application:

public @RequestScoped @Named class LoginAction {


    @Inject Identity identity;
    public void login() {
        AuthenticationResult result = identity.login();
        if (AuthenticationResult.FAILED.equals(result)) {
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(
                        "Authentication was unsuccessful.  Please check your username and password " +
                        "before trying again."));
        }
    }
}

In the above code, the Identity bean is injected into the action bean via the CDI @Inject annotation. The login() method is essentially a wrapper method that delegates to Identity.login() and stores the authentication result in a variable. If authentication was unsuccessful, a FacesMessage is created to let the user know that their login failed. Also, since the bean is @Named it can be invoked directly from a JSF control like so:


<h:commandButton value="LOGIN" action="#{loginAction.login}"/>

The isLoggedIn() method may be used to determine whether there is a user logged in for the current session. It is typically used as an authorization check to control either an aspect of the user interface (for example, not displaying a menu item if the user isn't logged in), or to restrict certain business logic. While logged in, the getAgent() method can be used to retrieve the currently authenticated agent (or user). If the current session is not authenticated, then getAgent() will return null. The following example shows both the isLoggedIn() and getAgent() methods being used inside a JSF page:


<ui:fragment rendered="#{identity.loggedIn}">Welcome, #{identity.agent.loginName}

Note

If you're wondering what an Agent is, it is simply a representation of the external entity that is interacting with your application, whether that be a human user or some third party (non-human) system. The Agent interface is actually the superclass of User - see the Identity Management chapter for more details.

The logout() method allows the user to log out, thereby clearing the authentication state for their session. Also, if the user's session expires (for example due to inactivity) their authentication state will also be lost requiring the user to authenticate again.

The following JSF code example demonstrates how to render a log out button when the current user is logged in:


<ui:fragment rendered="#{identity.loggedIn}">
    <h:form>
        <h:commandButton value="Log out" action="#{identity.logout}"/>
    </h:form>
</ui:fragment>

While it is the Identity bean that controls the overall authentication process, the actual authentication "business logic" is defined by the Authenticator interface:

public interface Authenticator {

    public enum AuthenticationStatus {
        SUCCESS, 
        FAILURE, 
        DEFERRED
    }
    void authenticate();
    void postAuthenticate();
    AuthenticationStatus getStatus();
    Agent getAgent();
}

During the authentication process, the Identity bean will invoke the methods of the active Authenticator (more on this in a moment) to perform user authentication. The authenticate() method is the most important of these, as it defines the actual authentication logic. After authenticate() has been invoked by the Identity bean, the getStatus() method will reflect the authentication status (either SUCCESS, FAILURE or DEFERRED). If the authentication process was a success, the getAgent() method will return the authenticated Agent object and the postAuthenticate() method will be invoked also. If the authentication was not a success, getAgent() will return null.

Now that we've looked at all the individual pieces, let's take a look at how they all work together to process an authentication request. For starters, the following sequence diagram shows the class interaction that occurs during a successful authentication:

  • 1 - The user invokes the login() method of the Identity bean.

  • 1.1 - The Identity bean (after performing a couple of validations) invokes its own authenticate() method.

  • 1.1.1 - Next the Identity bean invokes the Authenticator bean's authenticate() method (which has a return value of void).

  • 1.1.2 - To determine whether authentication was successful, the Identity bean invokes the Authenticator's getStatus() method, which returns a SUCCESS.

  • 1.1.3 - Upon a successful authentication, the Identity bean then invokes the Authenticator's postAuthenticate() method to perform any post-authentication logic.

  • 1.1.4 - The Identity bean then invokes the Authenticator's getAgent() method, which returns an Agent object representing the authenticated agent, which is then stored as a private field in the Identity bean.

The authentication process ends when the Identity.authenticate() method returns a value of true to the login() method, which in turn returns an authentication result of SUCCESS to the invoking user.

Let's take a closer look at an extremely simple example of an Authenticator. The following code demonstrates an Authenticator implementation that simply tests the username and password credentials that the user has provided against hard coded values of jsmith for the username, and abc123 for the password, and if they match then authentication is deemed to be a success:

@PicketLink

public class SimpleAuthenticator extends BaseAuthenticator {
    @Inject DefaultLoginCredentials credentials;
    @Override
    public void authenticate() {
        if ("jsmith".equals(credentials.getUserId()) &&
                "abc123".equals(credentials.getPassword())) {
            setStatus(AuthenticationStatus.SUCCESS);
            setUser(new SimpleUser("jsmith"));
        } else {
            setStatus(AuthenticationStatus.FAILURE);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
                    "Authentication Failure - The username or password you provided were invalid."));
        }
    }
}

The first thing we can notice about the above code is that the class is annotated with the @PicketLink annotation. This annotation indicates that this bean should be used for the authentication process. The next thing is that the authenticator class extends something called BaseAuthenticator. This abstract base class provided by PicketLink implements the Authenticator interface and provides implementations of the getStatus() and getAgent() methods (while also providing matching setStatus() and setAgent() methods), and also provides an empty implementation of the postAuthenticate() method. By extending BaseAuthenticator, our Authenticator implementation simply needs to implement the authenticate() method itself.

We can see in the above code that in the case of a successful authentication, the setStatus() method is used to set the authentication status to SUCCESS, and the setUser() method is used to set the user (in this case by creating a new instance of SimpleUser). For an unsuccessful authentication, the setStatus() method is used to set the authentication status to FAILURE, and a new FacesMessage is created to indicate to the user that authentication has failed. While this code is obviously meant for a JSF application, it's possible to execute whichever suitable business logic is required for the view layer technology being used.

One thing that hasn't been touched on yet is the following line of code:

@Inject DefaultLoginCredentials credentials;

This line of code injects the credentials that have been provided by the user using CDI's @Inject annotation, so that our Authenticator implementation can query the credential values to determine whether they're valid or not. We'll take a look at credentials in more detail in the next section.

Note

You may be wondering what happens if you don't provide an Authenticator bean in your application. If this is the case, PicketLink will automatically authenticate via the identity management API, using a sensible default configuration. See the Identity Management chapter for more information.

Credentials are something that provides evidence of a user's identity; for example a username and password, an X509 certificate or some kind of biometric data such as a fingerprint. PicketLink has extensive support for a variety of credential types, and also makes it relatively simple to add custom support for credential types that PicketLink doesn't support out of the box itself.

In the previous section, we saw a code example in which a DefaultLoginCredentials (an implementation of the Credentials interface that supports a user ID and a credential value) was injected into the SimpleAuthenticator bean. The most important thing to know about the Credentials interface in relation to writing your own custom Authenticator implementation is that you're not forced to use it. However, while the Credentials interface is mainly designed for use with the Identity Management API (which is documented in a separate chapter) and its methods would rarely be used in a custom Authenticator, PicketLink provides some implementations which are suitably convenient to use as such, DefaultLoginCredentials being one of them.

So, in a custom Authenticator such as this:

public class SimpleAuthenticator extends BaseAuthenticator {


    @Inject DefaultLoginCredentials credentials;
    
    // code snipped
}

The credential injection is totally optional. As an alternative example, it is totally valid to create a request-scoped bean called UsernamePassword with simple getters and setters like so:

public @RequestScoped class UsernamePassword {

    private String username;
    private String password;
    
    public String getUsername() { return username; }
    public String getPassword() { return password; }
    
    public void setUsername(String username) { this.username = username; }
    public void setPassword(String password) { this.password = password; }
}

And then inject that into the Authenticator bean instead:

public class SimpleAuthenticator extends BaseAuthenticator {


    @Inject UsernamePassword usernamePassword;
    
    // code snipped
}

Of course it is not recommended that you actually do this, however this simplistic example serves adequately for demonstrating the case in point.

If you'd like to get up and running with IDM quickly, the good news is that PicketLink will provide a default configuration that stores your identity data on the file system if no other configuration is available. This means that if you have the PicketLink libraries in your project, you can simply inject the IdentityManager bean into your own classes and start using it immediately:

@Inject IdentityManager identityManager;

Once you have injected the IdentityManager you can begin creating users, groups and roles for your application:

User user = new SimpleUser("jane");

user.setFirstName("Jane");
user.setLastName("Doe");
identityManager.add(user);
Group group = new SimpleGroup("employees");
identityManager.add(group);
Role admin = new SimpleRole("admin");
identityManager.add(admin);

It is also quite easy to override the default behaviour and provide your own configuration - simply provide a producer method in your application that returns an IdentityConfiguration object:

@Produces

public IdentityConfiguration createIdentityConfiguration() {
    IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
    builder
        .stores()
            .file()
                .supportAllFeatures();
    return builder.build();
}

The code above essentially produces a configuration identical to the default configuration. If you'd like to use LDAP or JPA to store your identity data, you'll also find example configurations later in this chapter.

PicketLink's identity model consists of a number of core interfaces that define the fundamental identity types upon which much of the Identity Management API is based. The following class diagram shows the classes and interfaces in the org.picketlink.idm.model package that form the base identity model.

  • AttributedType is the base interface for the identity model. It declares a number of methods for managing a set of attribute values, plus getId() and setId() methods for setting a unique UUID value.

  • Attribute is used to represent an attribute value. An attribute has a name and a (generically typed) value, and may be marked as read-only. Attribute values that are expensive to load (such as large binary data) may be lazy-loaded; the isLoaded() method may be used to determine whether the Attribute has been loaded or not.

  • IdentityType is the base interface for Identity objects. It declares properties that indicate whether the identity object is enabled or not, optional created and expiry dates, plus methods to read and set the owning Partition.

  • Agent represents a unique entity that may access the services secured by PicketLink. In contrast to a user which represents a human, Agent is intended to represent a third party non-human (i.e. machine to machine) process that may authenticate and interact with your application or services. It declares methods for reading and setting the Agent's login name.

  • User represents a human user that accesses your application and services. In addition to the login name property defined by its parent interface Agent, the User interface declares a number of other methods for managing the user's first name, last name and e-mail address.

  • Group is used to manage collections of identity types. Each Group has a name and an optional parent group.

  • Role is used in various relationship types to designate authority to another identity type to perform various operations within an application. For example, a forum application may define a role called moderator which may be assigned to one or more Users or Groups to indicate that they are authorized to perform moderator functions.

The following diagram shows the main components that realize PicketLink Identity Management:

  • IdentityConfiguration is the the class responsible for holding all PicketLink configuration options. This class is usually built using the Configuration Builder API, which we'll cover in the next sections. Once created and populated with the configuration options, an instance is used to create a IdentityManagerFactory.

  • IdentityManagerFactory is the class from which IdentityManager instances are created for a specific realm, considering all configurations provided by a IdentityConfiguration instance.

  • SecurityContextFactory is an interface that provides methods for creating SecurityContext instances. This component knows how to properly create and prepare the context that will be propagated during identity management operations.

  • SecurityContext is the class that holds context data that will be used during the execution of identity management operations. Once created, the context is used to create IdentityStore instances and to invoke their methods.

    This component allows to share data between the IdentityManager and IdentityStore instances. And also provides direct access for some IDM subsystems such as: event handling, caching and so on.

    Beyond that, this component is critical when access to external resources are required, such as the current EntityManager when using a JPA-based store.

    Each IdentityManager instance is associated with a single SecurityContext.

  • StoreFactory is an interface that provides methods for creating IdentityStore instances. Instances are created considering the Feature Set supported by each identity store and also the current SecurityContext instance.

  • IdentityStore is an interface that provides a contract for implementations that store data using a specific repository such as: LDAP, databases, file system, etc.

    It is a critical component as it provides all the necessary logic about how to store data.

  • IdentityManager is an interface that provides a simple access for all identity management operations using one or more of the configured identity stores.

    All functionality provided by PicketLink is available from this interface, from where applications will interact most of the time.

For most use cases, users will only work with the IdentityManagerFactory and IdentityManager classes. Only advanced use cases may require a deep knowledge about other components in order to customize the default behaviour/implementation to suit a specific requirement.

The diagram below shows an overview about how a specific identity management operation is realized:

  • 1 - The Application creates an IdentityManagerFactory instance from a previously created IdentityConfiguration. At this point, the factory reads the configuration and bootstraps the identity management ecosystem.

  • 1.1 - The IdentityManagerFactory initializes the SecurityContextFactory.

  • 1.2 - The IdentityManagerFactory initializes the StoreFactory.

  • 2 - With a fully initialized IdentityManagerFactory instance, the Application is able to create IdentityManager instances and execute operations. IdentityManager instances are created for a specific realm, in this specific case we're creating an instance using the default realm.

  • 2.1 and 2.1.1 - An IdentityManager instance is always associated with a SecurityContext. The SecurityContext is created and set into the IdentityManager instance. The same security context is used during the entire lifecycle of the IdentityManager, it will be used to share state with the underlying identity stores and provide access to external resources (if necessary) in order to execute operations.

    At this time, the IdentityManager is also configured to hold a reference to the StoreFactory in order to execute the operations against the underlying/configured IdentityStore instances.

  • 3 - Now the application holds a reference to the IdentityManager instance and it is ready to perform identity management operations (eg.: add an user, queries, validate credentials, etc).

  • 3.1 and 3.1.1 - But before executing the operations, the IdentityManager needs to obtain from the StoreFactory the IdentityStore instance that should be used to execute a specific operation. Identity stores are selected by examining the configuration to see which store configuration supports a specific operation or feature.

  • 3.2 - Now that the IdentityManager have selected which IdentityStore instance should be used, this last is invoked in order to process the operation.

  • 3.2.1 - Usually, during the execution of an operation, the IdentityStore uses the current SecurityContext. The SecurityContext can hold some state that may be useful during the execution (eg.: the JPA store uses the security context to gain access to the current EntityManager instance) and also provide access for some IDM internal services like event handling, caching, etc.

  • 3.2.2 - Finally, the IdentityStore executes the operation and persist or retrieve identity data from the underlying repository.

PicketLink IDM design is quite flexible and allows you to configure or even customize most of the behaviours described above. As stated earlier, most use cases require minimal knowledge about these details and the default implementation should be enough to satisfy the majority of requirements.

Configuration in PicketLink is in essence quite simple; an IdentityConfiguration object must first be created to hold the PicketLink configuration options. Once all configuration options have been set, you just create a IdentityManagerFactory instance passing the previously created configuration. The IdentityManagerFactory can then be used to create IdentityManager instances via the createIdentityManager() method.

The IdentityConfiguration is usually created using a Configuration Builder API, which provides a rich and fluent API for every single aspect of PicketLink configuration.

Note

For now, all configuration is set programmatically using the Configuration Builder API only. Later versions will also support a declarative configuration in a form of XML documents.

Each IdentityManager instance has its own security context, represented by the SecurityContext class. The security context contains temporary state which is maintained for one or more identity management operations within the scope of a single realm or tier. The IdentityManager (and its associated SecurityContext) is typically modelled as a request-scoped object (for environments which support such a paradigm, such as a servlet container), or alternatively as an actor within the scope of a transaction. In the latter case, the underlying resources being utilised by the configured identity stores (such as a JPA EntityManager) would participate in the active transaction, and changes made as a result of any identity management operations would either be committed or rolled back as appropriate for the logic of the encapsulating business method.

The following sections describe various ways that configuration may be performed in different environments.

Configuration for Identity Management can be defined programmatically using the Configuration Builder API. The aim of this API is to make it easier to chain coding of configuration options in order to speed up the coding itself and make the configuration more readable.

Let's assume that you want to quick start with PicketLink Identity Management features using a file-based Identity Store. First, a fresh instance of IdentityConfiguration is created using the IdentityConfigurationBuilder helper object, where we choose which identity store we want to use (in this case a file-based store) and any other configuration option, if necessary. Finally, we use the configuration to create a IdentityManagerFactory from where we can create IdentityManager instances and start to perform Identity Management operations:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

        
builder
  .stores()
  .file()
  .supportAllFeatures();
IdentityConfiguration configuration = builder.build();
IdentityManagerFactory identityManagerFactory = new IdentityManagerFactory(configuration);
IdentityManager identityManager = identityManagerFactory.createIdentityManager();
User user = new SimpleUser("john");
identityManager.add(user);

The SecurityContext plays an important role in the PicketLink IDM achitecture. As discussed in the Architectural Overview, it is strongly used during the execution of operations. It carries very sensitive and contextual information for a specific operation and provides access for some of the IDM underlying services such as caching, event handling, UUID generator for IdentityTpe and Relationship instances, among others.

Operations are always executed by a specific IdentityStore in order to persist or store identity data using a specific repository (eg.: LDAP, databases, filesystem, etc). When executing a operation the identity store must be able to:

  • Access the current Partition. All operations are executed for a specific Realm or Tier

  • Access the current IdentityManager instance, from which the operation was executed.

  • Access the Event Handling API in order to fire events such as when an user is created, updated, etc.

  • Access the Caching API in order to cache identity data and increase performance.

  • Access the Credential Handler API in order to be able to update and validate credentials.

  • Access to external resources, provided before the operation is executed and initialized by a ContextInitializer.

Sometimes you may need to provide additional configuration or even references for external resources before the operation is executed by an identity store. An example is how you tell to theJPAIdentityStore which EntityManager instance should be used. When executing an operation, the JPAIdentityStore must be able to access the current EntityManager to persist or retrieve data from the database. You need someway to populate the SecurityContext with such information. When you're configuring an identity store, there is a configuration option that allows you to provide a ContextInitializer implementation.

public interface ContextInitializer {    

    void initContextForStore(SecurityContext context, IdentityStore<?> store);
}

The method initContextForStore will be invoked for every single operation and before its execution by the identity store. It can be implemented to provide all the necessary logic to initialize and populate the SecurityContext for a specific IdentityStore.

The configuration is also very simple, you just need to provide the following configuration:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .addContextInitializer(new MySecurityContextInitializer());  
}

You can provide multiple initializers.

Note

Remember that initializers are executed for every single operation. Also, the same instance is used between operations which means your implementation should be stateless. You should be careful about the implementation in order to not impact performance, concurrency or introduce unexpected behaviors.

When configuring identity stores you must tell which features and operations should be executed by them. Features and operations are a key concept if you want to mix stores in order to execute operations against different repositories.

PicketLink provides a Java enum, called FeatureGroup, in which are defined all supported features. The table bellow summarize them:


The features are a determinant factor when choosing an identity store to execute a specific operation. For example, if an identity store is configured with FeatureGroup.user we're saying that all User operations should be executed by this identity store. The same goes for FeatureGroup.credential, we're just saying that credentials can also be updated and validated using the identity store.

Beside that, provide only the feature is not enough. We must also tell the identity store which operations are supported by a feature. For example, we can configure a identity store to support only read operations for users, which is very common when using the LDAP identity store against a read-only tree. Operations are also defined by an enum, called FeatureOperation, as follows:


During the configuration you can provide which features and operations should be supported using the Configuration API. You don't need to be forced to specify them individually, if you want to support all features and operations for a particular identity store you can use:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .supportAllFeatures();  
}

For a more granular configuration you can also use:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .supportFeature(
        FeatureGroup.agent, 
        FeatureGroup.user, 
        FeatureGroup.role, 
        FeatureGroup.group)  
}

The configuration above defines the features individually. In this case the configured features are also supporting all operations. If you want to specify which operation should be supported by a feature you can use:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .supportFeature(FeatureGroup.agent, FeatureOperation.read)
      .supportFeature(FeatureGroup.user, FeatureOperation.read))
      .supportFeature(FeatureGroup.role, FeatureOperation.create))
      .supportFeature(FeatureGroup.role, FeatureOperation.read))
      .supportFeature(FeatureGroup.role, FeatureOperation.update))
      .supportFeature(FeatureGroup.role, FeatureOperation.delete))  
      .supportFeature(FeatureGroup.group, FeatureOperation.create))
      .supportFeature(FeatureGroup.group, FeatureOperation.read))
      .supportFeature(FeatureGroup.group, FeatureOperation.update))
      .supportFeature(FeatureGroup.group, FeatureOperation.delete))  
}

For a more complex configuration evolving multiple identity stores with a different feature set, look at the example bellow:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .ldap()
      .supportFeature(FeatureGroup.agent)
      .supportFeature(FeatureGroup.user)
      .supportFeature(FeatureGroup.credential)
    .jpa()
      .supportFeature(FeatureGroup.role)
      .supportFeature(FeatureGroup.group)
      .supportFeature(FeatureGroup.relationship)
}

The configuration above shows how to use LDAP to store only agents, users and credentials and database for roles, groups and relationships.

Note

Remember that identity stores must have their features and operations configured. If you don't provide them you won't be able to build the configuration.

The JPA identity store uses a relational database to store identity state. The configuration for this identity store provides control over which entity beans are used to store identity data, and how their fields should be used to store various identity-related state. The entity beans that store the identity data must be configured using the annotations found in the org.picketlink.jpa.annotations package. All identity configuration annotations listed in the tables below are from this package.

The Identity class is the entity bean that is used to store the record for users, roles and groups. It should be annotated with @IdentityType and declare the following field values:


The following code shows an example of an entity class configured to store Identity instances:


Sometimes you may need to configure how the EntityManager is provided to the JPAIdentityStore, like when your application is using CDI and you must run the operations in the scope of the current transaction by using a injected EntityManager instance.

In cases like that, you need to initialize the SecurityContext by providing a ContextInitializer implementation, as discussed in Security Context Configuration. The JPAContextInitializer is provided by PicketLink and can be used to initialize the security context with a specific EntityManager instance. You can always extend this class and provide your own way to obtain the EntityManager from your application's environment.

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .file()
      .addContextInitializer(new JPAContextInitializer(emf) {
        @Override
        public EntityManager getEntityManager() {
          // logic goes here
        }
      });
}

By default, the JPAContextInitializer creates a EntityManager from the EntityManagerFacatory provided when creating a new instance.

The LDAP identity store allows an LDAP directory server to be used to provide identity state. You can use this store in read-only or write-read mode, depending on your permissions on the server.

The LDAP identity store can be configured by providing the following configuration:

IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();


builder
  .stores()
    .ldap()
      .baseDN("dc=jboss,dc=org")
      .bindDN("uid=admin,ou=system")
      .bindCredential("secret")
      .url("ldap://localhost:10389")
      .userDNSuffix("ou=People,dc=jboss,dc=org")
      .roleDNSuffix("ou=Roles,dc=jboss,dc=org")
      .groupDNSuffix("ou=Groups,dc=jboss,dc=org")
      .supportAllFeatures();

The following table describes all configuration options:


This identity store uses the file system to persist identity state. The configuration for this identity store provides control over where to store identity data and if the state should be preserved between initializatons.

Identity data is stored using the Java Serialization API.

PicketLink IDM provides a number of basic implementations of the identity model interfaces for convenience, in the org.picketlink.idm.model package. The following sections provide examples that show these implementations in action.

Relationships are used to model typed associations between two or more identities. All concrete relationship types must implement the marker interface org.picketlink.idm.model.Relationship:

The IdentityManager interface provides three standard methods for managing relationships:

  void add(Relationship relationship);

  void update(Relationship relationship);
  void remove(Relationship relationship);
  • The add() method is used to create a new relationship.

  • The update() method is used to update an existing relationship.

    Note

    Please note that the identities that participate in a relationship cannot be updated themselves, however the attribute values of the relationship can be updated. If you absolutely need to modify the identities of a relationship, then delete the relationship and create it again.

  • The remove() method is used to remove an existing relationship.

Note

To search for existing relationships between identity objects, use the Relationship Query API described later in this chapter.

Besides the above methods, IdentityManager also provides a number of convenience methods for managing many of the built-in relationship types. See the next section for more details.

PicketLink provides a number of built-in relationship types, designed to address the most common requirements of a typical application. The following sections describe the built-in relationships and how they are intended to be used. Every built-in relationship type extends the AbstractAttributedType abstract class, which provides the basic methods for setting a unique identifier value and managing a set of attribute values:

What this means in practical terms, is that every single relationship is assigned and can be identified by, a unique identifier value. Also, arbitrary attribute values may be set for all relationship types, which is useful if you require additional metadata or any other type of information to be stored with a relationship.

The GroupMembership and GroupRole relationships are used to represent a user's membership within a Group, and a user's role for a group, respectively.

Note

While the GroupRole relationship type extends GroupMembership, it does not mean that a member of a GroupRole automatically receives GroupMembership membership also - these are two distinct relationship types with different semantics.

A Group is typically used to form logical collections of users. Within an organisation, groups are often used to mirror the organisation's structure. For example, a corporate structure might consist of a sales department, administration, management, etc. This structure can be modelled in PicketLink by creating corresponding groups such as sales, administration, and so forth. Users (who would represent the employees in a corporate structure) may then be assigned group memberships corresponding to their place within the company's organisational structure. For example, an employee who works in the sales department may be assigned to the sales group. Specific application privileges can then be blanket assigned to the sales group, and anyone who is a member of the group is free to access the application's features that require those privileges.

The GroupRole relationship type should be used when it is intended for an identity to perform a specific role for a group, but not be an actual member of the group itself. For example, an administrator of a group of doctors may not be a doctor themselves, but have an administrative role to perform for that group. If the intent is for an individual identity to both be a member of a group and have an assigned role in that group also, then the identity should have both GroupRole and GroupMembership relationships for that group.

Let's start by looking at a simple example - we'll begin by making the assumption that our organization is structured in the following way:

The following code demonstrates how we would create the hypothetical Sales group which is displayed at the head of the above organisational chart:

  Group sales = new SimpleGroup("Sales");

  identityManager.add(sales);

We can then proceed to create its subgroups:

identityManager.add(new SimpleGroup("North America", sales);

identityManager.add(new SimpleGroup("EMEA", sales);
identityManager.add(new SimpleGroup("Asia", sales);
// and so forth

The second parameter of the SimpleGroup() constructor is used to specify the group's parent group. This allows us to create a hierarchical group structure, which can be used to mirror either a simple or complex personnel structure of an organisation. Let's now take a look at how we assign users to these groups.

The following code demonstrates how to assign an administrator group role for the Northeast sales group to user jsmith. The administrator group role may be used to grant certain users the privilege to modify permissions and roles for that group:

Role admin = identityManager.getRole("administrator");

User user = identityManager.getUser("jsmith");
Group group = identityManager.getGroup("Northeast");
identityManager.grantGroupRole(user, admin, group);

A group role can be revoked using the revokeGroupRole() method:

identityManager.revokeGroupRole(user, admin, group);

To test whether a user has a particular group role, you can use the hasGroupRole() method:

boolean isUserAGroupAdmin = identityManager.hasGroupRole(user, admin, group);

Next, let's look at some examples of how to work with simple group memberships. The following code demonstrates how we assign sales staff rbrown to the Northeast sales group:

User user = identityManager.getUser("rbrown");

Group group = identityManager.getGroup("Northeast");
identityManager.addToGroup(user, group);

A User may also be a member of more than one Group; there are no built-in limitations on the number of groups that a User may be a member of.

We can use the removeFromGroup() method to remove the same user from the group:

identityManager.removeFromGroup(user, group);

To check whether a user is the member of a group we can use the isMember() method:

boolean isUserAMember = identityManager.isMember(user, group);

Relationships can also be created via the add() method. The following code is equivalent to assigning a group role via the grantGroupRole() method shown above:

Role admin = identityManager.getRole("administrator");

User user = identityManager.getUser("jsmith");
Group group = identityManager.getGroup("Northeast");
GroupRole groupRole = new GroupRole(user, group, admin);
identityManager.add(groupRole);

One of the strengths of PicketLink is its ability to support custom relationship types. This extensibility allows you, the developer to create specific relationship types between two or more identities to address the domain-specific requirements of your own application.

Note

Please note that custom relationship types are not supported by all IdentityStore implementations - see the Identity Store section above for more information.

To create a custom relationship type, we start by creating a new class that implements the Relationship interface. To save time, we also extend the AbstractAttributedType abstract class which takes care of the identifier and attribute management methods for us:

  public class Authorization extends AbstractAttributedType implements Relationship {

        
  }

The next step is to define which identities participate in the relationship. Once we create our identity property methods, we also need to annotate them with the org.picketlink.idm.model.annotation.RelationshipIdentity annotation. This is done by creating a property for each identity type.

  private User user;

  private Agent application;
  
  @RelationshipIdentity
  public User getUser() {
    return user;
  }
  
  public void setUser(User user) {
    this.user = user;
  }
  
  @RelationshipIdentity
  public Agent getApplication() {
    return application;
  }
  
  public void setApplication(Agent application) {
    this.application = application;
  }
We can also define some attribute properties, using the @RelationshipAttribute annotation:
  private String accessToken;

  
  @RelationshipAttribute
  public String getAccessToken() {
    return accessToken;
  }
  
  public void setAccessToken(String accessToken) {
    this.accessToken = accessToken;
  }

Note

While the IDM module of PicketLink provides authentication features, for common use cases involving standard username and password based authentication in a Java EE environment, PicketLink provides a more streamlined method of authentication. Please refer to the authentication chapter of this documentation for more information.

PicketLink IDM provides an authentication subsystem that allows user credentials to be validated thereby confirming that an authenticating user is who they claim to be. The IdentityManager interface provides a single method for performing credential validation, as follows:

void validateCredentials(Credentials credentials);

The validateCredentials() method accepts a single Credentials parameter, which should contain all of the state required to determine who is attempting to authenticate, and the credential (such as a password, certificate, etc) that they are authenticating with. Let's take a look at the Credentials interface:

public interface Credentials {

    public enum Status {
        UNVALIDATED, IN_PROGRESS, INVALID, VALID, EXPIRED
    };
   Agent getValidatedAgent();
   
   Status getStatus();
   
   void invalidate();
}
  • The Status enum defines the following values, which reflect the various credential states:

    • UNVALIDATED - The credential is yet to be validated.

    • IN_PROGRESS - The credential is in the process of being validated.

    • INVALID - The credential has been validated unsuccessfully

    • VALID - The credential has been validated successfully

    • EXPIRED - The credential has expired

  • getValidatedAgent() - If the credential was successfully validated, this method returns the Agent object representing the validated user.

  • getStatus() - Returns the current status of the credential, i.e. one of the above enum values.

  • invalidate() - Invalidate the credential. Implementations of Credential should use this method to clean up internal credential state.

Let's take a look at a concrete example - UsernamePasswordCredentials is a Credentials implementation that supports traditional username/password-based authentication:

public class UsernamePasswordCredentials extends AbstractBaseCredentials {


    private String username;
    private Password password;
    public UsernamePasswordCredentials() { }
    public UsernamePasswordCredentials(String userName, Password password) {
        this.username = userName;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
    public UsernamePasswordCredentials setUsername(String username) {
        this.username = username;
        return this;
    }
    public Password getPassword() {
        return password;
    }
    public UsernamePasswordCredentials setPassword(Password password) {
        this.password = password;
        return this;
    }
    @Override
    public void invalidate() {
        setStatus(Status.INVALID);
        password.clear();
    }
}

The first thing we may notice about the above code is that the UsernamePasswordCredentials class extends AbstractBaseCredentials. This abstract base class implements the basic functionality required by the Credentials interface. Next, we can see that two fields are defined; username and password. These fields are used to hold the username and password state, and can be set either via the constructor, or by their associated setter methods. Finally, we can also see that the invalidate() method sets the status to INVALID, and also clears the password value.

Let's take a look at an example of the above classes in action. The following code demonstrates how we would authenticate a user with a username of "john" and a password of "abcde":

Credentials creds = new UsernamePasswordCredentials("john",

    new Password("abcde"));
identityManager.validate(creds);
if (Status.VALID.equals(creds.getStatus())) {
  // authentication was successful
}

We can also test if the credentials that were provided have expired (if an expiry date was set). In this case we might redirect the user to a form where they can enter a new password.

Credentials creds = new UsernamePasswordCredentials("john",

    new Password("abcde"));
identityManager.validate(creds);
if (Status.EXPIRED.equals(creds.getStatus())) {
  // password has expired, redirect the user to a password change screen
}

Updating user credentials is even easier than validating them. The IdentityManager interface provides the following two methods for updating credentials:

void updateCredential(Agent agent, Object credential);

void updateCredential(Agent agent, Object credential, Date effectiveDate, Date expiryDate);

Both of these methods essentially do the same thing; they update a credential value for a specified Agent (or User). The second overloaded method however also accepts effectiveDate and expiryDate parameters, which allow some temporal control over when the credential will be valid. Use cases for this feature include implementing a strict password expiry policy (by providing an expiry date), or creating a new account that might not become active until a date in the future (by providing an effective date). Invoking the first overloaded method will store the credential with an effective date of the current date and time, and no expiry date.

Note

One important point to note is that the credential parameter is of type java.lang.Object. Since credentials can come in all shapes and sizes (and may even be defined by third party libraries), there is no common base interface for credential implementations to extend. To support this type of flexibility in an extensible way, PicketLink provides an SPI that allows custom credential handlers to be configured that override or extend the default credential handling logic. Please see the next section for more information on how this SPI may be used.

PicketLink provides built-in support for the following credential types:

Warning

Not all built-in IdentityStore implementations support all credential types. For example, since the LDAPIdentityStore is backed by an LDAP directory server, only password credentials are supported. The following table lists the built-in IdentityStore implementations that support each credential type.


Let's take a look at a couple of examples. Here's some code demonstrating how a password can be assigned to user "jsmith":

User user = identityManager.getUser("jsmith");

identityManager.updateCredential(user, new Password("abcd1234"));

This example creates a digest and assigns it to user "jdoe":

User user = identityManager.getUser("jdoe");

Digest digest = new Digest();
digest.setRealm("default");
digest.setUsername(user.getLoginName());
digest.setPassword("abcd1234");        
identityManager.updateCredential(user, digest);

For IdentityStore implementations that support multiple credential types, PicketLink provides an optional SPI to allow the default credential handling logic to be easily customized and extended. To get a better picture of the overall workings of the Credential Handler SPI, let's take a look at the sequence of events during the credential validation process when validating a username and password against JPAIdentityStore:

  • 1 - The user (or some other code) first invokes the validateCredentials() method on IdentityManager, passing in the Credentials instance to validate.

  • 1.1 - After looking up the correct IdentityStore (i.e. the one that has been configured to validate credentials) the IdentityManager invokes the store's validateCredentials() method, passing in the SecurityContext and the credentials to validate.

  • 1.1.1 - In JPAIdentityStore's implementation of the validateCredentials() method, the SecurityContext is used to look up the CredentialHandler implementation that has been configured to process validation requests for usernames and passwords, which is then stored in a local variable called handler.

  • 1.1.2 - The validate() method is invoked on the CredentialHandler, passing in the security context, the credentials value and a reference back to the identity store. The reference to the identity store is important as the credential handler may require it to invoke certain methods upon the store to validate the credentials.

The CredentialHandler interface declares three methods, as follows:

public interface CredentialHandler {

    void setup(IdentityStore<?> identityStore);
    void validate(SecurityContext context, Credentials credentials, 
                  IdentityStore<?> identityStore);
    void update(SecurityContext context, Agent agent, Object credential, 
                IdentityStore<?> identityStore, Date effectiveDate, Date expiryDate);
}

The setup() method is called once, when the CredentialHandler instance is first created. Credential handler instantiation is controlled by the CredentialHandlerFactory, which creates a single instance of each CredentialHandler implementation to service all credential requests for that handler. Each CredentialHandler implementation must declare the types of credentials that it is capable of supporting, which is done by annotating the implementation class with the @SupportsCredentials annotation like so:

@SupportsCredentials({ UsernamePasswordCredentials.class, Password.class })

public class PasswordCredentialHandler implements CredentialHandler {

Since the validate() and update() methods receive different parameter types (validate() takes a Credentials parameter value while update() takes an Object that represents a single credential value), the @SupportsCredentials annotation must contain a complete list of all types supported by that handler.

Similarly, if the IdentityStore implementation makes use of the credential handler SPI then it also must declare which credential handlers support that identity store. This is done using the @CredentialHandlers annotation; for example, the following code shows how JPAIdentityStore is configured to be capable of handling credential requests for usernames and passwords, X509 certificates and digest-based authentication:

@CredentialHandlers({ PasswordCredentialHandler.class,

          X509CertificateCredentialHandler.class, DigestCredentialHandler.class })
public class JPAIdentityStore implements IdentityStore<JPAIdentityStoreConfiguration>, 
                                         CredentialStore {

For IdentityStore implementations that support multiple credential types (such as JPAIdentityStore and FileBasedIdentityStore), the implementation may choose to also implement the CredentialStore interface to simplify the interaction between the CredentialHandler and the IdentityStore. The CredentialStore interface declares methods for storing and retrieving credential values within an identity store, as follows:

public interface CredentialStore {

   void storeCredential(SecurityContext context, Agent agent, 
                        CredentialStorage storage);
   <extends CredentialStorage> T retrieveCurrentCredential(SecurityContext context, 
                                                 Agent agent, Class<T> storageClass);
   <extends CredentialStorage> List<T> retrieveCredentials(SecurityContext context, 
                                                 Agent agent, Class<T> storageClass);
}

The CredentialStorage interface is quite simple and only declares two methods, getEffectiveDate() and getExpiryDate():

public interface CredentialStorage {

   @Stored Date getEffectiveDate();
   @Stored Date getExpiryDate();
}

The most important thing to note above is the usage of the @Stored annotation. This annotation is used to mark the properties of the CredentialStorage implementation that should be persisted. The only requirement for any property values that are marked as @Stored is that they are serializable (i.e. they implement the java.io.Serializable interface). The @Stored annotation may be placed on either the getter method or the field variable itself. Here's an example of one of a CredentialStorage implementation that is built into PicketLink - EncodedPasswordStorage is used to store a password hash and salt value:

public class EncodedPasswordStorage implements CredentialStorage {


    private Date effectiveDate;
    private Date expiryDate;
    private String encodedHash;
    private String salt;
    @Override @Stored
    public Date getEffectiveDate() {
        return effectiveDate;
    }
    public void setEffectiveDate(Date effectiveDate) {
        this.effectiveDate = effectiveDate;
    }
    @Override @Stored
    public Date getExpiryDate() {
        return expiryDate;
    }
    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }
    @Stored
    public String getEncodedHash() {
        return encodedHash;
    }
    public void setEncodedHash(String encodedHash) {
        this.encodedHash = encodedHash;
    }
    @Stored
    public String getSalt() {
        return this.salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }
}

PicketLink has been designed from the ground up to support a system of partitioning, allowing the users, groups and roles of an application to be divided into Realms and Tiers.

A Realm is used to define a discrete set of users, groups and roles. A typical use case for realms is the segregation of corporate user accounts within a multi-tenant application, although it is not limited this use case only. As all identity management operations must be performed within the context of an active partition, PicketLink defines the concept of a default realm which becomes the active partition if no other partition has been specified.

A Tier is a more restrictive type of partition than a realm, as it only allows groups and roles to be defined (but not users). A Tier may be used to define a set of application-specific groups and roles, which may then be assigned to groups within the same Tier, or to users and groups within a separate Realm.

In terms of API, both the Realm and Tier classes implement the Partition interface, as shown in the following class diagram:

Selecting the specific partition that the identity management operations are performed in is controlled by specifying the partition when creating the IdentityManager via the IdentityManagerFactory's overloaded createIdentityManager() methods:

  IdentityManager createIdentityManager();

    IdentityManager createIdentityManager(Partition partition);

The first method (without parameters) will create an IdentityManager instance for the default realm. The second parameter allows a Partition object to be specified. Once the IdentityManager has been created, any identity management methods invoked on it will be performed within the selected partition. To look up the partition object, the IdentityManagerFactory provides two additional methods:

  Realm getRealm(String id);

    Tier getTier(String id);

Here's an example demonstrating how a new user called "bob" is created in a realm called acme:

  Realm acme = identityManagerFactory.getRealm("acme");

    IdentityManager im = identityManagerFactory.createIdentityManager(acme);
    im.add(new SimpleUser("bob"));

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 4.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 4.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 4.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 4.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 4.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 .