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.
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.
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.
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.
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.
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.
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).
Hi, Thank you for the article!
ReplyDeleteUsing 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
Hi Susil,
ReplyDeleteIf 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.
Hi ,
ReplyDeletei 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
Hi colm,
ReplyDeleteWhere do we set the IDP metadata information on CXF side(Service provider) ?
ReplyDeleteYou 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.
Thanks Colm. I was able to finish it (though i am updating a bit too late)
ReplyDeleteI have checked in the code in github(https://github.com/SaurabhMIttal/SingleSignOn) so that it may help people reading this nice blog in future
https://github.com/SaurabhMIttal/SingleSignOn/cxfRest
DeleteWhat about SSO support for JAX-WS endpoints?
ReplyDelete"If validation is successful the RACS redirects to the service provider endpoint. "
ReplyDeleteWhat happen if the service provider endpoint was called with POST and the service provider is able to serve only POST request?
What I got is HTTP/1.1 405 Method Not Allowed
Please send your query to the CXF users list instead.
ReplyDeleteColm.