The new features are:
- Federation Metadata
The IDP supports publishing the WS-Federation Metadata document which allows to more easily integrate the IDP into platforms which support referencing a Metadata document. Metadata consists of the signing certificate, the provided claims, etc.
- Spring Web Flow support
The IDP has been refactored to use Spring Web Flow to manage the federation flow. This provides flexibility to be able to customize the IDP to company's specific requirements. The IDP is secured by Spring Security to get the benefits and flexibility of Spring Security.
- Resource IDP and Home Realm Discovery
This is the major new feature. The IDP is able to figure out from which security domain/realm the browser request is coming from to redirect the sign-in request to the requestor IDP which does the authentication and issues a token which is sent to the Resource IDP. The Resource IDP will then either map the principal from one security domain to the target security domain and get claims information of the mapped principal or transform the claims information and finally issue a new token for the relying party (application).
More complex scenarios described in this blog are now possible.
In this post, I'd like to focus on the last feature which I splitted in two posts. The first one focuses on extending the STS to support more than one security domain/realm and the second on the IDP.
Install Apache CXF Fediz IDP
The Fediz STS is provided as part of the subproject Fediz in Apache CXF. The new version provides the Apache Maven profile realms which packages an STS with two security domains called REALM A and REALM B.You can either download the sources of the Fediz STS from the following location
git clone git://git.apache.org/cxf-fediz.git
or
svn co https://svn.apache.org/repos/asf/cxf/fediz/trunkYou can build the whole project with the following Maven command
mvn clean installThe STS is packaged as a WAR file at
services/sts/target/fediz-idp-sts.war
or download the archive from the snapshot maven repository.
The URL of the STS has the following syntax depending on the hostname and port of your servlet engine.
https://localhost:port/fediz-idp-sts/?wsdl
The page will list the different URL's for the web service endpoints of the STS:
https://localhost:9443/fediz-idp-sts/REALMA/STSServiceTransportUT
STS endpoint for REALM A which requires username/password authenticationhttps://localhost:9443/fediz-idp-sts/REALMA/STSServiceTransport
STS endpoint for REALM A with requires on-behalf-of saml token (implicit identiy mapping is done)https://localhost:9443/fediz-idp-sts/REALMB/STSServiceTransportUT
STS endpoint for REALM B which requires username/password authenticationhttps://localhost:9443/fediz-idp-sts/REALMB/STSServiceTransport
STS endpoint for REALM B with requires on-behalf-of saml token (implicit identiy mapping is done)
(.../fediz-idp-sts/<realm>/STSServiceTransportUT)
. After authentication, you request (implicitly as part of the federation protocol) a specific token of the security domain/realm where the application is conntected to. This use case makes use of the on-behalf-of feature of the STS (.../fediz-idp-sts/<realm>/STSServiceTransport)
.
The following users are set up in realm A and realm B:
User | Password |
Realm A | |
alice | eclia |
bob | bob |
ted | det |
Realm B | |
ALICE | ECLIA |
BOB | BOB |
TED | DET |
As you might imagine, the identity mapping implementation is fairly easy as you just lower- or uppercase the principal name. More information about how to plug in different identity mappings see below in Identity Mapper.
How to add a new security domain to the STS
If you customize the realms or add an additional realm, the following steps are required. You can either customize the deployed WARfediz-idp-sts.war
of the downloaded archive or make updates to the sourcesNote: You could also make use of the Overlay feature of Apache Maven.
- Define a new realm C
Some beans must be updated and added to the Spring configuration file
cxf-transport.xml
. The changes for a new realm are highlighted in bold.Source location: src/realms/webapps/WEB-INF/
Deploy location:/WEB-INF/ <util:map id="realms"> <entry key="REALMA" value-ref="realmA"/> <entry key="REALMB" value-ref="realmB"/> <entry key="REALMC" value-ref="realmC"/> </util:map> <bean id="realmA" class="org.apache.cxf.sts.token.realm.SAMLRealm"> <property name="issuer" value="STS Realm A"/> <property name="signaturePropertiesFile" value="stsKeystoreA.properties" /> <property name="callbackHandlerClass" value="org.apache.cxf.fediz.service.sts.PasswordCallbackHandler" /> </bean> <bean id="realmB" class="org.apache.cxf.sts.token.realm.SAMLRealm"> <property name="issuer" value="STS Realm B"/> <property name="signaturePropertiesFile" value="stsKeystoreB.properties" /> <property name="callbackHandlerClass" value="org.apache.cxf.fediz.service.sts.PasswordCallbackHandler" /> </bean> <bean id="realmC" class="org.apache.cxf.sts.token.realm.SAMLRealm"> <property name="issuer" value="STS Realm C"/> <property name="signaturePropertiesFile" value="stsKeystoreC.properties" /> <property name="callbackHandlerClass" value="org.apache.cxf.fediz.service.sts.PasswordCallbackHandler" /> </bean> <util:list id="relationships"> <bean class="org.apache.cxf.sts.token.realm.Relationship"> <property name="sourceRealm" value="REALMA" /> <property name="targetRealm" value="REALMB"/> <property name="identityMapper" ref="identityMapper" /> <property name="type" value="FederatedIdentity" /> </bean> <bean class="org.apache.cxf.sts.token.realm.Relationship"> <property name="sourceRealm" value="REALMB" /> <property name="targetRealm" value="REALMA"/> <property name="identityMapper" ref="identityMapper" /> <property name="type" value="FederatedIdentity" /> </bean> <bean class="org.apache.cxf.sts.token.realm.Relationship"> <property name="sourceRealm" value="REALMA" /> <property name="targetRealm" value="REALMC"/> <property name="identityMapper" ref="identityMapper" /> <property name="type" value="FederatedIdentity" /> </bean> </util:list>
In this case, only a trust relationship from realm A to realm C is established and it's based on identity mapping. - Generate signing certificate
The new realm signs the SAML assertion with a different signer certificate.
keytool -genkeypair -keyalg RSA -validity 3600 -alias realmc -keystore stsrealm_c.jks -dname "cn=REALMC" -keypass realmc -storepass storepass keytool -export -keystore stsrealm_c.jks -storepass storepass -export -alias realmc -file realmc.cert
The keystore has to be added
Source location: src/realms/resources
Deploy location:/WEB-INF/classes - Import certificate into truststore
ststrust.jks
The certificate must be added to the truststore thus another realm is able to validate the SAML assertion signed by realm C.
Note: The above federation relationship definitions do not cover the use case that another realm must trust realm C but I recommend to add the certificate for completeness reasons.
keytool -import -trustcacerts -keystore ststrust.jks -storepass storepass -alias realmc -file realmc.cert -noprompt
The truststore has to be updated at
Source location: src/realms/resources
Deploy location:/WEB-INF/classes - Configure signer certificate
Above, the signer certificate is configured for the SAML Realm C in the attribute
signaturePropertiesFile
of the beanrealmC
. The properties file has to look like this:org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=storepass org.apache.ws.security.crypto.merlin.keystore.alias=realmc org.apache.ws.security.crypto.merlin.file=stsrealm_c.jks
Note: Copy&paste an existing stsKeystore properties file.The properties file has to be added here:
Source location: src/realms/resources
Deploy location:/WEB-INF/classes - Define JAX-WS endpoint in STS
Two JAX-WS endpoints are required depending on whether you need to support username/password authentication in the STS (WSDL Port
TransportUT_Port
) and/or issue a SAML token on-behalf-of another SAML token (WSDL PortTransport_Port
).The former requires an authentication backend which is file based in the Fediz STS. The file based authentication backend relies on the
UsernamePasswordCallbackHandler
which uses a spring map with user name and password entries (see beansupCallBackHandlerRealmC
andREALMC
).<jaxws:endpoint id="transportSTSRealmC" implementor="#transportSTSProviderBean" address="/REALMC/STSServiceTransport" wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl" xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" serviceName="ns1:SecurityTokenService" endpointName="ns1:Transport_Port"> <jaxws:properties> </jaxws:properties> </jaxws:endpoint> <jaxws:endpoint id="transportSTSRealmCUT" implementor="#transportSTSProviderBean" address="/REALMC/STSServiceTransportUT" wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl" xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" serviceName="ns1:SecurityTokenService" endpointName="ns1:TransportUT_Port"> <jaxws:properties> <entry key="ws-security.callback-handler" value-ref="upCallBackHandlerRealmC" /> </jaxws:properties> </jaxws:endpoint> <bean id="upCallBackHandlerRealmC" class="org.apache.cxf.fediz.service.sts.UsernamePasswordCallbackHandler"> <property name="passwords" ref="REALMC" /> </bean> <util:map id="REALMC"> <entry key="user1" value="pw1" /> </util:map>
The bean which defines the username and passwords is defined in the imported spring configuration filepasswords.xml
. The claims of the users in the two realms are defined inuserClaims.xml
. You can find more information about managing the claims in an older blog.If you want to configure an LDAP backend check the following blog post:
- Identity mapper
If you add a new realm, the Identity Mapper provided in
org.apache.cxf.fediz.service.sts.realms.IdentityMapperImpl
must be enhanced. In practise, you access a database, LDAP or web service to get the mapping. You must implement the interfaceorg.apache.cxf.sts.IdentityMapper
to customize the identity mapping to your needs.
Customize realm concept
-
The Fediz STS is based on the Apache CXF STS. The STS supports several realms where each realm is connected to an authentication server like LDAP, file (Testing), database or a custom authentication server. The service consumer must know from which STS realm he requests a token from by choosing the appropriate URL. The distinction is based on the syntax in the URL.
The syntax in the Fediz STS is
https://
. The distinction is after the servlet context: /fediz-idp-sts/<REALM>/... fediz-idp-sts
. This behaviour is implemented inorg.apache.cxf.fediz.service.sts.realms.UriRealmParser
and configured in the beancustomRealmParser
.You can implement a different distinction based on the machine name, http port or servlet context. Maybe you prefer to deploy the realms in different JVMs. You just have to implement the interface
org.apache.cxf.sts.RealmParser
. -
When you request a token on-behalf-of another SAML token, the STS must be able to figure out from which realm the token has been issued. As above, the STS provides an interface which must be implement to change the default behaviour of the Fediz STS. The default behaviour is implemented in
org.apache.cxf.fediz.service.sts.realms.SamlRealmCodec
and expects the realm encoded in the CN (Common Name) of the signer certificate. This class implements the interfaceorg.apache.cxf.sts.token.realm.SAMLRealmCodec
and can be customized. You have to update the beansamlRealmCodec
or update the bean which references this bean.
Please post feedback and ideas to the CXF mailing list.
Thank you for all support and feedback!
No comments:
Post a Comment