Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

From the 3.3.0 release, the Claims access control annotations/interceptors now work with JWT tokens (as well as SAML tokens). This resulted in the following package changes:

  • ClaimsAuthorizingInterceptor has moved from the cxf-rt-security-saml module to the cxf-rt-security module. The package name of the ClaimsAuthorizingInterceptor has changed: from org.apache.cxf.rt.security.saml.interceptor.ClaimsAuthorizingInterceptor to org.apache.cxf.rt.security.claims.interceptor.ClaimsAuthorizingInterceptor.
  • ClaimsAuthorizingFilter has moved from the cxf-rt-rs-security-xml module to the cxf-rt-frontend-jaxrs module. The package name of the ClaimsAuthorizingFilter  has changed: from org.apache.cxf.rs.security.saml.authorization.ClaimsAuthorizingFilter to orgto org.apache.cxf.rsjaxrs.security.claims.ClaimsAuthorizingFilter

Maven dependencies

Code Block
xml
xml
<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-security-xml</artifactId>security</artifactId>
  <version>3.3.0</version>
</dependency>

In addition, cxf-rt-rs-security-xml is required if you are working with SAML tokens, and cxf-rt-rs-security-jose-jaxrs is required if you are working with JWT tokens.

Claims based access control

Claims annotations

Here is a simple code fragment to secure a service object using Claims annotations:

...

where "auth-format" and "authentication" are aliases for "http://claims/authentication-format" and "http://claims/authentication" respectively.

Given the above example, the question is how to extract the information available in a received token (SAML/JWT) for the current request to succeed in passing through the security filter enforcing the CBAC rules.

The first and most important thing which needs to be done is to verify that an assertion Subject can be mapped to a recognized identity instance.

There is a number of ways a Subject can be validated.

If STS is asked to validate the assertion then a successful response from IDP will likely be good enough for CXF to trust the identity of the provider.
If the assertion signature is verified locally using the public key of IDP then it could a good enough confirmation too.

Alternatively, a custom validator, extending either org.apache.ws.security.validate.SamlAssertionValidator or CXF SAML SecurityContextProvider implementation can be registered with the server side SAML handler.

The latter option is preferred because not only one can validate Subject - but also ensure that a resulting SecurityContext will return a user Principal with a proper name - given that the actual Subject name available in the assertion may need to be translated to a name recognized by the local security stores or application. A combination of the assertion's Subject and AttributeStatement elements may need to be checked to establish a real name.

In cases like this you may want to register a custom SecurityContextProvider even if you have STS validating the assertion. Yet another reason is to retrieve the information about roles for a given Subject or map the assertion claims to roles for working with the RBAC to succeed, see the next section for more information.

Enforcing Claims authorization

Simply adding Claims annotations are per the examples above is not sufficient to enforce claims based authorization.

First we need to configure the appropriate interceptors/filters to authenticate the type of token we are interested in extracting claims from. See the JAX-RS SAML page for information on how to configure SAML, and the JAX-RS JOSE page for information on how to configure JWT.

For both SAML and JWT, once the incoming token is validated, a ClaimsSecurityContext security context will be created containing the claims contained in the token, as well as the authenticated subject and role (claims).

To enforce claims authorization, a ClaimsAuthorizingInterceptor must be set as an "inInterceptor", passing it a reference to the secured object. There is also a JAX-RS filter wrapper around ClaimsAuthorizingInterceptor available, which is called ClaimsAuthorizingFilter.

An instance of org.apache.cxf.rs.security.saml.authorization.ClaimsAuthorizingFilter (note org.apache.cxf.rs.security.claims.ClaimsAuthorizingFilter from CXF 3.3.0) is used to enforce CBAC. It's a simple JAX-RS filter wrapper around ClaimsAuthorizingInterceptor.

Here is an example of enforcing Claims authorization against a JWT token. BookStoreAuthn is the service object which is annotated with Claims annotations. The ClaimsAuthorizingFilter is added as a JAX-RS provider to the endpoint, wrapping the serviceBean. A JwtAuthenticationFilter instance is also added to validate the received JWT token and to set up the ClaimsSecurityContext. The rs.security.signature.in.properties property is used to verify the signature on the received token.Have a look please at this server configuration example:

Code Block
xml
xml
<bean id="serviceBeanClaimsserviceBean" class="org.apache.cxf.systest.jaxrs.security.samljose.jwt.SecureClaimBookStoreBookStoreAuthn"/>

<bean id="samlEnvHandlerclaimsHandler" class="org.apache.cxf.rsjaxrs.security.saml.SamlEnvelopedInHandlerClaimsAuthorizingFilter">
    <property name="securityContextProvidersecuredObject">
    <bean class="org.apache.cxf.systest.jaxrs.security.saml.CustomSecurityContextProvider ref="serviceBean"/>
 </property>
</bean>
    
<bean id="claimsHandlerjwtAuthzFilter" 
     class="org.apache.cxf.rs.security.samljose.authorizationjaxrs.ClaimsAuthorizingFilterJwtAuthenticationFilter">
    <property name="securedObjectroleClaim" refvalue="serviceBeanClaimsrole"/>   
</bean>

<jaxrs:server address="/saml-claims"> 
https://localhost:${testutil.ports.jaxrs-jwt-authn-authz}/signedjwtauthz">
        <jaxrs:serviceBeans>
            <ref bean="serviceBeanClaimsserviceBean"/>
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref bean="samlEnvHandlerjwtAuthzFilter"/>
            <ref bean="claimsHandler"/>
        </jaxrs:providers>
</jaxrs:server>

...

        <jaxrs:properties>
            <entry key="rs.security.

...

signature.in.properties"
                   value="org/apache/cxf/systest/jaxrs/security/bob.jwk.properties"/>
        </jaxrs:properties>
</jaxrs:server>

Role based access control

If we have a SAML Assertion or JWT token with claims that are known to represent roles, then making those claims work with an RBAC system can be achieved easily.

SimpleAuthorizingInterceptor

One option is to enforce that only users in a given role can access a method in the service bean is to use CXF's SimpleAuthorizingInterceptor. It has a "methodRolesMap" property can maps method names to roles. This interceptor must then be added to the inInterceptor chain of the service endpoint. For example:

Code Block
xml
xml
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.jose.jwt.BookStoreAuthn"/>

<bean id="jwtAuthzFilter" class="org.apache.cxf.rs.security.jose.jaxrs.JwtAuthenticationFilter">
    <property name="roleClaim" value="role"/>
</bean>

<bean id="authorizationInterceptor" 
    class="org.apache.cxf.interceptor.security.SimpleAuthorizingInterceptor">
    <property name="methodRolesMap">
        <map>
            <entry key="echoBook" value="boss"/>
            <entry key="echoBook2" value="boss"/>
        </map>
    </property> 
</bean>

<jaxrs:server address="https://localhost:${testutil.ports.jaxrs-jwt-authn-authz}/signedjwtauthz">
        <jaxrs:serviceBeans>
            <ref bean="serviceBean"/>
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref bean="jwtAuthzFilter"/>
        </jaxrs:providers>
        <jaxrs:inInterceptors>
            <ref bean="authorizationInterceptor"/>
        </jaxrs:inInterceptors>
        <jaxrs:properties>
            <entry key="rs.security.signature.in.properties"
                   value="org/apache/cxf/systest/jaxrs/security/bob.jwk.properties"/>
        </jaxrs:properties>
</jaxrs:server>

Using annotations

Instead of mapping method names to roles using the SimpleAuthorizingInterceptor, we can instead annotate them in the service bean with javax.annotation.security.RolesAllowed or org.springframework.security.annotation.Secured annotations. For example:

import org.springframework.security.annotation.Secured;
 
@Path("/bookstore")
public class SecureBookStore {
     
    @POST
    @Secured("admin")
    public Book addBook(Book book) {
        return book;
    }
}

where @Secured can be replaced with @RoledAllowed if needed, the following configuration will do it:

<bean id="serviceBeanRoles" class="org.apache.cxf.systest.jaxrs.security.saml.SecureBookStore"/>
<bean id="samlEnvHandler" class="org.apache.cxf.rs.security.saml.SamlEnvelopedInHandler">
 <property name="securityContextProvider">
    <bean class="org.apache.cxf.systest.jaxrs.security.saml.CustomSecurityContextProvider"/>
 </property>
</bean>
 
<bean id="authorizationInterceptor" class="org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor">
    <property name="securedObject" ref="serviceBean"/>
    <property name="annotationClassName"
              value="org.springframework.security.annotation.Secured"/>
</bean>
     
<bean id="rolesHandler" class="org.apache.cxf.jaxrs.security.SimpleAuthorizingFilter">
    <property name="interceptor" ref="authorizationInterceptor"/>
</bean>
     
<jaxrs:server address="/saml-roles">
  <jaxrs:serviceBeans>
     <ref bean="serviceBeanRoles"/>
  </jaxrs:serviceBeans>
  <jaxrs:providers>
      <ref bean="samlEnvHandler"/>
      <ref bean="rolesHandler"/>
  </jaxrs:providers>
   
  <!-- If default role qualifier and format are not supported:
        
  <jaxrs:properties>
     <entry key="org.apache.cxf.saml.claims.role.nameformat"
                value="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"/>
     <entry key="org.apache.cxf.saml.claims.role.qualifier"
                value="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"/>
  </jaxrs:properties>
  -->
</jaxrs:server>

That is all what is needed. Note that in order to help the default SAML SecurityContextProvider figure out which claims are roles, one can set the two properties as shown above - this not needed if it's known that claims identifying roles have NameFormat and Name values with the default values, which are "http://schemas.xmlsoap.org/ws/2005/05/identity/claims" and "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role" respectively at the moment.

Note that you can have RBAC and CBAC combined for a more sophisticated access control rules be enforced while still keeping the existing code relying on @RolesAllowed or @Secured intact. Override ClaimsAuthorizingFilter and configure it with the Claims rules directly and register it alongside SimpleAuthorizingFilter and here you go.

Also note how SecureAnnotationsInterceptor can handle different types of role annotations, with @RolesAllowed being supported by defaultauthorization.ClaimsAuthorizingFilter (note org.apache.cxf.rs.security.claims.ClaimsAuthorizingFilter from CXF 3.3.0) is used to enforce CBAC. It's a simple JAX-RS filter wrapper around ClaimsAuthorizingInterceptor. SamlEnvelopedInHandler processes and validates SAML assertions and it also relies on a simple CustomSecurityContextProvider to help it to figure out what the actual Subject name is. A more involved implementation can do some additional validation as well as override few more super class methods, more on it next. The claims themselves have already been parsed and will be made available to a resulting SecurityContext which ClaimsAuthorizingFilter will rely upon.