Use Case Description
This article describes a use case scenario for Tivoli Federated Identity manager requiring the development of a custom Security Token Service module. This is an advanced article targeted at readers who are already familiar with Tivoli Federated Identity Manager and the security token service it provides. The article extends the concept I introduced in Complex Federation Identity and Attribute Mapping for proxying STS mapping module processing to another STS chain. In this article we will look at the concept of proxying not just STS identity mapping operations, but also STS token validation and issuing operations. The proxying of token validation and issuing described in this article is NOT suitable for SSO federations - it is only suitable for scenarios where you have manually constructed STS chains such as in ESB or web services scenarios. The structure of STS chains for SSO federations is defined by TFIM itself and you may not substitute an arbitrary validate or issue module in those chains. Only custom mapping modules may be used in the STS chains associated with SSO federations. Said another way, the module included in this article may be used in "map" mode in an SSO federation, but may not be used in "validate", "issue" or "exchange" modes in an SSO federation. The module may be used in any mode in a custom STS chain. As with previous articles I have written, source code is provided for the STS token module described however this module itself is not formally supported by IBM. The interfaces it uses are supported, and I am happy to answer any questions you may have about the module. For detailed information on development of custom STS modules see the STS Module Development Tutorial.
Consider a use case where you have multiple TFIM installations installed in your environment, each associated with a different security domain (e.g. different user registries). You wish to provide a single STS endpoint to all clients which supports validating security tokens, however the real work of validating that token may need to be proxied out to one of a number of different STS chains on either the same or separate servers. The tokens to be validated may even be of different types, or be of the same type and simply require different STS instances to perform the validation due to key material or user registry partitioning.
The following diagram demonstrates the type of scenario described (click on the picture to view original size):
This scenario presents a configuration challenge as typically STS chains are constructed with the first module in a chain set to validate a token of a particular type, and configured with particular key configuration or backend validation database, etc. Sometimes (as in the case of UsernameToken) the validation database is bound to the current user registry for WebSphere or TAM and cannot be changed on a per-instance basis.
The proxy STS module included with this article can be used in "validate" mode as the first module in a chain to implement the scenario described above. The proxy STS module must have some way to determine from the incoming request which secondary STS to proxy the request to. While there may be many different ways this could be accomplished (e.g. token type introspection), the technique I have chosen for my demo module is to perform regular expression matching on only the Issuer address and AppliesTo address of the incoming WS-Trust request. This is a relatively simple technique that should suit most use cases. As the Issuer address and/or AppliesTo address will have to be different for each unique client, this means that the configuration of the primary STS chain will need to also use regular expressions for one or both of those elements so that the chain will be chosen by TFIM to process the initial client's request. TFIM supports a special REGEXP configuration syntax to support this. The best way to describe the configuration is by way of an example.
Expanding on the example scenario in the previous diagram, we will prescribe that Client 1 and Client 2 will use different Issuer addresses as shown:
The following diagram shows the chain mapping lookup configuration used to match either of these clients. In particular note the use of the REGEXP syntax for the Issuer Address:
Using a REGEXP for the AppliesTo and/or Issuer address will allow different unique client requests to map to the same chain at the STS.
The next part of the configuration puzzle is how to configure the Proxy STS module to know which secondary STS to send the request to, and what request parameters to set. It's possible that the secondary STS chain is on the same physical STS as the primary STS chain and in that case the request parameters used for chain selection (RequestType, Issuer, AppliesTo, TokenType) must be somehow different from the initial client request so that a different STS chain is executed, otherwise an endless calling loop would occur.
The Proxy STS module I have developed in conjunction with this article has a rather complex configuration model, split into three parts:
- An ordered list of match rules which decides which target STS and request mapping definition will be used as the proxy target.
- A set of named STS target definitions
- A set of named request mapping definitions
The Proxy STS module must be configured with one or more match rules. Each match rule is a JSON string containing a simple set of name/value strings. The match rules form an ordered list, and the first rule which matches an incoming request will determine the STS target and request mapping definitions to use for proxying this request.
Here is an example ordered list of match rules:
The JSON string representing a match rule must contain an STSTarget and RequestMapping attribute, and may contain an Issuer and/or AppliesTo attribute. The value of the STSTarget attribute must match the name of a defined STS target definition. The value of the RequestMapping attribute must match the name of a defined request mapping definition. The value of an optional Issuer or AppliesTo attribute is itself a Java regular expression which will be used to match against the incoming STS request. If an Issuer attribute is not provided, no matching condition will be applied against the issuer address of the incoming request. If an AppliesTo attribute is not provided, no matching condition will be applied against the AppliesTo address of the incoming request. Therefore if there is no Issuer and no AppliesTo attributes, the incoming request will always match the match rule (as per the last match rule in the above examples).
In the example match rules above no checking is done on AppliesTo addresses, although of course since the chain mapping itself is configured with an AppliesTo address of "http://appliesto/saml" we know that will always be the value sent otherwise this chain would not be invoked in the first place. The Issuer address (which is a REGEXP match in the chain mapping definition) will be used to determine whether or not the request goes to sts1, sts2, or sts3, with sts3 being a "default" if the Issuer address does not end in usernametoken1 or usernametoken2. Note that the ordering of match rules is extremely important since the first rule that matches the incoming request will determine the target STS and request mapping definitions.
STS Target Definitions
The Proxy STS module must be configured with one or more STS target definitions. Each STS target definition is a JSON string containing a simple set of name/value strings. Each definition must contain both a unique Name and URL attribute, and may optionally contain Username, Password and SSLConfig attributes for connecting to the STS. Essentially these set of properties define everything that is needed to physically connect to the secondary STS.
Here is an example set of STS target definitions:
The Name is a simple string label used for reference from a match rule. The URL is the connection URL to the WS-Trust 1.2 endpoint of a target STS. The example code for the proxy STS module included in this article only supports WS-Trust 1.2 for target STS servers. The Username and Password attributes are required if basic-authentication protection is in place for the target STS. The SSLConfig is required if the URL endpoint is https. More information on configuring values for these parameters can be found in my previous article on Complex Federation Identity and Attribute Mapping.
Request Mapping Definitions
The Proxy STS module must be configured with one or more request mapping definitions. Each request mapping definition is a JSON string containing a simple set of name/value strings. Each definition must contain a unique Name attribute, and may optionally contain TokenType, AppliesTo, Issuer, PropagateClaims and RemoveRSTAttributes attributes. Essentially these set of properties define how certain parameters from the original request are mapped to the WS-Trust call made to the secondary STS.
Here is an example set of request mapping definitions:
The Name is a simple string label used for reference from a match rule. The TokenType, AppliesTo and Issuer parameters allow you to override the values from the initial request when constructing the WS-Trust message to the secondary STS. Additionally you can include replacement macros in these attributes which will be replaced with the value from the initial request. The replace macros are @TOKENTYPE@, @APPLIESTO@ and @ISSUER@ respectively. Using these replacement macros is optional. If no override is defined for one of those attributes, a copy of the value from the original request will be used. The PropagateClaims and StripRSTFromSTSUU attributes must be either "true" or "false". Both default to "true" if not explicitly set. The PropageClaims attribute determines whether or not any Claims element from the original request will be proxied to the target STS. The StripRSTFromSTSUU attribute is only applicable to "issue", "exchange" and "map" modes ("issue" and "exchange" are treated the same). In each of those cases the token being sent to the secondary STS will always be an STSUniversalUser token. This attribute decides whether or not to strip out the "RequestSecurityToken" attributes section of the STSUU that is sent to the secondary STS. This can be a very large element that is rarely required at the secondary STS so for performance reasons it is recommended to strip it when not needed.
Other Configuration Notes
Care must be taken when designing the chain mappings to ensure that you do not inadvertently point a proxy module back to its own chain thereby creating an endless calling loop. There is no built-in protection to detect that situation.
When configuring the Proxy STS module you will notice two sets of configuration panels - one labeled "self" configuration and the other "partner" configuration. It does not matter which set you use - only one set is requried. The module will first look for "partner" configuration data and if none is found will then look for "self" configuration data.
The structure of the secondary STS chain is quite important, and will depend on the mode that the STS Proxy module is operating in.
When the STS proxy module is operating in "validate" mode, the secondary STS chain should have a structure that looks like:
- Optional map modules
- STSUU Token module(issue)
When the STS proxy module is operating in "map" mode, the secondary STS chain should have a structure that looks like:
- STSUU Token module(validate)
- Optional map modules
- STSUU Token module(issue)
When the STS proxy module is operating in "issue" or "exchange" modes (logically equivalent), the secondary STS chain should have a structure that looks like:
- STSUU Token module(validate)
- Optional map modules
That concludes the configuration requirements and notes. As the example module is provided with source and is not part of the actual TFIM product, you would be responsible for any ongoing development and maintenance. Let's now discuss some of the internal development details of this module.
The STS Module Development Tutorial is compulsory reading before attempting to understand this section. The rest of this section highlights some key points about the source code in the module, and the development environment used to put it together.
Note: I used Rational Application Developer 8 as my development environment for the module, with IBM WebSphere 6.1 as the test environment. The module has been written to work with TFIM 6.2.0 and later.
The STSProxy module uses a primitive JAX-RPC WebSphere web services client for WS-Trust and all the generated stub code and helper classes have been provided. I chose to use this WS-Trust client for compatibility with WebSphere 6.1 for a particular scenario I was developing. If supporting only WebSphere 7.0 and later, I would have used the WebSphere WS-Trust client documented in my WS-Trust Clients article.
The com.tivoli.am.fim.demo.sts.client.stubs package contains all the generated stubs from the wsdl2java that we performed on the ws-trust.wsdl. This wsdl is really a skeleton ws-trust 1.2 wsdl, but perfectly adequate for our needs.
The com.tivoli.am.fim.demo.stsclient package contains a set of helper classes that are used to invoke the WS-Trust web services client. The main entry point into this package is the class STSClientHelper. Due to the fact that the JAX-RPC runtime uses SOAPFactory, we did have to make use of the J2EEContainerAction pattern described in the STS Module Development tutorial. You can see this in action in the class J2EEHelper.
The com.tivoli.am.fim.demo.stsproxy package contains the actual mapping module class as well as a message bundle class.
The com.tivoli.am.fim.demo.stsproxy.config and com.tivoli.am.fim.demo.stsproxy.config.impl packages contain helper classes and interfaces used by the proxy module to manage configuration.
When building the module yourself, you will most definitely need the com.tivoli.am.fim.sdk project described in the STS Module development tutorial. This will be needed to resolve a bunch of WebSphere-provided classes used byt the web services client runtime. In particular you will need to included these jars in the com.tivoli.am.fim.sdk classpath:
Then you will need to export the following packages from the com.tivoli.am.fim.sdk bundle as they are imported as dependencies by the com.tivoli.am.fim.demo.stsmap manifest:
As you can see from this example, custom STS modules in Tivoli Federated Identity Manager can be written to perform web services calls to other services for a variety of purposes including proxying requests to other STS servers. This technique allows one STS to delegate validation, mapping or issuing of a security token to another security token service. This is particular useful for token types which have validation or issuing constraints bound to a single instance of the STS such as those which have tight coupling to a user registry or unique set of keys.
If you have found this article useful, or believe this module is valuable enough to be considered for inclusion in the next GA release of TFIM, please contact me and give me your feedback.
If you have skipped to the bottom looking for the download link, here it is: Download the custom STSProxy module. Remember to rename it as a .jar file if it downloads as a zip.