Monday, December 7, 2015

Javascript Object Signing and Encryption (JOSE) support in Apache CXF - part III

This is the third article in a series of posts on support for Javascript Object Signing and Encryption (JOSE) in Apache CXF. The previous article described how to encrypt content using the JSON Web Encryption specification. This post covers support for JSON Web Tokens (JWT) in Apache CXF. The JWT spec is not strictly part of the JOSE specification set, but it makes sense to cover it in this series of posts, as it is a natural extension of the JOSE specs to convey claims.

1) Test Cases:

As before, let's start by looking at some practical unit tests on github:
  • cxf-jaxrs-jose: This project contains a number of tests that show how to use the JSON Security functionality in Apache CXF to sign and encrypt JSON payloads to/from a JAX-RS service.
These tests mostly follow the same basic format. The web service configuration defines a number of endpoints that map to the same web service implementation (a simple DoubleItService, which allows a user to POST a number and receive the doubled amount, where both the request and response are encoded via JSON). The client test code first constructs a JWT Token and then includes it in the message payload by adding a specific provider. The JWT Token is then processed by a provider on the receiving side. We will cover the individual tests in more detail below.

2) Constructing JWT Tokens

Apache CXF provides an easy to use API to create JWT Tokens. Let's look at how this is done in one of the tests - the JWTAuthenticationTest. The tests construct a JwtClaims Object, which has convenient set/get methods for the standard JWT Claims. For example:


The JwtClaims Object can be passed through to the constructor of a JwtToken Object. To serialize the JwtToken in the request simply add the JwtAuthenticationClientFilter to the client provider list, and specify the JwtToken Object as a message property under "jwt.token". Alternatively, the JwtClaims Object can be passed without having to construct a JwtToken via the property "jwt.claims". On the receiving side, a JWTToken can be processed by the JwtAuthenticationFilter provider.

3) Authentication and Authorization using signed JWT Tokens

By default, the JwtAuthenticationClientFilter signs the JWT Token. The signature configuration is the exact same as that used for JWS as covered in the first article. On the receiving side, the JwtAuthenticationFilter will set up a security context for the authenticated client, assuming that a public key signature algorithm was used to sign the token. The "sub" (Subject) claim from the token gets mapped to the principal of the security context.

As the JWT Token can convey arbitrary claims, it can be used for RBAC authorization. This is demonstrated in the JWTAuthorizationTest. A role called "boss" is inserted into the token. On the receiving side, the JwtAuthenticationFilter is configured to use the "role" claim in the token to extract roles for the authenticated principal and to populate the security context with them. As part of the test-case, CXF's SimpleAuthorizingInterceptor is used to require that a client must have a role of "boss" to invoke on the web service method in question.

4) Encrypting JWT Tokens

It is very easy to encrypt JWT Tokens (as well as both sign and encrypt) them in CXF. The JwtAuthenticationClientFilter needs to be configured to also encrypt the token, and the same configuration is used as for JWE in the previous article. Similarly, on the receiving side the JwtAuthenticationFilter must have the property "jweRequired" set to "true" to decrypt incoming encrypted tokens. See the JWTEncryptedTest test for some examples. 

5) Token validation

On receiving a JAX-RS request containing a JWT token, the
JwtAuthenticationFilter will first parse the token, and then verify the signature (if present) and decrypt the token (if encrypted). It then performs some quality of service validation on the token claims, which I'll detail here. This validation can be easily modified by overriding the protected "validateToken" method in JwtAuthenticationFilter.
  • The "exp" (Expiration Time) claim is validated if present. If the expiry value is before the current date/time, then the token is rejected. 
  • The "nbf" (Not Before) claim is validated if present. If the not before value is after the current date/time, then the token is rejected. 
  • The "iat" (Issued At) claim is validated if present. To validate the "iat" claim, a "ttl" property must be set on the JwtAuthenticationFilter.
  • Either an "iat" or "exp" claim must be present in the token, as otherwise we have no way of enforcing an expiry on a token.
  • A clockskew value can also be configured on the JwtAuthenticationFilter via the "clockOffset" property.
  • The "aud" (Audience) claim is validated. This claim must contain at least one audience value which matches the endpoint address of the recipient.

No comments:

Post a Comment