Tuesday, June 19, 2012

SAML Web SSO profile support in Apache CXF

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

8 comments:

  1. Hi, Thank you for the article!
    Using Shibboleth, When secured cxf rs service is called from a browser, it does redirects to IDP site with login and password. Once authenticated IDP does not seems to redirect back to RACS. Can you please provide some idea on how this can be debugged or is there any missing piece you suggest to look at.
    Thank you.
    Susil

    ReplyDelete
  2. Hi Susil,

    If the Shibboleth IDP is not redirecting back to the RACS then I suggest you take a look at the Shibboleth logs to see what is going wrong.

    There are a number of steps you have to follow to get Shibboleth working with a CXF JAX-RS endpoint:

    a) Add MetadataProvider configuration for the CXF endpoint in the Shibboleth "relying-party.xml" configuration.
    b) Look for the "ProfileConfiguration" of type "saml:SAML2SSOProfile" in "relying-party.xml" and change the "encryptAssertions" attribute
    from "conditional" to "never" (this step is optional from CXF 2.6.2 onwards).
    c) Edit "attribute-filter.xml" and look for "AttributeFilterPolicy". Add the following:

    <afp:AttributeFilterPolicy id="releasePersistentIdToAnyone">
    <afp:PolicyRequirementRule xsi:type="basic:ANY"/>

    <afp:AttributeRule attributeID="persistentId">
    <afp:PermitValueRule xsi:type="basic:ANY"/>
    </afp:AttributeRule>

    </afp:AttributeFilterPolicy>"

    Then edit "attribute-resolver.xml" and search for the "transient" AttributeDefinition. Add the following:

    <resolver:AttributeDefinition id="persistentId" xsi:type="ad:PrincipalName">
    <resolver:AttributeEncoder xsi:type="enc:SAML1StringNameIdentifier" nameFormat="urn:mace:shibboleth:1.0:nameIdentifier"/>
    <resolver:AttributeEncoder xsi:type="enc:SAML2StringNameID" nameFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
    </resolver:AttributeDefinition>"

    That will enable Shibboleth to process the "persistent" attribute using the Principal Name.

    If this doesn't work I suggest mailing the users@cxf list with your configuration + Shibboleth config.

    Colm.

    ReplyDelete
  3. Hi ,

    i was trying to configure cxf rest service as SP using "SamlRedirectBindingFilter" with OpenAM(opensso) as IDP
    In the process , configuring remote SP in openAM i provided the Metadata.xml



    urn:oasis:names:tc:SAML:2.0:nameid-format:persistent




    The request is getting redirected to openAM loginpage , but after authentication its landing to home page of OpenAM instead of redirecting to rest service
    Help will be appreciatede

    thanks

    ReplyDelete
  4. Hi colm,

    Where do we set the IDP metadata information on CXF side(Service provider) ?

    ReplyDelete

  5. You don't need to set the IDP metadata information on the service provider side...just have the correct idp service address ("http://localhost:8080/opensso/SSORedirect/metaAlias/idp") worked for me when testing with OpenAM. You do however need to add metadata for the service provider to OpenAM. Please redirect all future questions to the CXF user mailing list instead of here.

    Colm.

    ReplyDelete
  6. Thanks Colm. I was able to finish it (though i am updating a bit too late)
    I have checked in the code in github(https://github.com/SaurabhMIttal/SingleSignOn) so that it may help people reading this nice blog in future

    ReplyDelete
    Replies
    1. https://github.com/SaurabhMIttal/SingleSignOn/cxfRest

      Delete
  7. What about SSO support for JAX-WS endpoints?

    ReplyDelete