Apache CXF has an impressive range of security features for JAX-RS endpoints. It also has excellent documentation in this space, see for example the JAX-RS documentation for
OAuth,
SAML Assertions and
XML Encryption/Signature. Apache CXF 2.6.1 enhances the JAX-RS security story further by featuring
support for the SAML 2.0 Web SSO
profile.
The basic standard scenario for Web SSO involves a client browser accessing a secured (JAX-RS) endpoint. The endpoint constructs a SAML AuthnRequest and includes it in redirecting the browser to a Identity Provider (IDP) for authentication (via a dialog). The IDP constructs a SAML Response, and redirects the browser to a Request Assertion Consumer Service (RACS) URI contained in the AuthnRequest. The RACS validates the SAML Response and redirects the browser yet again to the original secured endpoint. The endpoint stores the session in a cookie (hence single sign-on) and access is granted to the resource. The following steps detail how to implement this scenario in Apache CXF using the new "cxf-rt-rs-security-sso-saml" module.
1) JAX-RS binding filters
The SAML 2.0
bindings specification defines HTTP redirect and HTTP POST bindings which can be used to "redirect" the client browser, e.g. from the secured endpoint to the IDP. Apache CXF 2.6.1 provides a separate JAX-RS
filter to support these bindings. Therefore, the first step in securing a JAX-RS endpoint is to decide which of these two bindings to use. The
SamlRedirectBindingFilter class providers support for the HTTP redirect binding, and the
SamlPostBindingFilter provides support for the HTTP POST binding. Both of these filters share the following configuration properties.
1.1) Required Configuration Properties
Each filter implementation must be configured with the URL of the IDP, as well as the URL of the RACS. It must also be configured with a SPStateManager implementation to keep track of the Authenticated sessions. CXF ships with an EhCache based
implementation:
- SPStateManager stateProvider - An object which keeps track of authenticated sessions.
- String idpServiceAddress - The URL of the IDP.
- String assertionConsumerServiceAddress - The URL of the RACS. This can be a relative or absolute URL.
1.2) Signature Configuration Properties
The following configuration properties are only required if you wish to sign the AuthnRequest:
- boolean signRequest - Whether to sign the AuthnRequest or not. The default is false.
- String signatureUsername - The keystore alias to use to sign the AuthnRequest.
- Crypto signatureCrypto - A WSS4J Crypto object if the SAML AuthnRequest is to be signed.
- String signaturePropertiesFile - This points to a properties file that can be used to load a Crypto instance if the SAML AuthnRequest is to be signed.
- CallbackHandler callbackHandler - A CallbackHandler object to retrieve the private key password used to sign the request.
- String callbackHandlerClass - A class name that is loaded for use as the CallbackHandler object.
Either the "signatureCrypto" or "signaturePropertiesFile" properties must be set if "signRequest" is set to true. Similarly, either "callbackHandler" or "callbackHandlerClass" must be configured.
1.3) Optional Configuration Properties
Here are some optional configuration properties that can be set:
- long stateTimeToLive - The default value (in milliseconds) that a session is valid for. The default value is 2 minutes.
- String issuerId - The Issuer Id value to be included in the AuthnRequest. It defaults to the Base URI.
- AuthnRequestBuilder authnRequestBuilder - An object that constructs the AuthnRequest. It defaults to DefaultAuthnRequestBuilder.
There are some additional optional configuration properties relating to domains and web application contexts that will be covered in more detail on the CXF wiki.
1.4) Sample Spring configuration
Here is a sample spring configuration extract to support the redirect binding filter which signs AuthnRequests:
<bean id="stateManager" class="org.apache.cxf.rs.security.saml.sso.state.EHCacheSPStateManager"/>
<bean id="ssoSignedRedirectURI" class="org.apache.cxf.rs.security.saml.sso.SamlRedirectBindingFilter">
<property name="idpServiceAddress" value="https://.../idp-sig/"/>
<property name="assertionConsumerServiceAddress"
value="/racs/sso"/>
<property name="stateProvider" ref="stateManager"/>
<property name="addEndpointAddressToContext" value="true"/>
<property name="callbackHandlerClass" value="....SSOCallbackHandler"/>
<property name="signatureUsername" value="myservicekey"/>
<property name="signaturePropertiesFile" value="serviceKeystore.properties"/>
<property name="signRequest" value="true"/>
</bean>
<jaxrs:server address="/app1">
<jaxrs:serviceBeans><ref bean="..."/></jaxrs:serviceBeans>
<jaxrs:providers><ref bean="ssoRedirectURI"/></jaxrs:providers>
</jaxrs:server>
2) The Request Assertion Consumer Service
The JAX-RS filters described above take care of creating a SAML AuthnRequest, optionally signing it, and redirecting the client browser to the IDP. The next step is to define a RequestAssertionConsumerService (RACS) which will intercept the SAML Response from the IDP. The RACS processes the SAML Response, and validates it in a number of ways:
- The SAMLProtocolResponseValidator validates the Response against the specifications and checks the signature of the Response (if it exists), as well as doing the same for any child Assertion of the Response. It validates the status code of the Response as well.
- The SAMLSSOResponseValidator validates the Response according to the Web SSO profile.
If validation is successful the RACS redirects to the service provider endpoint.
2.1) Configuration properties shared with the filters
The
RequestAssertionConsumerService
that ships with CXF shares a number of configuration properties with
the filters, mainly relating to state and signature configuration:
- SPStateManager stateProvider - An object which keeps track of authenticated sessions. This is required.
- long stateTimeToLive - The default value (in milliseconds) that a session is valid for. The default value is 2 minutes.
- Crypto signatureCrypto - A WSS4J Crypto object to use to validate a signed Response.
- String
signaturePropertiesFile - This points to a properties file that can be
used to load a Crypto instance for signature validation.
- CallbackHandler callbackHandler - A CallbackHandler object to retrieve the private key password used to decrypt an encrypted request.
- String callbackHandlerClass - A class name that is loaded for use as the CallbackHandler object.
Only the "stateProvider" property is required. Either the "signatureCrypto" or "signaturePropertiesFile" properties
must be set if you wish to support verifying signed Responses, or signed Assertions contained in a Response. Similarly, either
"callbackHandler" or "callbackHandlerClass" must be configured if you wish to support decrypting encrypted Assertions.
2.2) Additional Optional Configuration properties
The RACS supports the following additional (optional) configuration properties:
- boolean supportDeflateEncoding - Whether Deflate encoding is used to inflate a token received via the Redirect binding. The default is true.
- boolean supportBase64Encoding - Whether the token is Base64 decoded or not. The default is true.
- boolean enforceAssertionsSigned - Whether the Assertions contained in a Response must be signed or not. The default is true.
- boolean enforceKnownIssuer - Whether the Issuer of the Response (and child Assertions) is "known" to the RACS. This value is compared against the IDP URL configured on the filter. The default value is true.
- TokenReplayCache<String> replayCache - A TokenReplayCache implementation to store Assertion ID's for the POST binding to guard against replay attacks. The default uses an implementation based on EhCache.
2.4) Sample Spring Configuration
Here is a sample spring configuration extract to set up a RACS, that could be used with the filter configuration in section 1.4:
<bean id="consumerService" class="org.apache.cxf.rs.security.saml.sso.RequestAssertionConsumerService">
<property name="stateProvider" ref="stateManager"/>
<property name="signaturePropertiesFile" value="serviceKeystore.properties"/>
<property name="enforceKnownIssuer" value="false"/>
<property name="callbackHandlerClass" value="...SSOCallbackHandler"/>
</bean>
3) The Identity Provider (IDP)
The remaining piece of the puzzle is the Identity Provider (IDP) which receives the SAML AuthnRequest from the service provider, processes it accordingly, and then interacts with the client browser to authenticate the client. It then creates a SAML Response and redirects the client browser to the RACS defined in the AuthnRequest. Apache CXF does not (yet) ship with an IDP, although this may change in the near future. However, there are a number of open-source IDPs that can be used with a CXF JAX-RS endpoint that supports SAML Web SSO. These include
Shibboleth,
PicketLink,
OpenAM,
JOSSO, etc. I have tested against all of these products. Report any interop issues to the CXF
JIRA.
4) Ongoing Development
SAML Web SSO profile support is still being actively developed. A number of bugs have been fixed that do not yet appear in a released version of CXF. For example,
support for decrypting encrypted Assertions in a SAML Response, and some bugs relating to signed Assertions (
CXF-4352 and
CXF-4365).