This walk-through will show how simple it is to integrate Approov in a stateless API server using Java and the Spring framework.We will see the requirements, dependencies and a step by step walk-through of the code necessary to implement Approov in a Java Spring stateless API.
Before we tackle the integration of Approov we first need to know how the Approov validation is processed on the server side, and how to set up the environment to follow this walk-through.
Note that this article assumes a basic understanding of the Approov mechanics. If you need an overview of that, please read first the Approov in Detail page.
Before we dive into the code we need to understand the Approov validation process on the back-end side.
API calls protected by Approov will typically include a header holding an Approov JWT token. This token must be checked to ensure it has not expired and that it is properly signed with the secret shared between the back-end and the Approov cloud service.
We will use the `io.jsonwebtoken.*` package to help us in the validation of the Approov JWT token.
Just to be sure that we are on the same page, a JWT token has 3 parts which are separated by dots and represented as a string in the format of header.payload.signature. Read more about JWT tokens here.
When an Approov token contains the key pay, its value is a base64 encoded sha256 hash of some unique identifier in the request, that we may want to bind with the Approov token, in order to enhance the security on that request, like an Authorization token.
Dummy example for the JWT token middle part, the payload:
"exp": 123456789, # required - the timestamp for when the token expires.
"pay":"f3U2fniBJVE04Tdecj0d6orV9qT9t52TjfHxdUqDBgY=" # optional - a sha256 hash of the token binding, encoded with base64.
The token binding in an Approov token is the one in the pay key:
ALERT: Please bear in mind that the token binding is not meant to pass application data to the API server.
In order to correctly check for the expiration times of the Approov tokens it is important that the system clock for the Java server is synchronized automatically over the network with an authoritative time source. In Linux this is usual done with an NTP server.
We will use Java 11.0.3 with the Spring Boot 2.1.3.RELEASE, and Gradle 5.2.1 to compile, build and run this Approov integration.
Postman is the tool we recommend to be used when simulating the queries against the API, but feel free to use another tool if you prefer.
Docker is only required for developers who want to use the Java docker stack provided by the stack bash script, which is a wrapper around docker commands.
Import this Postman collection that contains all the API endpoints for the Approov Shapes API Server and we strongly recommend you to follow this walk-through after finishing the Approov integration that we are about to start.
The Approov tokens used in the headers of this Postman collection were generated by the Approov CLI Tool and they cover all necessary scenarios, but feel free to use the script to generate some more valid and invalid tokens, with different expiry times and token bindings. Some examples of genrating Approov tokens can be found here, and for examples of generating Approov token binding you can go here.
We recommend the use of the included Docker stack to play with this Approov Integration. For details on how to use it you need to follow the setup instructions in the Approov Shapes API Server walk-through, but feel free to use your local environment to play with this Approov integration.
For example, to get a shell inisde the docker container execute from your host terminal:
Now that you have a shell inside the docker container you can start by checking what versions of java and gradle is running:
In the docker container shell run:
and the output should look like:
openjdk 11.0.3 2019-04-16
OpenJDK Runtime Environment (build 11.0.3+1-Debian-1bpo91)
OpenJDK 64-Bit Server VM (build 11.0.3+1-Debian-1bpo91, mixed mode, sharing)
For the gradle version just run:
and the output should be similar to this:
Build time: 2019-02-08 19:00:10 UTC
Kotlin DSL: 1.1.3
Ant: Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM: 11.0.3 (Oracle Corporation 11.0.3+1-Debian-1bpo91)
OS: Linux 4.15.0-47-generic amd64
Probably the only dependencies from the build.gradle that you do not have in your own project are these ones:
We will learn how to integrate Approov in a skeleton generated with Spring Boot, where we added some endpoints:
To integrate Approov into your own project you may want to use the package com.criticalblue.approov.jwt.authentication, which contains all the code that is project agnostic. To use this package you need to configure it from the class extending the WebSecurityConfigurerAdapter. In this demo it is named WebSecurityConfig.
The WebSecurityConfig is where we will set up the security configuration for the Spring framework, and this is done by @override some of the methods for the abstract class it extends from, the WebSecurityConfigurerAdapter.
When implementing Approov it is required to always check if the signature and expiration time of the Approov token is valid, and optionally to check if the Aproov token binding matches the one in the header.
Now we need to configure what endpoints will perform the required and optional checks, and for this we need to add ApproovSecurityContextRepository(approovConfig, checkTokenBinding) and the ApproovAuthenticationEntryPoint() to the Spring framework security context, plus the endpoint name and http verbs, where the authentication should be triggered.
The approovConfig contains several pieces of information necessary to check the Approov token, such as the Approov secret used by the Approov cloud service to sign the JWT token. For more details on what it contains you can inspect the code here .
Each time we add an endpoint to be protected by an Approov token we need to indicate if the Approov token binding is to be checked or not, and this is done with the boolean flag checkTokenBinding.
In order to have endpoints that perform only the required checks in the Approov token, while at the same time having other endpoints where both the required and optional checks must take place, we need to configure the Spring framework security context with the static subclasses of the main WebSecurityConfig class, and these subclasses also need to implement the abstract WebSecurityConfigurerAdapter class. These subclasses will be annotated with a configuration order @Order(n), thus their configuration order is important. So where we define @Order(1) we are telling to the Spring framework security context to perform first the required checks on the Approov token, afterwards with @Order(2) we perform the optional check for the Approov Token binding, and then with @Order(3) we proceed as usual, which in this demo is to allow any request for the endpoints / and /v2/hello to be served without authentication of any kind.
If you don't have already an `.env` file, then you need to create one in the root of your project by using this .env.example as your starting point.
The .env file must contain these five variables:
The Approov base64 seccret is a dummy secret used for testing the implementation in localhost with the provided Postman Collection.
Add the package com.criticalblue.approov.jwt.authentication to your current project and then configure it from the class in your project that extends the WebSecurityConfigurerAdapter.
Let's consider as a starting point an initial WebSecurityConfig without requiring authentication for any of its endpoints:
When implemeting Approov in an existing API server you will need to support for a while the old mobile apps without Approov protection, thus a possible option is to use `v2` enpoints to implement Approov, and then remove the current ones when all your users have upgraded their mobile app.
The `/v2/shapes` endpoint it will be protected only by the required checks for an Approov token, while the `/v2/forms` endpoint will have the optional check for the token binding in the Approov token.
As already mentioned we will need to add to the WebSecurityConfig a subclass for the endpoints we want to secure with only the required checks for an Approov token, another for the endpoints secured with the required and optional checks for an Approov token, and finally a subclass for endpoints that do not require authentication at all.
So let's prepare the WebSecurityConfig with only a subclass that maintains access to all endpoints without any authentication.
Let’s add the subclass ApiWebSecurityConfig:
In order to integrate Approov we will need to use an `Approov-Token` header, thus we need to allow it in the CORS configuration.
If our Approov integration also uses the Approov token binding check, then we need to also allow the header from where we want to retrieve the value we bind to the Approov token in the mobile app, that in this demo is the Authorization header.
So we add to the CORS configuration this 2 new lines:
That will give us this new CORS configuration:
To protect the `/v2/shapes` endpoint we will add the subclass ApproovWebSecurityConfig:
and change the configuration order for subclass ApiWebSecurityConfig from `1` to `2`:
finally you can see we have removed the line of code allowing the endpoint `/v2/shapes` to be reached without any authentication.
This endpoint also requires that we perform the optional check for the Approov Token binding, thus to protect the `/v2/forms` endpoint another subclass is necessary.
Let's add the subclass ApproovTokenBindingWebSecurityConfig:
If you are paying attention you will have noticed that the configuration order is the same as the subclass ApiWebSecurityConfig in the previous step, thus we need to change it again, this time from `2` to `3`:
and finally you can see that we removed the line of code allowing the endpoint `/v2/orms` to be reached without any authentication.
After we have implemented the Approov protection for the `/v2/shapes` and `/v2/forms` endpoints the class WebSecurityConfig should look like:
If we compare the initial implementation with the final result for the class WebSecurityConfig we will see this difference:
As we can see the Approov integration into an existing server is simple, easy and is done with just a few lines of code.
If you have not done it already, now is the time to follow the Approov Shapes API Server walk-through to gain an appreciation for how it all works together.
Let's see how to query the Java Spring stateless API from Postman with the collection we told you to install in the requirements section.
For your convenience the Postman collection includes a token that only expires in the very distant future for this call "Approov Token with valid signature and expire time". For the call "Expired Approov Token with valid signature" an expired token is also included.
Postman view with an Approov token correctly signed and not expired:
Postman view with token correctly signed but this time it is expired:
Shell view with the logs for the above requests:
We used this helper script to generate an Approov Token that was valid for 1 minute.
In Postman we performed 2 requests with the same token and the first one was successful, but the second request, performed 2 minutes later, failed with a 401 response because the token had expired as we can see by the log messages in the shell view.
Now that you have had a taste of Approov in action, and if you have not done so already, it is time to follow the Approov Shapes API Server walk-through to play and get an understanding for how all this works in practice.
The Approov Shapes API Server contains endpoints with and without the Approov protection. The protected endpoints differ in the sense that one uses the optional token binding in the Approov token.
We will demonstrate how to call each API endpoint with screen-shots from Postman and from the shell. Postman is used here as an easy way to demonstrate how you can play with the Approov integration in the API server, but to see a real demo of how Approov would work in production you can request a demo here.
In order to protect the communication between your mobile app and the API server is important to only communicate over a secure communication channel, also known as https.
Please bear in mind that https on its own is not enough, certificate pinning must be also used to pin the connection between the mobile app and the API server in order to prevent Man in the Middle Attacks.
We do not use HTTPS and certificate pinning in this Approov integration example because we want to be able to run the Approov Shapes API Server in localhost.
However in production is mandatory to configure and implement certificate pinning, that is made easy by using the dynamic pinning feature built-in the Appoov CLI tool, that allows to update the pins without the need to release a new version of your mobile app.